diff options
966 files changed, 29237 insertions, 12835 deletions
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz Binary files differindex 22685d1ce..bf637e93d 100644 --- a/dictionaries/en_GB_wordlist.combined.gz +++ b/dictionaries/en_GB_wordlist.combined.gz diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz Binary files differindex 92a3761df..9ea04b146 100644 --- a/dictionaries/en_US_wordlist.combined.gz +++ b/dictionaries/en_US_wordlist.combined.gz diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz Binary files differindex f8b71c821..87a863330 100644 --- a/dictionaries/en_wordlist.combined.gz +++ b/dictionaries/en_wordlist.combined.gz diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz Binary files differindex 56617db99..181a958c0 100644 --- a/dictionaries/es_wordlist.combined.gz +++ b/dictionaries/es_wordlist.combined.gz diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz Binary files differindex 697f36799..1b9fd73f9 100644 --- a/dictionaries/fr_wordlist.combined.gz +++ b/dictionaries/fr_wordlist.combined.gz diff --git a/java/proguard.flags b/java/proguard.flags index f7b7f2898..35b3ac3d3 100644 --- a/java/proguard.flags +++ b/java/proguard.flags @@ -17,7 +17,8 @@ # Keep classes that are used as a parameter type of methods that are also marked as keep # to preserve changing those methods' signature. --keep class com.android.inputmethod.latin.utils.LanguageModelParam -keep class com.android.inputmethod.latin.AssetFileAddress --keep class com.android.inputmethod.latin.makedict.ProbabilityInfo -keep class com.android.inputmethod.latin.Dictionary +-keep class com.android.inputmethod.latin.PrevWordsInfo +-keep class com.android.inputmethod.latin.makedict.ProbabilityInfo +-keep class com.android.inputmethod.latin.utils.LanguageModelParam diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_ics_dark.9.png Binary files differindex 9aa8db60e..9aa8db60e 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_ics_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_klp_dark.9.png Binary files differindex fa2cb8542..fa2cb8542 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_klp_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..82e850c6b --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..e2b9e3d9c --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal.9.png Binary files differdeleted file mode 100644 index bc130cab6..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off.9.png Binary files differdeleted file mode 100644 index 43099899c..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on.9.png Binary files differdeleted file mode 100644 index 2d1acf22f..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed.9.png Binary files differdeleted file mode 100644 index af5ea6bd2..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off.9.png Binary files differdeleted file mode 100644 index 3e25a9817..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on.9.png Binary files differdeleted file mode 100644 index fc7ba2aeb..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_normal.9.png Binary files differdeleted file mode 100644 index 005c4e498..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_normal.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_popup_selected.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_popup_selected.9.png Binary files differdeleted file mode 100644 index 9a07acd91..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_popup_selected.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed.9.png Binary files differdeleted file mode 100644 index be420a7af..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_holo_dark.9.png Binary files differindex fa2cb8542..fa2cb8542 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_holo_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_holo_light.9.png Binary files differindex 6da273b09..6da273b09 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_holo_light.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_holo_dark.9.png Binary files differindex b1af23b6c..b1af23b6c 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_holo_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..2e6489cf5 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_ics_dark.9.png Binary files differindex 9f4587b4a..9f4587b4a 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_ics_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_klp_dark.9.png Binary files differindex 814e40235..814e40235 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_klp_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..0b1482281 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_ics_dark.9.png Binary files differindex 7ec33dd20..7ec33dd20 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_ics_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_ics_light.9.png Binary files differindex 5612c51a1..5612c51a1 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_ics_light.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_klp_dark.9.png Binary files differindex 90abe3940..90abe3940 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_klp_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_klp_light.9.png Binary files differindex 6768241a7..6768241a7 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_klp_light.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_ics_dark.9.png Binary files differindex 655bc01b1..655bc01b1 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_ics_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_klp_dark.9.png Binary files differindex 48eeb3f54..48eeb3f54 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_klp_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..4bf38fc35 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_ics.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_ics_dark.9.png Binary files differindex 138e915d9..138e915d9 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_ics.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_ics_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_klp.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_klp_dark.9.png Binary files differindex 71e0683cd..71e0683cd 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_klp.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_klp_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..ea12c7776 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_suggestion_pressed.9.png b/java/res/drawable-hdpi/btn_suggestion_pressed.9.png Binary files differdeleted file mode 100644 index 7acceaee7..000000000 --- a/java/res/drawable-hdpi/btn_suggestion_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/emoji_category_tab_selected_ics.9.png b/java/res/drawable-hdpi/emoji_category_tab_selected_ics.9.png Binary files differnew file mode 100644 index 000000000..9138cef9c --- /dev/null +++ b/java/res/drawable-hdpi/emoji_category_tab_selected_ics.9.png diff --git a/java/res/drawable-hdpi/emoji_category_tab_selected_klp.9.png b/java/res/drawable-hdpi/emoji_category_tab_selected_klp.9.png Binary files differnew file mode 100644 index 000000000..345d05e9d --- /dev/null +++ b/java/res/drawable-hdpi/emoji_category_tab_selected_klp.9.png diff --git a/java/res/drawable-hdpi/tab_unselected.9.png b/java/res/drawable-hdpi/emoji_category_tab_unselected_holo_dark.9.png Binary files differindex bbcfb2c64..bbcfb2c64 100644 --- a/java/res/drawable-hdpi/tab_unselected.9.png +++ b/java/res/drawable-hdpi/emoji_category_tab_unselected_holo_dark.9.png diff --git a/java/res/drawable-hdpi/ic_emoji_emoticons_activated_holo_dark.png b/java/res/drawable-hdpi/ic_emoji_emoticons_activated_holo_dark.png Binary files differnew file mode 100644 index 000000000..1c937c9ef --- /dev/null +++ b/java/res/drawable-hdpi/ic_emoji_emoticons_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_emoticons_normal_holo_dark.png b/java/res/drawable-hdpi/ic_emoji_emoticons_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..3508d242a --- /dev/null +++ b/java/res/drawable-hdpi/ic_emoji_emoticons_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-hdpi/ic_emoji_nature_activated_holo_dark.png Binary files differindex 5525df2f7..5525df2f7 100644 --- a/java/res/drawable-hdpi/ic_emoji_nature_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_nature_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-hdpi/ic_emoji_nature_normal_holo_dark.png Binary files differindex 34e16b9de..34e16b9de 100644 --- a/java/res/drawable-hdpi/ic_emoji_nature_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_nature_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-hdpi/ic_emoji_objects_activated_holo_dark.png Binary files differindex c3c7ec1b8..c3c7ec1b8 100644 --- a/java/res/drawable-hdpi/ic_emoji_objects_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_objects_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-hdpi/ic_emoji_objects_normal_holo_dark.png Binary files differindex f012d7707..f012d7707 100644 --- a/java/res/drawable-hdpi/ic_emoji_objects_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_objects_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_light_activated.png b/java/res/drawable-hdpi/ic_emoji_people_activated_holo_dark.png Binary files differindex cfacbc2e7..cfacbc2e7 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_people_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_light_normal.png b/java/res/drawable-hdpi/ic_emoji_people_normal_holo_dark.png Binary files differindex c54dbc1f5..c54dbc1f5 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_people_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_light_activated.png b/java/res/drawable-hdpi/ic_emoji_places_activated_holo_dark.png Binary files differindex 959dfdfd5..959dfdfd5 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_places_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_light_normal.png b/java/res/drawable-hdpi/ic_emoji_places_normal_holo_dark.png Binary files differindex fc0d9711d..fc0d9711d 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_places_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-hdpi/ic_emoji_recents_activated_holo_dark.png Binary files differindex de570a185..de570a185 100644 --- a/java/res/drawable-hdpi/ic_emoji_recent_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-hdpi/ic_emoji_recents_normal_holo_dark.png Binary files differindex b2562088d..b2562088d 100644 --- a/java/res/drawable-hdpi/ic_emoji_recent_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-hdpi/ic_emoji_symbols_activated_holo_dark.png Binary files differindex af1fd27ec..af1fd27ec 100644 --- a/java/res/drawable-hdpi/ic_emoji_symbols_light_activated.png +++ b/java/res/drawable-hdpi/ic_emoji_symbols_activated_holo_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-hdpi/ic_emoji_symbols_normal_holo_dark.png Binary files differindex 02b84d51b..02b84d51b 100644 --- a/java/res/drawable-hdpi/ic_emoji_symbols_light_normal.png +++ b/java/res/drawable-hdpi/ic_emoji_symbols_normal_holo_dark.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..9d6514bdd --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..8ad54f69d --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..f9dd3b8b1 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_popup_panel_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png Binary files differnew file mode 100644 index 000000000..5f5eb3fe7 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f81130d1a --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png Binary files differnew file mode 100644 index 000000000..516a7f1c7 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png Binary files differnew file mode 100644 index 000000000..edf9a20de --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png Binary files differdeleted file mode 100644 index 3c5469403..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8a88a90d7 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png Binary files differnew file mode 100644 index 000000000..ee804abce --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_return_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_return_lxx_dark.png Binary files differnew file mode 100644 index 000000000..6ab33a76d --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_return_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png Binary files differnew file mode 100644 index 000000000..de117d35b --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png Binary files differnew file mode 100644 index 000000000..52feef04c --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f2613023f --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_lxx_dark.png Binary files differnew file mode 100644 index 000000000..ef6b19efe --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_lxx_dark.png Binary files differnew file mode 100644 index 000000000..cb03e55a8 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_shift_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png Binary files differnew file mode 100644 index 000000000..3999d1e2d --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png Binary files differdeleted file mode 100644 index 34a1ebde2..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..f344deeec --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png Binary files differnew file mode 100644 index 000000000..6b68c8af5 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png Binary files differnew file mode 100644 index 000000000..e67697b3f --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png diff --git a/java/res/drawable-hdpi/tab_selected.9.png b/java/res/drawable-hdpi/tab_selected.9.png Binary files differdeleted file mode 100644 index 84e63df17..000000000 --- a/java/res/drawable-hdpi/tab_selected.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_ics_dark.9.png Binary files differindex e810c7789..e810c7789 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_ics_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_klp_dark.9.png Binary files differindex 8e9a34957..8e9a34957 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_klp_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..281b5d3bc --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..eb1564353 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal.9.png Binary files differdeleted file mode 100644 index 49329f094..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off.9.png Binary files differdeleted file mode 100644 index 46e9db092..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on.9.png Binary files differdeleted file mode 100644 index ee60e4864..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed.9.png Binary files differdeleted file mode 100644 index c6876f76e..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off.9.png Binary files differdeleted file mode 100644 index 1f8f318d1..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on.9.png Binary files differdeleted file mode 100644 index 2bb7b64f4..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_normal.9.png Binary files differdeleted file mode 100644 index f5ce40cf6..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_normal.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_popup_selected.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_popup_selected.9.png Binary files differdeleted file mode 100644 index ca73b9249..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_popup_selected.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed.9.png Binary files differdeleted file mode 100644 index 73f2006d4..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_holo_dark.9.png Binary files differindex 8e9a34957..8e9a34957 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_holo_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_holo_light.9.png Binary files differindex 2915588bf..2915588bf 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_holo_light.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_holo_dark.9.png Binary files differindex 58a316fba..58a316fba 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_holo_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..9f244f2e9 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_ics_dark.9.png Binary files differindex f3fc64114..f3fc64114 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_ics_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_klp_dark.9.png Binary files differindex b7b2dca43..b7b2dca43 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_klp_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..c5b3fbbd7 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_ics_dark.9.png Binary files differindex 8f340d355..8f340d355 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_ics_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_ics_light.9.png Binary files differindex c39dd4a94..c39dd4a94 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_ics_light.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_klp_dark.9.png Binary files differindex 4a92b80dd..4a92b80dd 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_klp_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_klp_light.9.png Binary files differindex 049385984..049385984 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_klp_light.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_ics_dark.9.png Binary files differindex 53ea5f894..53ea5f894 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_ics_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_klp_dark.9.png Binary files differindex 72125a065..72125a065 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_klp_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..66824cf8e --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_ics.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_ics_dark.9.png Binary files differindex 69c84e7ec..69c84e7ec 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_ics.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_ics_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_klp.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_klp_dark.9.png Binary files differindex 82413d4cc..82413d4cc 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_klp.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_klp_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..527dfd014 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_suggestion_pressed.9.png b/java/res/drawable-mdpi/btn_suggestion_pressed.9.png Binary files differdeleted file mode 100644 index 02b4e9a53..000000000 --- a/java/res/drawable-mdpi/btn_suggestion_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/emoji_category_tab_selected_ics.9.png b/java/res/drawable-mdpi/emoji_category_tab_selected_ics.9.png Binary files differnew file mode 100644 index 000000000..1731b46ad --- /dev/null +++ b/java/res/drawable-mdpi/emoji_category_tab_selected_ics.9.png diff --git a/java/res/drawable-mdpi/emoji_category_tab_selected_klp.9.png b/java/res/drawable-mdpi/emoji_category_tab_selected_klp.9.png Binary files differnew file mode 100644 index 000000000..6354b997a --- /dev/null +++ b/java/res/drawable-mdpi/emoji_category_tab_selected_klp.9.png diff --git a/java/res/drawable-mdpi/tab_unselected.9.png b/java/res/drawable-mdpi/emoji_category_tab_unselected_holo_dark.9.png Binary files differindex bb45ab960..bb45ab960 100644 --- a/java/res/drawable-mdpi/tab_unselected.9.png +++ b/java/res/drawable-mdpi/emoji_category_tab_unselected_holo_dark.9.png diff --git a/java/res/drawable-mdpi/ic_emoji_emoticons_activated_holo_dark.png b/java/res/drawable-mdpi/ic_emoji_emoticons_activated_holo_dark.png Binary files differnew file mode 100644 index 000000000..c7394e1b0 --- /dev/null +++ b/java/res/drawable-mdpi/ic_emoji_emoticons_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_emoticons_normal_holo_dark.png b/java/res/drawable-mdpi/ic_emoji_emoticons_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..eb4dab4c7 --- /dev/null +++ b/java/res/drawable-mdpi/ic_emoji_emoticons_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-mdpi/ic_emoji_nature_activated_holo_dark.png Binary files differindex d4c8d8da8..d4c8d8da8 100644 --- a/java/res/drawable-mdpi/ic_emoji_nature_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_nature_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-mdpi/ic_emoji_nature_normal_holo_dark.png Binary files differindex 1555aa7a9..1555aa7a9 100644 --- a/java/res/drawable-mdpi/ic_emoji_nature_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_nature_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-mdpi/ic_emoji_objects_activated_holo_dark.png Binary files differindex 081dc66c2..081dc66c2 100644 --- a/java/res/drawable-mdpi/ic_emoji_objects_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_objects_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-mdpi/ic_emoji_objects_normal_holo_dark.png Binary files differindex 58e6f6e75..58e6f6e75 100644 --- a/java/res/drawable-mdpi/ic_emoji_objects_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_objects_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_light_activated.png b/java/res/drawable-mdpi/ic_emoji_people_activated_holo_dark.png Binary files differindex 067ad5496..067ad5496 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_people_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_light_normal.png b/java/res/drawable-mdpi/ic_emoji_people_normal_holo_dark.png Binary files differindex d835d4ec7..d835d4ec7 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_people_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_light_activated.png b/java/res/drawable-mdpi/ic_emoji_places_activated_holo_dark.png Binary files differindex 1aecec598..1aecec598 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_places_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_light_normal.png b/java/res/drawable-mdpi/ic_emoji_places_normal_holo_dark.png Binary files differindex c70e484e1..c70e484e1 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_places_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-mdpi/ic_emoji_recents_activated_holo_dark.png Binary files differindex 8009e932f..8009e932f 100644 --- a/java/res/drawable-mdpi/ic_emoji_recent_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-mdpi/ic_emoji_recents_normal_holo_dark.png Binary files differindex c2e598dfb..c2e598dfb 100644 --- a/java/res/drawable-mdpi/ic_emoji_recent_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-mdpi/ic_emoji_symbols_activated_holo_dark.png Binary files differindex caea87191..caea87191 100644 --- a/java/res/drawable-mdpi/ic_emoji_symbols_light_activated.png +++ b/java/res/drawable-mdpi/ic_emoji_symbols_activated_holo_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-mdpi/ic_emoji_symbols_normal_holo_dark.png Binary files differindex 0edada626..0edada626 100644 --- a/java/res/drawable-mdpi/ic_emoji_symbols_light_normal.png +++ b/java/res/drawable-mdpi/ic_emoji_symbols_normal_holo_dark.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..d3200921a --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..60a36333e --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..896505518 --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_popup_panel_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png Binary files differnew file mode 100644 index 000000000..2d3ac9749 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8a63c116a --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png Binary files differnew file mode 100644 index 000000000..89051722a --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png Binary files differnew file mode 100644 index 000000000..a90bf757c --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png Binary files differdeleted file mode 100644 index 5e58866a7..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png Binary files differnew file mode 100644 index 000000000..414cb0d58 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png Binary files differnew file mode 100644 index 000000000..40655ca3b --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_return_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_return_lxx_dark.png Binary files differnew file mode 100644 index 000000000..d060c89f6 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_return_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png Binary files differnew file mode 100644 index 000000000..722d40258 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png Binary files differnew file mode 100644 index 000000000..29dd9defc --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png Binary files differnew file mode 100644 index 000000000..dea7addfe --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_lxx_dark.png Binary files differnew file mode 100644 index 000000000..c70084339 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_shift_locked_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_lxx_dark.png Binary files differnew file mode 100644 index 000000000..c10066e81 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_shift_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8276d992c --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png Binary files differdeleted file mode 100644 index abd8b742e..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..ed32cf652 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png Binary files differnew file mode 100644 index 000000000..5661f6b33 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png Binary files differnew file mode 100644 index 000000000..5182f1e8f --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png diff --git a/java/res/drawable-mdpi/tab_selected.9.png b/java/res/drawable-mdpi/tab_selected.9.png Binary files differdeleted file mode 100644 index 4b00f350a..000000000 --- a/java/res/drawable-mdpi/tab_selected.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_ics_dark.9.png Binary files differindex d990c0258..d990c0258 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_ics_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_klp_dark.9.png Binary files differindex a2f6ac0e2..a2f6ac0e2 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_klp_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..0f6a3e1e0 --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..44c84f1f8 --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal.9.png Binary files differdeleted file mode 100644 index d0090a305..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off.9.png Binary files differdeleted file mode 100644 index 2baf7d90c..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on.9.png Binary files differdeleted file mode 100644 index 6812f9e8f..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed.9.png Binary files differdeleted file mode 100644 index a932249a8..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off.9.png Binary files differdeleted file mode 100644 index 16416f000..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on.9.png Binary files differdeleted file mode 100644 index 3ca93fdb3..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal.9.png Binary files differdeleted file mode 100644 index aa4f44fdd..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_popup_selected.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_popup_selected.9.png Binary files differdeleted file mode 100644 index 4539255c2..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_popup_selected.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed.9.png Binary files differdeleted file mode 100644 index 568392444..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_holo_dark.9.png Binary files differindex a2f6ac0e2..a2f6ac0e2 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_holo_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_holo_light.9.png Binary files differindex 0ef4a4b5f..0ef4a4b5f 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_holo_light.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_holo_dark.9.png Binary files differindex 2f00fc623..2f00fc623 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_holo_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..98c085b15 --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_ics_dark.9.png Binary files differindex ab8fb2e86..ab8fb2e86 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_ics_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_klp_dark.9.png Binary files differindex 20251a000..20251a000 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_klp_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..f0c132869 --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_ics_dark.9.png Binary files differindex 3871689ef..3871689ef 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_ics_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_ics_light.9.png Binary files differindex c23a4b225..c23a4b225 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_ics_light.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_klp_dark.9.png Binary files differindex 84d173967..84d173967 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_klp_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_klp_light.9.png Binary files differindex f770962c3..f770962c3 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_klp_light.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_ics_dark.9.png Binary files differindex 912506368..912506368 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_ics_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_klp_dark.9.png Binary files differindex ee4490eac..ee4490eac 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_klp_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..a2b17ba50 --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_ics.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_ics_dark.9.png Binary files differindex 35ce67fdc..35ce67fdc 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_ics.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_ics_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_klp.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_klp_dark.9.png Binary files differindex e8124776c..e8124776c 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_klp.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_klp_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..99ff0affb --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_suggestion_pressed.9.png b/java/res/drawable-xhdpi/btn_suggestion_pressed.9.png Binary files differdeleted file mode 100644 index 41e126a73..000000000 --- a/java/res/drawable-xhdpi/btn_suggestion_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/emoji_category_tab_selected_ics.9.png b/java/res/drawable-xhdpi/emoji_category_tab_selected_ics.9.png Binary files differnew file mode 100644 index 000000000..11bc966b0 --- /dev/null +++ b/java/res/drawable-xhdpi/emoji_category_tab_selected_ics.9.png diff --git a/java/res/drawable-xhdpi/emoji_category_tab_selected_klp.9.png b/java/res/drawable-xhdpi/emoji_category_tab_selected_klp.9.png Binary files differnew file mode 100644 index 000000000..5e8549bdd --- /dev/null +++ b/java/res/drawable-xhdpi/emoji_category_tab_selected_klp.9.png diff --git a/java/res/drawable-xhdpi/tab_unselected.9.png b/java/res/drawable-xhdpi/emoji_category_tab_unselected_holo_dark.9.png Binary files differindex 8cede8d5e..8cede8d5e 100644 --- a/java/res/drawable-xhdpi/tab_unselected.9.png +++ b/java/res/drawable-xhdpi/emoji_category_tab_unselected_holo_dark.9.png diff --git a/java/res/drawable-xhdpi/ic_emoji_emoticons_activated_holo_dark.png b/java/res/drawable-xhdpi/ic_emoji_emoticons_activated_holo_dark.png Binary files differnew file mode 100644 index 000000000..997c9b700 --- /dev/null +++ b/java/res/drawable-xhdpi/ic_emoji_emoticons_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_emoticons_normal_holo_dark.png b/java/res/drawable-xhdpi/ic_emoji_emoticons_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..23a519c5d --- /dev/null +++ b/java/res/drawable-xhdpi/ic_emoji_emoticons_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_nature_activated_holo_dark.png Binary files differindex 3e6744343..3e6744343 100644 --- a/java/res/drawable-xhdpi/ic_emoji_nature_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_nature_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_nature_normal_holo_dark.png Binary files differindex 5344a9ee9..5344a9ee9 100644 --- a/java/res/drawable-xhdpi/ic_emoji_nature_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_nature_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_objects_activated_holo_dark.png Binary files differindex 75695d43d..75695d43d 100644 --- a/java/res/drawable-xhdpi/ic_emoji_objects_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_objects_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_objects_normal_holo_dark.png Binary files differindex 2adb186e6..2adb186e6 100644 --- a/java/res/drawable-xhdpi/ic_emoji_objects_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_objects_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_people_activated_holo_dark.png Binary files differindex e6baa2e59..e6baa2e59 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_people_normal_holo_dark.png Binary files differindex c26aa4efb..c26aa4efb 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_places_activated_holo_dark.png Binary files differindex eaa3b86cf..eaa3b86cf 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_places_normal_holo_dark.png Binary files differindex d6e1eaa35..d6e1eaa35 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_recents_activated_holo_dark.png Binary files differindex 06003b82d..06003b82d 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recent_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_recents_normal_holo_dark.png Binary files differindex da2effedb..da2effedb 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recent_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_symbols_activated_holo_dark.png Binary files differindex 438fde2b4..438fde2b4 100644 --- a/java/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png +++ b/java/res/drawable-xhdpi/ic_emoji_symbols_activated_holo_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_symbols_normal_holo_dark.png Binary files differindex 757863233..757863233 100644 --- a/java/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png +++ b/java/res/drawable-xhdpi/ic_emoji_symbols_normal_holo_dark.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..d40afebd8 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..d1a2f7599 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_popup_panel_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_popup_panel_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..36df715b6 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_popup_panel_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png Binary files differnew file mode 100644 index 000000000..7c9f34f12 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f25e3dfd9 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png Binary files differnew file mode 100644 index 000000000..6cd43cf8a --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f964346c1 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png Binary files differdeleted file mode 100644 index 566ba1fcd..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8b5b0b4de --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png Binary files differnew file mode 100644 index 000000000..9bc4fc81e --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_return_lxx_dark.png Binary files differnew file mode 100644 index 000000000..5d6e6cbf9 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_return_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png Binary files differnew file mode 100644 index 000000000..28a4bd329 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png Binary files differnew file mode 100644 index 000000000..4360a4ea1 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8c83d9fb6 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_lxx_dark.png Binary files differnew file mode 100644 index 000000000..e329cbc8f --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_lxx_dark.png Binary files differnew file mode 100644 index 000000000..19acffaeb --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_shift_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png Binary files differnew file mode 100644 index 000000000..78923fadb --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png Binary files differdeleted file mode 100644 index ba4e9ec49..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..71234e570 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png Binary files differnew file mode 100644 index 000000000..4e9631e7b --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png Binary files differnew file mode 100644 index 000000000..66c0e3c56 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png diff --git a/java/res/drawable-xhdpi/tab_selected.9.png b/java/res/drawable-xhdpi/tab_selected.9.png Binary files differdeleted file mode 100644 index 95e5f4341..000000000 --- a/java/res/drawable-xhdpi/tab_selected.9.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_ics_dark.9.png Binary files differindex 680421eaf..680421eaf 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_ics_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_klp_dark.9.png Binary files differindex 17f0a7a58..17f0a7a58 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_klp_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..dc67c6f19 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..2e12dd1d4 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_holo_dark.9.png Binary files differindex 17f0a7a58..17f0a7a58 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_holo_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_holo_light.9.png Binary files differindex 4ddfdcb6c..4ddfdcb6c 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_holo_light.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_holo_dark.9.png Binary files differindex b0e815eb2..b0e815eb2 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_holo_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..4ec5864c1 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_ics_dark.9.png Binary files differindex 40f5011c0..40f5011c0 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_ics_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_klp_dark.9.png Binary files differindex 97f96258e..97f96258e 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_klp_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..6b5c0c0a4 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_ics_dark.9.png Binary files differindex 6ff6319d3..6ff6319d3 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_ics_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_ics_light.9.png Binary files differindex 3c17c5eec..3c17c5eec 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_ics_light.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_klp_dark.9.png Binary files differindex dfb16a76b..dfb16a76b 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_klp_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_klp_light.9.png Binary files differindex 17144b673..17144b673 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_klp_light.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_ics_dark.9.png Binary files differindex 818ea70fd..818ea70fd 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_ics_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_klp_dark.9.png Binary files differindex bf1d34686..bf1d34686 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_klp_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..6fd8eedd3 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_ics.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_ics_dark.9.png Binary files differindex a476d2a9e..a476d2a9e 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_ics.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_ics_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_klp.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_klp_dark.9.png Binary files differindex 962277165..962277165 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_klp.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_klp_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..91322882f --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/emoji_category_tab_selected_ics.9.png b/java/res/drawable-xxhdpi/emoji_category_tab_selected_ics.9.png Binary files differnew file mode 100644 index 000000000..b13ee2b6a --- /dev/null +++ b/java/res/drawable-xxhdpi/emoji_category_tab_selected_ics.9.png diff --git a/java/res/drawable-xxhdpi/emoji_category_tab_selected_klp.9.png b/java/res/drawable-xxhdpi/emoji_category_tab_selected_klp.9.png Binary files differnew file mode 100644 index 000000000..c81e651dd --- /dev/null +++ b/java/res/drawable-xxhdpi/emoji_category_tab_selected_klp.9.png diff --git a/java/res/drawable-xxhdpi/tab_unselected.9.png b/java/res/drawable-xxhdpi/emoji_category_tab_unselected_holo_dark.9.png Binary files differindex 389188695..389188695 100644 --- a/java/res/drawable-xxhdpi/tab_unselected.9.png +++ b/java/res/drawable-xxhdpi/emoji_category_tab_unselected_holo_dark.9.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_emoticons_activated_holo_dark.png b/java/res/drawable-xxhdpi/ic_emoji_emoticons_activated_holo_dark.png Binary files differnew file mode 100644 index 000000000..a217269cb --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_emoji_emoticons_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_emoticons_normal_holo_dark.png b/java/res/drawable-xxhdpi/ic_emoji_emoticons_normal_holo_dark.png Binary files differnew file mode 100644 index 000000000..dfa43cd5b --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_emoji_emoticons_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_nature_activated_holo_dark.png Binary files differindex 470fd695c..470fd695c 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_nature_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_nature_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_nature_normal_holo_dark.png Binary files differindex a7fde0ec5..a7fde0ec5 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_nature_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_nature_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_objects_activated_holo_dark.png Binary files differindex c582b704a..c582b704a 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_objects_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_objects_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_objects_normal_holo_dark.png Binary files differindex acc95d725..acc95d725 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_objects_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_objects_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_people_activated_holo_dark.png Binary files differindex 5973ac355..5973ac355 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_people_normal_holo_dark.png Binary files differindex 22e06f808..22e06f808 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_places_activated_holo_dark.png Binary files differindex 690e95fd5..690e95fd5 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_places_normal_holo_dark.png Binary files differindex ced4b08cf..ced4b08cf 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_holo_dark.png Binary files differindex 25e847ef3..25e847ef3 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recent_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_holo_dark.png Binary files differindex c86368d73..c86368d73 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recent_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_symbols_activated_holo_dark.png Binary files differindex 29dfc7118..29dfc7118 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_activated.png +++ b/java/res/drawable-xxhdpi/ic_emoji_symbols_activated_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_symbols_normal_holo_dark.png Binary files differindex 0570567cf..0570567cf 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_normal.png +++ b/java/res/drawable-xxhdpi/ic_emoji_symbols_normal_holo_dark.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..f7814917d --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..a79499f24 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..91d5d7f90 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png Binary files differnew file mode 100644 index 000000000..dd95be61e --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png Binary files differnew file mode 100644 index 000000000..ccd270e78 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png Binary files differnew file mode 100644 index 000000000..0617c15e3 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png Binary files differnew file mode 100644 index 000000000..fc85e512d --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png Binary files differdeleted file mode 100644 index f55af308c..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png Binary files differnew file mode 100644 index 000000000..1e2819b73 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png Binary files differnew file mode 100644 index 000000000..bae6cb1fa --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_return_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8c34d1160 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_return_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png Binary files differnew file mode 100644 index 000000000..a32b6c5d9 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png Binary files differnew file mode 100644 index 000000000..21a9e0909 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png Binary files differnew file mode 100644 index 000000000..d513ad9e6 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_lxx_dark.png Binary files differnew file mode 100644 index 000000000..316903ef8 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_lxx_dark.png Binary files differnew file mode 100644 index 000000000..ddfc1a90d --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png Binary files differnew file mode 100644 index 000000000..a4df3bb0e --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..01075ebf3 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png Binary files differnew file mode 100644 index 000000000..a36724cac --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png Binary files differnew file mode 100644 index 000000000..2706dca57 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/tab_selected.9.png b/java/res/drawable-xxhdpi/tab_selected.9.png Binary files differdeleted file mode 100644 index e5efc5828..000000000 --- a/java/res/drawable-xxhdpi/tab_selected.9.png +++ /dev/null diff --git a/java/res/drawable/btn_keyboard_key_functional_ics.xml b/java/res/drawable/btn_keyboard_key_functional_ics.xml index 847ca72f4..846bccc49 100644 --- a/java/res/drawable/btn_keyboard_key_functional_ics.xml +++ b/java/res/drawable/btn_keyboard_key_functional_ics.xml @@ -17,6 +17,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Functional keys. --> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_ics" /> - <item android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_ics_dark" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/btn_keyboard_key_functional_klp.xml b/java/res/drawable/btn_keyboard_key_functional_klp.xml index 0e17ed234..7b444f7c5 100644 --- a/java/res/drawable/btn_keyboard_key_functional_klp.xml +++ b/java/res/drawable/btn_keyboard_key_functional_klp.xml @@ -17,6 +17,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Functional keys. --> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_klp" /> - <item android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_klp_dark" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/btn_keyboard_key_functional_lxx_dark.xml b/java/res/drawable/btn_keyboard_key_functional_lxx_dark.xml new file mode 100644 index 000000000..fd1dbb9f0 --- /dev/null +++ b/java/res/drawable/btn_keyboard_key_functional_lxx_dark.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Functional keys. --> + <item android:state_pressed="true" + android:drawable="@color/key_background_pressed_lxx_dark" /> + <item android:drawable="@color/key_background_lxx_dark" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_key_ics.xml b/java/res/drawable/btn_keyboard_key_ics.xml index 259bb9ba5..0bb098d23 100644 --- a/java/res/drawable/btn_keyboard_key_ics.xml +++ b/java/res/drawable/btn_keyboard_key_ics.xml @@ -15,34 +15,28 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Functional keys. --> - <item android:state_single="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_ics" /> - <item android:state_single="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" /> - <!-- Action keys. --> <item android:state_active="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_ics" /> + android:drawable="@drawable/btn_keyboard_key_pressed_ics_dark" /> <item android:state_active="true" - android:drawable="@drawable/btn_keyboard_key_dark_active_ics" /> + android:drawable="@drawable/btn_keyboard_key_active_ics_dark" /> <!-- Toggle keys. Use checkable/checked state. --> <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_on_ics" /> + android:drawable="@drawable/btn_keyboard_key_pressed_on_ics_dark" /> <item android:state_checkable="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_off_ics" /> + android:drawable="@drawable/btn_keyboard_key_pressed_off_ics_dark" /> <item android:state_checkable="true" android:state_checked="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_on_ics" /> + android:drawable="@drawable/btn_keyboard_key_normal_on_ics_dark" /> <item android:state_checkable="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_off_holo" /> + android:drawable="@drawable/btn_keyboard_key_normal_off_holo_dark" /> <!-- Empty background keys. --> <item android:state_empty="true" - android:drawable="@drawable/transparent" /> + android:drawable="@android:color/transparent" /> <!-- Normal keys. --> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_light_pressed_ics" /> - <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_ics_light" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_light" /> </selector> diff --git a/java/res/drawable/btn_keyboard_key_klp.xml b/java/res/drawable/btn_keyboard_key_klp.xml index 16b5fa00b..2a202a179 100644 --- a/java/res/drawable/btn_keyboard_key_klp.xml +++ b/java/res/drawable/btn_keyboard_key_klp.xml @@ -15,34 +15,28 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Functional keys. --> - <item android:state_single="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_klp" /> - <item android:state_single="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" /> - <!-- Action keys. --> <item android:state_active="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_klp" /> + android:drawable="@drawable/btn_keyboard_key_pressed_klp_dark" /> <item android:state_active="true" - android:drawable="@drawable/btn_keyboard_key_dark_active_klp" /> + android:drawable="@drawable/btn_keyboard_key_active_klp_dark" /> <!-- Toggle keys. Use checkable/checked state. --> <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_on_klp" /> + android:drawable="@drawable/btn_keyboard_key_pressed_on_klp_dark" /> <item android:state_checkable="true" android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_dark_pressed_off_klp" /> + android:drawable="@drawable/btn_keyboard_key_pressed_off_klp_dark" /> <item android:state_checkable="true" android:state_checked="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_on_klp" /> + android:drawable="@drawable/btn_keyboard_key_normal_on_klp_dark" /> <item android:state_checkable="true" - android:drawable="@drawable/btn_keyboard_key_dark_normal_off_holo" /> + android:drawable="@drawable/btn_keyboard_key_normal_off_holo_dark" /> <!-- Empty background keys. --> <item android:state_empty="true" - android:drawable="@drawable/transparent" /> + android:drawable="@android:color/transparent" /> <!-- Normal keys. --> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_light_pressed_klp" /> - <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_klp_light" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_light" /> </selector> diff --git a/java/res/drawable/btn_keyboard_key_lxx_dark.xml b/java/res/drawable/btn_keyboard_key_lxx_dark.xml new file mode 100644 index 000000000..bb1789ae3 --- /dev/null +++ b/java/res/drawable/btn_keyboard_key_lxx_dark.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Action keys. --> + <item android:state_active="true" android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_active_pressed_lxx_dark" /> + <item android:state_active="true" + android:drawable="@drawable/btn_keyboard_key_active_lxx_dark" /> + + <!-- Toggle keys. Use checkable/checked state. --> + <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_pressed_on_lxx_dark" /> + <item android:state_checkable="true" android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_pressed_off_lxx_dark" /> + <item android:state_checkable="true" android:state_checked="true" + android:drawable="@drawable/btn_keyboard_key_normal_on_lxx_dark" /> + <item android:state_checkable="true" + android:drawable="@drawable/btn_keyboard_key_normal_off_lxx_dark" /> + + <!-- Empty background keys. --> + <item android:state_empty="true" + android:drawable="@color/key_background_lxx_dark" /> + + <!-- Normal keys. --> + <item android:state_pressed="true" + android:drawable="@color/key_background_pressed_lxx_dark" /> + <item android:drawable="@color/key_background_lxx_dark" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_key_popup_ics.xml b/java/res/drawable/btn_keyboard_key_popup_ics.xml index 31b613176..17d646b8f 100644 --- a/java/res/drawable/btn_keyboard_key_popup_ics.xml +++ b/java/res/drawable/btn_keyboard_key_popup_ics.xml @@ -17,5 +17,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/btn_keyboard_key_popup_selected_ics" /> - <item android:drawable="@drawable/transparent" /> + <item android:drawable="@android:color/transparent" /> </selector> diff --git a/java/res/drawable/btn_keyboard_key_popup_klp.xml b/java/res/drawable/btn_keyboard_key_popup_klp.xml index 62cbca8ae..9dfc93ae8 100644 --- a/java/res/drawable/btn_keyboard_key_popup_klp.xml +++ b/java/res/drawable/btn_keyboard_key_popup_klp.xml @@ -17,5 +17,5 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/btn_keyboard_key_popup_selected_klp" /> - <item android:drawable="@drawable/transparent" /> + <item android:drawable="@android:color/transparent" /> </selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_ics.xml b/java/res/drawable/btn_keyboard_spacebar_ics.xml index 4530ea079..229f7a965 100644 --- a/java/res/drawable/btn_keyboard_spacebar_ics.xml +++ b/java/res/drawable/btn_keyboard_spacebar_ics.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_light_pressed_ics" /> - <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_ics_light" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_light" /> </selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_klp.xml b/java/res/drawable/btn_keyboard_spacebar_klp.xml index 6b07a392f..9882f9a74 100644 --- a/java/res/drawable/btn_keyboard_spacebar_klp.xml +++ b/java/res/drawable/btn_keyboard_spacebar_klp.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" - android:drawable="@drawable/btn_keyboard_key_light_pressed_klp" /> - <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> + android:drawable="@drawable/btn_keyboard_key_pressed_klp_light" /> + <item android:drawable="@drawable/btn_keyboard_key_normal_holo_light" /> </selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml b/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml new file mode 100644 index 000000000..5c595d9ed --- /dev/null +++ b/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@color/key_background_pressed_lxx_dark" /> + <item android:drawable="@color/key_background_lxx_dark" /> +</selector> diff --git a/java/res/drawable/transparent.xml b/java/res/drawable/btn_suggestion_lxx_dark.xml index 855cf2ad5..84a91209e 100644 --- a/java/res/drawable/transparent.xml +++ b/java/res/drawable/btn_suggestion_lxx_dark.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2011, The Android Open Source Project +** 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. @@ -18,13 +18,10 @@ */ --> -<shape +<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle" > - <solid - android:color="@android:color/transparent" /> - <size - android:width="50dp" - android:height="40dp" /> -</shape> + <item + android:state_pressed="true" + android:drawable="@color/suggested_word_background_selected_lxx_dark" /> +</selector> diff --git a/java/res/drawable/ic_emoji_emoticons_holo_dark.xml b/java/res/drawable/ic_emoji_emoticons_holo_dark.xml new file mode 100644 index 000000000..59e23499e --- /dev/null +++ b/java/res/drawable/ic_emoji_emoticons_holo_dark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_emoticons_activated_holo_dark" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_emoticons_activated_holo_dark" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_emoticons_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_emoticons_normal_holo_dark" /> +</selector> diff --git a/java/res/color/emoji_tab_label_color_holo.xml b/java/res/drawable/ic_emoji_nature_holo_dark.xml index 373e9314b..b94629580 100644 --- a/java/res/color/emoji_tab_label_color_holo.xml +++ b/java/res/drawable/ic_emoji_nature_holo_dark.xml @@ -21,13 +21,13 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:color="@color/key_text_color_holo" /> + android:drawable="@drawable/ic_emoji_nature_activated_holo_dark" /> <item android:state_pressed="true" - android:color="@color/key_text_color_holo" /> + android:drawable="@drawable/ic_emoji_nature_activated_holo_dark" /> <item android:state_selected="true" - android:color="@color/key_text_color_holo" /> + android:drawable="@drawable/ic_emoji_nature_activated_holo_dark" /> <item - android:color="@color/key_text_inactivated_color_holo" /> + android:drawable="@drawable/ic_emoji_nature_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/ic_emoji_nature_light.xml b/java/res/drawable/ic_emoji_objects_holo_dark.xml index 543409e03..266e81e0b 100644 --- a/java/res/drawable/ic_emoji_nature_light.xml +++ b/java/res/drawable/ic_emoji_objects_holo_dark.xml @@ -21,13 +21,12 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:drawable="@drawable/ic_emoji_nature_light_activated" /> + android:drawable="@drawable/ic_emoji_objects_activated_holo_dark" /> <item android:state_pressed="true" - android:drawable="@drawable/ic_emoji_nature_light_activated" /> + android:drawable="@drawable/ic_emoji_objects_activated_holo_dark" /> <item android:state_selected="true" - android:drawable="@drawable/ic_emoji_nature_light_activated" /> - <item - android:drawable="@drawable/ic_emoji_nature_light_normal" /> + android:drawable="@drawable/ic_emoji_objects_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_objects_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/ic_emoji_objects_light.xml b/java/res/drawable/ic_emoji_people_holo_dark.xml index 4096e695b..15955d261 100644 --- a/java/res/drawable/ic_emoji_objects_light.xml +++ b/java/res/drawable/ic_emoji_people_holo_dark.xml @@ -21,12 +21,12 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:drawable="@drawable/ic_emoji_objects_light_activated" /> + android:drawable="@drawable/ic_emoji_people_activated_holo_dark" /> <item android:state_pressed="true" - android:drawable="@drawable/ic_emoji_objects_light_activated" /> + android:drawable="@drawable/ic_emoji_people_activated_holo_dark" /> <item android:state_selected="true" - android:drawable="@drawable/ic_emoji_objects_light_activated" /> - <item android:drawable="@drawable/ic_emoji_objects_light_normal" /> + android:drawable="@drawable/ic_emoji_people_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_people_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/ic_emoji_people_light.xml b/java/res/drawable/ic_emoji_places_holo_dark.xml index ea9e406a4..260bbd843 100644 --- a/java/res/drawable/ic_emoji_people_light.xml +++ b/java/res/drawable/ic_emoji_places_holo_dark.xml @@ -21,12 +21,12 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" - android:drawable="@drawable/ic_emoji_people_light_activated" /> + android:drawable="@drawable/ic_emoji_places_activated_holo_dark" /> <item android:state_pressed="true" - android:drawable="@drawable/ic_emoji_people_light_activated" /> + android:drawable="@drawable/ic_emoji_places_activated_holo_dark" /> <item android:state_selected="true" - android:drawable="@drawable/ic_emoji_people_light_activated" /> - <item android:drawable="@drawable/ic_emoji_people_light_normal" /> + android:drawable="@drawable/ic_emoji_places_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_places_normal_holo_dark" /> </selector> diff --git a/java/res/drawable/ic_emoji_places_light.xml b/java/res/drawable/ic_emoji_places_light.xml deleted file mode 100644 index 312cad9c3..000000000 --- a/java/res/drawable/ic_emoji_places_light.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_focused="true" - android:drawable="@drawable/ic_emoji_places_light_activated" /> - <item - android:state_pressed="true" - android:drawable="@drawable/ic_emoji_places_light_activated" /> - <item - android:state_selected="true" - android:drawable="@drawable/ic_emoji_places_light_activated" /> - <item android:drawable="@drawable/ic_emoji_places_light_normal" /> -</selector> diff --git a/java/res/drawable/ic_emoji_recent_light.xml b/java/res/drawable/ic_emoji_recent_light.xml deleted file mode 100644 index 8c2123f83..000000000 --- a/java/res/drawable/ic_emoji_recent_light.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_focused="true" - android:drawable="@drawable/ic_emoji_recent_light_activated" /> - <item - android:state_pressed="true" - android:drawable="@drawable/ic_emoji_recent_light_activated" /> - <item - android:state_selected="true" - android:drawable="@drawable/ic_emoji_recent_light_activated" /> - <item android:drawable="@drawable/ic_emoji_recent_light_normal" /> -</selector> diff --git a/java/res/drawable/ic_emoji_recents_holo_dark.xml b/java/res/drawable/ic_emoji_recents_holo_dark.xml new file mode 100644 index 000000000..f14349f34 --- /dev/null +++ b/java/res/drawable/ic_emoji_recents_holo_dark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_recents_activated_holo_dark" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_recents_activated_holo_dark" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_recents_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_recents_normal_holo_dark" /> +</selector> diff --git a/java/res/drawable/ic_emoji_symbols_holo_dark.xml b/java/res/drawable/ic_emoji_symbols_holo_dark.xml new file mode 100644 index 000000000..831d6594b --- /dev/null +++ b/java/res/drawable/ic_emoji_symbols_holo_dark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/ic_emoji_symbols_activated_holo_dark" /> + <item + android:state_pressed="true" + android:drawable="@drawable/ic_emoji_symbols_activated_holo_dark" /> + <item + android:state_selected="true" + android:drawable="@drawable/ic_emoji_symbols_activated_holo_dark" /> + <item android:drawable="@drawable/ic_emoji_symbols_normal_holo_dark" /> +</selector> diff --git a/java/res/drawable/ic_emoji_symbols_light.xml b/java/res/drawable/ic_emoji_symbols_light.xml deleted file mode 100644 index 79aaf0fc5..000000000 --- a/java/res/drawable/ic_emoji_symbols_light.xml +++ /dev/null @@ -1,32 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:state_focused="true" - android:drawable="@drawable/ic_emoji_symbols_light_activated" /> - <item - android:state_pressed="true" - android:drawable="@drawable/ic_emoji_symbols_light_activated" /> - <item - android:state_selected="true" - android:drawable="@drawable/ic_emoji_symbols_light_activated" /> - <item android:drawable="@drawable/ic_emoji_symbols_light_normal" /> -</selector> diff --git a/java/res/drawable/keyboard_key_feedback_lxx_dark.xml b/java/res/drawable/keyboard_key_feedback_lxx_dark.xml new file mode 100644 index 000000000..ab1109bcc --- /dev/null +++ b/java/res/drawable/keyboard_key_feedback_lxx_dark.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Left edge --> + <item latin:state_left_edge="true" latin:state_has_morekeys="true" + android:drawable="@drawable/keyboard_key_feedback_more_background_lxx_dark" /> + <item latin:state_left_edge="true" + android:drawable="@drawable/keyboard_key_feedback_background_lxx_dark" /> + + <!-- Right edge --> + <item latin:state_right_edge="true" latin:state_has_morekeys="true" + android:drawable="@drawable/keyboard_key_feedback_more_background_lxx_dark" /> + <item latin:state_right_edge="true" + android:drawable="@drawable/keyboard_key_feedback_background_lxx_dark" /> + + <item latin:state_has_morekeys="true" + android:drawable="@drawable/keyboard_key_feedback_more_background_lxx_dark" /> + <item android:drawable="@drawable/keyboard_key_feedback_background_lxx_dark" /> +</selector> diff --git a/java/res/layout/emoji_keyboard_page.xml b/java/res/layout/emoji_keyboard_page.xml index 9afad366a..0d1086171 100644 --- a/java/res/layout/emoji_keyboard_page.xml +++ b/java/res/layout/emoji_keyboard_page.xml @@ -18,7 +18,7 @@ */ --> -<com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView +<com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/emoji_keyboard_page" android:layoutDirection="ltr" diff --git a/java/res/layout/emoji_keyboard_tab_icon.xml b/java/res/layout/emoji_keyboard_tab_icon.xml index 13bb41ca2..15f9c3a3e 100644 --- a/java/res/layout/emoji_keyboard_tab_icon.xml +++ b/java/res/layout/emoji_keyboard_tab_icon.xml @@ -19,6 +19,8 @@ --> <!-- Note: contentDescription will be added programatically in {@link EmojiPalettesView}. --> +<!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="0dip" android:layout_weight="1.0" @@ -26,4 +28,6 @@ android:gravity="center" android:scaleType="center" android:contentDescription="@null" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" /> diff --git a/java/res/layout/emoji_palettes_view.xml b/java/res/layout/emoji_palettes_view.xml index 552a474b4..9ff090aad 100644 --- a/java/res/layout/emoji_palettes_view.xml +++ b/java/res/layout/emoji_palettes_view.xml @@ -18,7 +18,7 @@ */ --> -<com.android.inputmethod.keyboard.EmojiPalettesView +<com.android.inputmethod.keyboard.emoji.EmojiPalettesView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/emoji_keyboard_view" android:orientation="vertical" @@ -41,11 +41,7 @@ android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/tab_selected" - android:divider="@null" - android:tabStripEnabled="true" - android:tabStripLeft="@drawable/tab_unselected" - android:tabStripRight="@drawable/tab_unselected" /> + android:divider="@null" /> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dip" @@ -65,24 +61,26 @@ android:layout_width="2dip" android:layout_height="match_parent" android:background="@drawable/suggestions_strip_divider" /> + <!-- TODO: Implement KeyView and replace this. --> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <ImageButton android:id="@+id/emoji_keyboard_delete" android:layout_width="0dip" android:layout_weight="12.5" android:layout_height="match_parent" - android:background="@color/emoji_key_background_color" - android:src="@drawable/sym_keyboard_delete_holo_dark" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" android:contentDescription="@string/spoken_description_delete" /> </LinearLayout> <android.support.v4.view.ViewPager android:id="@+id/emoji_keyboard_pager" android:layout_width="match_parent" android:layout_height="wrap_content" /> - <com.android.inputmethod.keyboard.EmojiCategoryPageIndicatorView + <com.android.inputmethod.keyboard.emoji.EmojiCategoryPageIndicatorView android:id="@+id/emoji_category_page_id_view" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:background="@color/emoji_category_page_id_view_background" /> + android:layout_height="2dip" /> <LinearLayout android:id="@+id/emoji_action_bar" android:orientation="horizontal" @@ -90,23 +88,47 @@ android:layout_height="0dip" android:layout_weight="1" > + <!-- TODO: Implement a KeyView and replace this. --> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <TextView android:id="@+id/emoji_keyboard_alphabet_left" android:layout_width="0dip" android:layout_weight="0.15" android:gravity="center" - android:layout_height="match_parent" /> - <ImageButton + android:layout_height="match_parent" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" /> + <!-- TODO: Implement KeyView and replace this. --> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> + <RelativeLayout android:id="@+id/emoji_keyboard_space" android:layout_width="0dip" android:layout_weight="0.70" android:layout_height="match_parent" - android:contentDescription="@string/spoken_description_space"/> + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" + android:contentDescription="@string/spoken_description_space"> + <!-- WORKAROUND: Show the spacebar icon as a bacground of this View. --> + <View + android:id="@+id/emoji_keyboard_space_icon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="12dp" + android:layout_marginRight="12dp" + android:layout_centerInParent="true" /> + </RelativeLayout> + <!-- TODO: Implement KeyView and replace this. --> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <TextView android:id="@+id/emoji_keyboard_alphabet_right" android:layout_width="0dip" android:layout_weight="0.15" android:gravity="center" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" /> </LinearLayout> -</com.android.inputmethod.keyboard.EmojiPalettesView> +</com.android.inputmethod.keyboard.emoji.EmojiPalettesView> diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index ed387e5b2..ff0b403d1 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -43,8 +43,6 @@ android:layout_width="match_parent" android:layout_height="@dimen/config_suggestions_strip_height" android:gravity="center_vertical" - android:paddingRight="@dimen/config_suggestions_strip_horizontal_padding" - android:paddingLeft="@dimen/config_suggestions_strip_horizontal_padding" style="?attr/suggestionStripViewStyle" /> <!-- To ensure that key preview popup is correctly placed when the current system locale is diff --git a/java/res/layout/research_feedback_activity.xml b/java/res/layout/research_feedback_activity.xml deleted file mode 100644 index a6b8b8a43..000000000 --- a/java/res/layout/research_feedback_activity.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<com.android.inputmethod.research.FeedbackLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:id="@+id/research_feedback_layout" -> - - <fragment - android:id="@+id/research_feedback_fragment" - android:name="com.android.inputmethod.research.FeedbackFragment" - android:layout_width="match_parent" - android:layout_height="wrap_content" - /> -</com.android.inputmethod.research.FeedbackLayout> diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml deleted file mode 100644 index fb5c27815..000000000 --- a/java/res/layout/research_feedback_fragment_layout.xml +++ /dev/null @@ -1,115 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<!-- Adapted from frameworks/base/core/res/res/layout/alert_dialog_holo.xml. We - want a dialog, but it must be its own activity so we can launch the soft - keyboard on it. A regular dialog will not work since it would be launched from - the IME. --> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="8dip" - android:layout_marginEnd="8dip" - android:orientation="vertical"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - <View android:layout_width="match_parent" - android:layout_height="2dip" - android:visibility="gone" - android:background="@android:color/holo_blue_light" /> - <TextView - style="?android:attr/windowTitleStyle" - android:singleLine="true" - android:ellipsize="end" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="64dip" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:gravity="center_vertical|left" - android:text="@string/research_feedback_dialog_title" /> - <View - android:layout_width="match_parent" - android:layout_height="2dip" - android:background="@android:color/holo_blue_light" /> - </LinearLayout> - - <EditText - android:id="@+id/research_feedback_contents" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_gravity="fill_horizontal|center_vertical" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" - android:layout_marginBottom="8dip" - android:layout_marginTop="8dip" - android:minLines="2" - android:scrollbars="vertical" - android:hint="@string/research_feedback_hint" - android:inputType="textMultiLine|textCapSentences"> - <requestFocus /> - </EditText> - <CheckBox - android:id="@+id/research_feedback_include_account_name" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:layout_marginBottom="8dip" - android:checked="false" - android:text="@string/research_feedback_include_account_name_label" /> - <CheckBox - android:id="@+id/research_feedback_include_recording_checkbox" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_marginLeft="16dip" - android:layout_marginRight="16dip" - android:layout_marginBottom="8dip" - android:checked="false" - android:text="@string/research_feedback_include_recording_label" /> - <LinearLayout - style="?android:attr/buttonBarStyle" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layoutDirection="locale" - android:measureWithLargestChild="true"> - <Button - android:id="@+id/research_feedback_cancel_button" - android:layout_width="wrap_content" - android:layout_gravity="left" - android:layout_weight="1" - android:maxLines="2" - style="?android:attr/buttonBarButtonStyle" - android:textSize="14sp" - android:text="@string/research_feedback_cancel" - android:layout_height="wrap_content" /> - <Button - android:id="@+id/research_feedback_send_button" - android:layout_width="wrap_content" - android:layout_gravity="right" - android:layout_weight="1" - android:maxLines="2" - style="?android:attr/buttonBarButtonStyle" - android:textSize="14sp" - android:text="@string/research_feedback_send" - android:layout_height="wrap_content" /> - </LinearLayout> - </LinearLayout> -</ScrollView> diff --git a/java/res/layout/research_feedback_layout.xml b/java/res/layout/research_feedback_layout.xml deleted file mode 100644 index bacd19101..000000000 --- a/java/res/layout/research_feedback_layout.xml +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2012 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:orientation="vertical" -> - - <EditText - android:id="@+id/research_feedback_contents" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_gravity="fill_horizontal|center_vertical" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" - android:layout_marginBottom="8dip" - android:layout_marginTop="8dip" - android:lines="2" - android:hint="@string/research_feedback_hint" - android:inputType="textMultiLine" - android:imeOptions="flagNoFullscreen" - android:focusable="true" - > - <requestFocus /> - </EditText> - - <CheckBox - android:id="@+id/research_feedback_include_history" - android:layout_height="wrap_content" - android:layout_width="match_parent" - android:layout_marginBottom="8dip" - android:checked="true" - android:text="@string/research_feedback_include_history_label" - /> -</LinearLayout> diff --git a/java/res/layout/suggestion_divider.xml b/java/res/layout/suggestion_divider.xml index 149095147..563599d82 100644 --- a/java/res/layout/suggestion_divider.xml +++ b/java/res/layout/suggestion_divider.xml @@ -18,11 +18,17 @@ */ --> +<!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <ImageView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="match_parent" + android:padding="0dp" + android:gravity="center" android:src="@drawable/suggestions_strip_divider" android:contentDescription="@null" - android:padding="0dp" - android:gravity="center" /> + android:clickable="false" + android:longClickable="false" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" /> diff --git a/java/res/layout/suggestions_strip.xml b/java/res/layout/suggestions_strip.xml index 0b614993b..489477990 100644 --- a/java/res/layout/suggestions_strip.xml +++ b/java/res/layout/suggestions_strip.xml @@ -20,17 +20,28 @@ <merge xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <LinearLayout android:id="@+id/suggestions_strip" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:layout_marginLeft="@dimen/config_suggestions_strip_horizontal_margin" + android:layout_marginRight="@dimen/config_suggestions_strip_horizontal_margin" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" /> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <LinearLayout android:id="@+id/add_to_dictionary_strip" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="invisible"> + android:layout_marginLeft="@dimen/config_suggestions_strip_horizontal_margin" + android:layout_marginRight="@dimen/config_suggestions_strip_horizontal_margin" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false"> <TextView android:id="@+id/word_to_save" android:layout_width="match_parent" @@ -45,11 +56,17 @@ android:textAlignment="viewStart" style="?attr/suggestionWordStyle" /> </LinearLayout> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <LinearLayout android:id="@+id/important_notice_strip" android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:layout_marginLeft="@dimen/config_suggestions_strip_horizontal_margin" + android:layout_marginRight="@dimen/config_suggestions_strip_horizontal_margin" + android:hapticFeedbackEnabled="false" + android:soundEffectsEnabled="false" > <TextView android:id="@+id/important_notice_title" android:layout_width="match_parent" @@ -58,4 +75,13 @@ android:textSize="16sp" style="?attr/suggestionWordStyle" /> </LinearLayout> + <ImageButton + android:id="@+id/suggestions_strip_voice_key" + android:layout_width="@dimen/config_suggestions_strip_edge_key_width" + android:layout_height="fill_parent" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:contentDescription="@string/spoken_description_mic" + style="?attr/suggestionWordStyle" /> </merge> diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict Binary files differindex 49adc9a19..d631d6fbc 100644 --- a/java/res/raw/main_en.dict +++ b/java/res/raw/main_en.dict diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict Binary files differindex fe24cd624..83eefe4f6 100644 --- a/java/res/raw/main_es.dict +++ b/java/res/raw/main_es.dict diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict Binary files differindex 94d1b9670..19532d9bf 100644 --- a/java/res/raw/main_fr.dict +++ b/java/res/raw/main_fr.dict diff --git a/java/res/values-af/strings-action-keys.xml b/java/res/values-af/strings-action-keys.xml index c5cd71ab7..bef175b16 100644 --- a/java/res/values-af/strings-action-keys.xml +++ b/java/res/values-af/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Vorige"</string> <string name="label_done_key" msgid="7564866296502630852">"Klaar"</string> <string name="label_send_key" msgid="482252074224462163">"Stuur"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Laat wag"</string> <string name="label_wait_key" msgid="5891247853595466039">"Wag"</string> </resources> diff --git a/java/res/values-af/strings-talkback-descriptions.xml b/java/res/values-af/strings-talkback-descriptions.xml index 3529e5a5d..09abb5f6c 100644 --- a/java/res/values-af/strings-talkback-descriptions.xml +++ b/java/res/values-af/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Geen teks is ingevoer nie"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> voer outokorrigering uit"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Sleutelkode %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift aan (tik om te deaktiveer)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Kasslot aan (tik om te deaktiveer)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Meer simbole"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simbole"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Vee uit"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simbole"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letters"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Vorige"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift geaktiveer"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Bokas-slot geaktiveer"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift gedeaktiveer"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Simboolmodus"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Meersimbole-modus"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Lettermodus"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Foonmodus"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Foonsimbool-modus"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Plekke"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simbole"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emosiekone"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index ce7cb18fc..2d1653f28 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Speltoetser gebruik inskrywings uit jou kontaklys"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Stel kontakname voor"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gebruik name van kontakte vir voorstelle en korreksies"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Gepersonaliseerde voorstelle"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dubbelspasie-punt"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbeltik op spasiebalk voeg \'n punt in, gevolg deur \'n spasie"</string> <string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Invoertale"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Raak weer om te stoor"</string> <string name="has_dictionary" msgid="6071847973466625007">"Woordeboek beskikbaar"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktiveer gebruikerterugvoer"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Help om hierdie invoermetode-redigeerder te verbeter deur gebruikstatistiek en omvalverslae outomaties te stuur"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Sleutelbordtema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engels (VK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engels (VS)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktiveer"</string> <string name="not_now" msgid="6172462888202790482">"Nie nou nie"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dieselfde invoerstyl bestaan reeds: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Bruikbaarheidstudie-modus"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging van sleutellangdruk"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Sleuteldruk se vibrasie-tydsduur"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sleuteldruk se klankvolume"</string> diff --git a/java/res/values-am/strings-action-keys.xml b/java/res/values-am/strings-action-keys.xml index 1813a86d8..26e45135b 100644 --- a/java/res/values-am/strings-action-keys.xml +++ b/java/res/values-am/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"ቀዳሚ"</string> <string name="label_done_key" msgid="7564866296502630852">"ተደርጓል"</string> <string name="label_send_key" msgid="482252074224462163">"ላክ"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"ቆም በል"</string> <string name="label_wait_key" msgid="5891247853595466039">"ጠብቅ"</string> </resources> diff --git a/java/res/values-am/strings-talkback-descriptions.xml b/java/res/values-am/strings-talkback-descriptions.xml index 2d6f0e56d..1b1da01e5 100644 --- a/java/res/values-am/strings-talkback-descriptions.xml +++ b/java/res/values-am/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"ምንም ፅሁፍ አልገባም"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>ን ወደ <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> ያርመዋል"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> የራስ ሰር እርማት ያከናውናል"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"የቁልፍ ኮድ %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"ቀይር"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"መቀያየሪያ ቁልፍ በርቷል (ለማሰናከል መታ ያድርጉ)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"አብይ ፊደል ማድረጊያ ቁልፍ በርቷል (ለማሰናክል ነካ ያድርጉ)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"ተጨማሪ ምልክቶች"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"ምልክቶች"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"ሰርዝ"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"ምልክቶች"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"ደብዳቤዎች"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"ቀዳሚ"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"መቀያየሪያ ቁልፍ ነቅቷል"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"አብይ ፊደል ማድረጊያ ቁልፍ ነቅቷል"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"መቀያየሪያ ቁልፍ ተሰናክሏል"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"የምልክቶች ሁኔታ"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"የተጨማሪ ምልክቶች ሁነታ"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"የደብዳቤዎች ሁኔታ"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"የስልክ ሁኔታ"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"የስልክ ምልክቶች ሁኔታ"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"ቦታዎች"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"ምልክቶች"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"ስሜት ገላጭ አዶዎች"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 32eed413b..f957d1111 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ፊደል አራሚ ከእውቅያ ዝርዝርህ የገቡትን ይጠቀማል"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"የዕውቂያ ስም ጠቁም"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ከዕውቂያዎች ለጥቆማዎች እና ማስተካከያዎች ስሞች ተጠቀም"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"ግላዊ የጥቆማ አስተያየቶች"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"የድርብ-ክፍተት ነጥብ"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"የክፍተት አሞሌው ላይ ሁለቴ መታ ማድረግ አንድ ነጥብ እና ክፍተት አስከትሎ ያስገባል"</string> <string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"ቋንቋዎች አግቤት"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"ለማስቀመጥ እንደገና ንካ"</string> <string name="has_dictionary" msgid="6071847973466625007">"መዝገበ ቃላት አለ"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"የተጠቃሚ ግብረ ምላሽ አንቃ"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"አጠቃቀም ስታስቲክስ እና የብልሽት ሪፖርቶችን በራስ-ሰር በመላክ ይህን ግቤት ስልት አርታዒ እንዲሻሻል ያግዙ።"</string> <string name="keyboard_layout" msgid="8451164783510487501">"የቁልፍ ሰሌዳ ገጽታ"</string> <string name="subtype_en_GB" msgid="88170601942311355">"እንግሊዘኛ (የታላቋ ብሪታንያ)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"እንግሊዘኛ (ዩ.ኤስ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"አንቃ"</string> <string name="not_now" msgid="6172462888202790482">"አሁን አልፈልግም"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ተመሳሳዩ የግብዓት ቅጥ አስቀድሞ አለ፦ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"የተገልጋይነት ጥናት ሁነታ"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"የሰሌዳ ቁልፍ ጠቅታ በመጫን መዘግየት"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"የቁልፍ ጭነት ንዝረት ርዝመት"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"የቁልፍ ጭነት ድምጽ መጠን"</string> diff --git a/java/res/values-ar/strings-action-keys.xml b/java/res/values-ar/strings-action-keys.xml index 481b22f20..1ee4f7a2d 100644 --- a/java/res/values-ar/strings-action-keys.xml +++ b/java/res/values-ar/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"سابق"</string> <string name="label_done_key" msgid="7564866296502630852">"تم"</string> <string name="label_send_key" msgid="482252074224462163">"إرسال"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"توقف"</string> <string name="label_wait_key" msgid="5891247853595466039">"انتظر"</string> </resources> diff --git a/java/res/values-ar/strings-talkback-descriptions.xml b/java/res/values-ar/strings-talkback-descriptions.xml index 9d2eab5ae..b39033a85 100644 --- a/java/res/values-ar/strings-talkback-descriptions.xml +++ b/java/res/values-ar/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"لم يتم إدخال نص"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> لتصحيح <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> إلى <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> لإجراء التصحيح التلقائي"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"رمز المفتاح %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift يعمل (انقر للتعطيل)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock يعمل (انقر للتعطيل)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"المزيد من الرموز"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"رموز"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"حذف"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"رموز"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"أحرف"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"السابق"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"تم تمكين Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"تم تمكين Caps lock"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"تم تعطيل Shift"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"وضع الرموز"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"المزيد من وضع الرموز"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"وضع الأحرف"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"وضع الهاتف"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"وضع رموز الهاتف"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"أماكن"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"رموز"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"رموز تعبيرية"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index 13aef2a67..111b5af51 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"يستخدم المدقق الإملائي إدخالات من قائمة جهات الاتصال"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند ضغط مفتاح"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"اقتراح أسماء جهات الاتصال"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"اقتراحات مخصصة"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"نقطة المسافة المزدوجة"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"يؤدي النقر نقرًا مزدوجًا على مفتاح المسافة إلى إدخال نقطة متبوعة بمسافة"</string> <string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"لغات الإدخال"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"المس مرة أخرى للحفظ"</string> <string name="has_dictionary" msgid="6071847973466625007">"القاموس متاح"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"تمكين ملاحظات المستخدم"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"المساعدة في تحسين محرر أسلوب الإدخال هذا من خلال إرسال إحصاءات الاستخدام وتقارير الأعطال تلقائيًا"</string> <string name="keyboard_layout" msgid="8451164783510487501">"مظهر لوحة المفاتيح"</string> <string name="subtype_en_GB" msgid="88170601942311355">"الإنجليزية (المملكة المتحدة)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"الإنجليزية (الولايات المتحدة)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"تمكين"</string> <string name="not_now" msgid="6172462888202790482">"ليس الآن"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"نمط الإدخال ذاته موجود من قبل: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"وضع سهولة الاستخدام"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخير الضغط الطويل للمفاتيح"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"مدة اهتزاز الضغط على المفاتيح"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"مستوى صوت الضغط على المفاتيح"</string> diff --git a/java/res/values-az-rAZ/strings-action-keys.xml b/java/res/values-az-rAZ/strings-action-keys.xml index 513712c40..7f982d686 100644 --- a/java/res/values-az-rAZ/strings-action-keys.xml +++ b/java/res/values-az-rAZ/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Öncəki"</string> <string name="label_done_key" msgid="7564866296502630852">"Hazırdır"</string> <string name="label_send_key" msgid="482252074224462163">"Göndər"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauza"</string> <string name="label_wait_key" msgid="5891247853595466039">"Gözlə"</string> </resources> diff --git a/java/res/values-az-rAZ/strings-emoji-descriptions.xml b/java/res/values-az-rAZ/strings-emoji-descriptions.xml new file mode 100644 index 000000000..b6c259f73 --- /dev/null +++ b/java/res/values-az-rAZ/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Müəllif hüququ nişanı"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Qeydiyyat nişanı"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"İki nida işarəsi"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Suallı nida işarəsi"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Ticarət nişanı"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"İnformasiya mənbəyi"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Sola-sağa ox"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Yuxarı-aşağı ox"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Şimal-qərb ox"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Şimal-şərq ox"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Cənub-şərq ox"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Cənub-qərb ox"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Qırmaqlı sola ox"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Qırmaqlı sağa ox"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"İzləyin"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Qum saatı"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Qara sağa ikiqat üçbucaq"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Qara sola ikiqat üçbucaq"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Qara yuxarı ikiqat üçbucaq"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Qara aşağı ikiqat üçbucaq"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Zəngli saat"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Axan qumlu qum saatı"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Çərçivəli böyük latın M hərfi"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Kiçik qara kvadrat"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Kiçik ağ kvadrat"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Qara sağa uçbucaq"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Qara sola üçbucaq"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Ağ orta kvadrat"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Qara orta kvadrat"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Ağ orta kiçik kvadrat"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Qara orta kiçik kvadrat"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Şüalı qara günəş"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Bulud"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Qara telefon"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Qeydli səsvermə qutusu"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Yağışlı çətir"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"İsti içki"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Yuxarı göstərən barmaq"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Ağ gülümsəyən sima"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Qoç"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Buğa"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Əkizlər"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Xərçəng"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Şir"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Qız"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Tərəzi"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Əqrəb"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Oxatan"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Oğlaq"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Dolça"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Balıqlar"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Qara nizə"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Qara xaç"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Qara ürək"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Qara romb"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"İsti bulaqlar"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Qara universal təkrar istifadə simvolu"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Təkərli kreslo"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Lövbər"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Xəbərdarlıq işarəsi"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Yüksək voltaj simvolu"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Orta ağ çevrə"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Orta qara çevrə"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Futbol topu"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Beysbol"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Qarsız qar adamı"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Bulud arxasında günəş"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"İlantutan"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Giriş qadağası"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Kilsə"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Fəvvarə"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Deşikdə bayraq"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Yeklənli qayıq"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Çadır"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Yanacaq nasosu"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Qara qayçı"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Ağ qalın qeyd nişanı"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Təyyarə"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Zərf"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Qaldırılmış yumruq"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Qaldırılmış əl"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Qələbə əli"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Karandaş"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Qara pero"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Qalın qeyd nişanı"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Qalın x vurma işarəsi"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Qığılcımlar"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Səkkiz guşəli ulduz"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Səkkiz guşəli qara ulduz"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Qar dənəsi"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Qığılcım"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Çarpaz nişan"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Mənfi kvadratlı çarpaz nişan"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Qara sual işarəsi ornamenti"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Ağ sual işarəsi ornamenti"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Ağ nida işarəsi ornamenti"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Qalın nida işarəsi"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Yüklü qara ürək"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Qalın toplama işarəsi"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Qalın çıxma işarəsi"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Qalın bölmə işarəsi"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Qara sağa ox"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Burulmuş halqa"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"İki dəfə burulmuş halqa"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Yuxarı sağa əyilən ox"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Aşağı sağa əyilən ox"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Qara sola ox"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Qara yuxarı ox"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Qara aşağı ox"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Qara böyük kvadrat"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Ağ böyük kvadrat"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Ağ orta ulduz"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Böyük qalın çevrə"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Dalğalı tire"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Qismən dəyişmə işarəsi"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Çevrəli təbrik ideoqramı"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Çevrəli sirr ideoqramı"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Mahconq plitəsi qırmızı əjdaha"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Kart oyunu qara coker"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"A qan növü"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"B qan növü"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"O qan növü"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Parkinq yeri"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"AB qan növü"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"Kvadrat CL"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Kvadrat kul"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Kvadrat azad"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"Kvadrat ID"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"Kvadrat yeni"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"Kvadrat N G"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"Kvadrat OK"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"Kvadrat SOS"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"Nidalı kvadrat"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"Kvadrat vs"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Burada kvadrat katakana"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Kvadrat katakana xidməti"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Ödənişsiz kvadrat ideoqram"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Rezerv edilmiş yer kvadrat ideoqram"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Kvadrat qadağa ideoqramı"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Kvadrat vakansiya ideoqramı"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Kvadrat qəbul ideoqramı"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Kvadrat tam məşğulluq ideoqramı"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Kvadrat ödənilmiş ideoqramı"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Aylıq kvadrat ideoqram"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Kvadrat tətbiq ideoqramı"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Kvadrat endirim ideoqramı"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Kvadrat biznes ideoqramı"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Çevrəli üstünlük ideoqramı"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Çevrəli qəbul ideoqramı"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Siklon"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Dumanlı"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Bağlı çətir"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Ulduzlu gecə"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Dağların arxasından doğan günəş"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Günəşin doğuşu"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Toranlıqda şəhər peyzajı"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Binaların arxasında günün batması"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Göy qurşağı"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Gecə vaxtı körpü"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Su dalğası"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Vulkan"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Samanyolu"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Avropa-Afrika Yer Qlobusu"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Amerika Yer Qlobusu"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Asiya-Avstraliya Yer Qlobusu"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Meridianlı qlobus"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Yeni ay simvolu"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Böyüyən aypara simvolu"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"İlk rüb ay simvolu"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Böyüyən yumru ay simvolu"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Tam ay simvolu"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Kiçilən yumru ay simvolu"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Son rüb ay simvolu"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Kiçilən aypara simvolu"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Aypara"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Simalı yeni ay"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Simalı ilk rüb ayı"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Simalı son rüb ayı"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Simalı tam ay"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Simalı günəş"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Parlaq ulduz"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Düşən ulduz"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Şabalıd"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Fidan"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Həmişəyaşıl ağac"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Yarpaqlı ağac"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Palma ağacı"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Kaktus"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Zanbaq"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Albalı çiçəyi"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Qızılgül"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Əməköməci"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Günəbaxan"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Çiçək"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Qarğıdalı qıçası"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Düyü qıçası"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Ot"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Dörd yarpaqlı yonca"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Ağcaqayın yarpağı"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Düşmüş yarpaq"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Xəzan yarpağı"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Göbələk"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Pomidor"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Badımcan"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Üzüm"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Yemiş"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Qarpız"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Mandarin"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Lemon"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Banan"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Ananas"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Qırmızı alma"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Yaşıl alma"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Armud"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Şaftalı"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Albalı"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Çiyələk"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Hamburger"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Pizza dilimi"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Sümüklü ət"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Quş ayağı"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Düyü krekeri"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Düyü küftəsi"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Bişmiş düyü"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Karri və düyü"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Buxarlanan kasa"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Spagetti"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Çörək"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Fransızsayağı kartof"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Qovrulmuş şirin kartof"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Danqo"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oden"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Suşi"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Qızardılmış krevet"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Burulğan dizaynlı balıq piroqu"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Yumşaq dondurma"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Buz deserti"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Dondurma"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Ponçik"</string> + <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Kökə"</string> + <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Şokolad batonu"</string> + <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Konfet"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Nabat"</string> + <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Bişmiş krem"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Bal dibçəyi"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Dənəvər peçenye"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Bento qutusu"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Qazan"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Yeməkbişirmə"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Çəngəl-bıçaq"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Qulpsuz fincan"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Sake şüşəsi və fincan"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Şərab şüşəsi"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Kokteyl şüşəsi"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Tropik içki"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Pivə parçı"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Cingildəyən pivə parçları"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Uşaq qidası üçün şüşə"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Lent"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Bükülmüş hədiyyə"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Doğum günü tortu"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Jack fənəri"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Milad yolkası"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Şaxta Baba"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Fişəng"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Benqal odu fişəngi"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Şar"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Parti fişəngi"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Konfetti topu"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Tanabata ağacı"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Çarpaz bayraqlar"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Küknar dekorasiyası"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Yapon kuklaları"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Karp strimer"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Külək uğultusu"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Aya tamaşa mərasimi"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Məktəb çantası"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Akademik papaq"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Karusel at"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Mənzərə çarxı"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Dalğalı yelləncək"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Qırmaq və balıq"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Mikrofon"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Film kamerası"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Kino"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Qulaqlıq"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Rəssam palitrası"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Sehrbaz papağı"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Sirk çadırı"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Bilet"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Çəkiliş lövhəsi"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Tətbiqi incəsənət"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Video oyun"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Birbaşa zərbə"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Oyun avtomatı"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Bilyard"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Zər oyunu"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Boulinq"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Güllü oyun kartları"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Musiqili qeyd"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Çoxsaylı musiqili qeyd"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Saksafon"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Gitara"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Musiqili klaviatura"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Truba"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Skripka"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Musiqi qiyməti"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Kəmərli qaçış köynəyi"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Tennis raketi və topu"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Xizək və xizək çəkməsi"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Basketbol və çənbər"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Dama-dama bayraq"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Snoubordçu"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Qaçışçı"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Sörfçü"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Kubok"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"At yarışması"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Amerikan futbolu"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Reqbi Futbolu"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Üzgüçü"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Ev tikintisi"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Bağçalı ev"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Ofis binası"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Yapon poçt ofisi"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Avropa poçt ofisi"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Hospital"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Bank"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Bankomat"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Otel"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Sevgi oteli"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Qarışıq mallar dükanı"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Məktəb"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Univermaq"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Zavod"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Izakaya fənəri"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Yapon qalası"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Avropa qalası"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Siçovul"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Siçan"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Öküz"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Su camışı"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"İnək"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Leopard"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Dovşan"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Pişik"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Əjdaha"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Timsah"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Balina"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"İlbiz"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"İlan"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"At"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Qoç"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Keçi"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Qoyun"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Meymun"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Xoruz"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Cücə"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"İt"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Donuz"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Qaban"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Fil"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Osminoq"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"İlbiz qabığı"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Böcək"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Qarışqa"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Bal arısı"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Parabüzən"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Balıq"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Tropik balıq"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Şar balığı"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Tısbağa"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Yumurtadan çıxan cücə"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Balaca cücə"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Önə baxan balaca cücə"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Quş"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Pinqvin"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Koala"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Pudel"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Bir hörgüclü dəvə"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"İki hörgüclü dəvə"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Delfin"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Siçan sifəti"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"İnək sifəti"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Pələng sifəti"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Dovşan sifəti"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Pişik sifəti"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Əjdaha sifəti"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Fontan vuran balina"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"At sifəti"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Meymun sifəti"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"İt sifəti"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Donuz sifəti"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Qurbağa sifəti"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Xomyak sifəti"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Canavar sifəti"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Ayı sifəti"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Panda sifəti"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Donuz burnu"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Pəncə izləri"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Gözlər"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Qulaq"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Burun"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Ağız"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Dil"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Yuxarı işarə edən ağ rəngli əl indeksi"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Aşağı işarə edən ağ rəngli əl indeksi"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Sola işarə edən ağ rəngli əl indeksi"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Sağa işarə edən ağ rəngli əl indeksi"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Yumruq işarəsi"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Yellənən əl işarəsi"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Ok əl işarəsi"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Yuxarı baş barmaq işarəsi"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Aşağı baş barmaq işarəsi"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Çalan əllər işarəsi"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Açıq əllər işarəsi"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Tac"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Qadın papağı"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Eynək"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Qalstuk"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Qısaqol köynək"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Cins"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Don"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Kimono"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Bikini"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Qadın geyimi"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Pulqabı"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Çanta"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Kisə"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Kişi ayaqqabısı"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"İdmançı ayaqqabısı"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Dikdaban ayaqqabı"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Qadın sandalı"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Qadın çəkməsi"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Ayaq izi"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Büst silueti"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Büst siluetləri"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Oğlan"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Qız"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Kişi"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Qadın"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Ailə"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Qadın və kişi əl-ələ tutur"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"İki kişi əl-ələ tutur"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"İki qadın əl-ələ tutur"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Polis içşisi"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Dövşanqulaq qadın"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Duvaqlı gəlin"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Sarı saçlı adam"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Qua pi maolu kişi"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Çalmalı kişi"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Yaşlı kişi"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Yaşlı qadın"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Uşaq"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Fəhlə"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Şahzadə"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Yapon oqru"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Yapon qoblini"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Ruh"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Uşaq mələk"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Yad planetli"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Yad planetli qulyabani"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"İmp"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Kəllə"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Qəbul bölməsi adamı"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Keşikçi"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Rəqqas"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Dodaq boyası"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Dırnaq boyası"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Üz masajı"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Saç düzümü"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Bərbərxana"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Şpris"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Həb"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Öpüş izi"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Sevgi məktubu"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Zəng"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Qiymətli daş"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Öpüş"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Buket"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Ürəkli cütlük"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Toy"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Ürək döyüntüsü"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Sınmış qəlb"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"İki ürək"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Parlaq ürək"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Böyüyən ürək"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Oxlu ürək"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Göy ürək"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Yaşıl ürək"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Sarı ürək"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Bənövşəyi ürək"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Lentli ürək"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Fırlanan ürək"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Ürək bəzəyi"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Nöqtəli brilliant"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Elektrik lampası"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Hirs simvolu"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Bomba"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Yuxu işarəsi"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Partlayış"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Tər işarəsi"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Damcı"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Tire simvolu"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Kakaşka"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Biseps əzələ"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Başgicəllənmə"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Nitq balonu"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Fikir balonu"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Ağ gül"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Yüz xal simvolu"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Pul kisəsi"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Valyuta mübadiləsi"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Ağır dollar işarəsi"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Kredit kartı"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Yen işarəli əskinaz"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Dollar nişanlı əskinaz"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Avro işarəli əskinaz"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Funt işarəli əskinaz"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Qanadlı pul"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Qalxan diaqram və yen işarəsi"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Oturacaq"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Şəxsi kompüter"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Portfel"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Mini disk"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Disket"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Optik disk"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Fayl qovluğu"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Açıq fayl qovluğu"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Qatlanmış səhifə"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Səhifə"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Təqvim"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Qoparılan təqvim"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Kart indeksi"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Qalxan diaqram"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Düşən diaqram"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Panel diaqramı"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Pano"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Basmadüymə"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Yumru basmadüymə"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Skrepka"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Düz xətkeş"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Üçbücaq xətkeş"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Əlfəcin tabları"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Qovluq"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Bloknot"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Bəzəkli bloknot"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Qapalı kitab"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Açıq kitab"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Yaşıl kitab"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Göy kitab"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Narıncı kitab"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Kitablar"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Ad kartı"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Tomar"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Memo"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Telefon qəbuledici"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Peycer"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Faks cihazı"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Peyk antennası"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Dinamik səsyüksəldici"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Səsyüksəldici"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Göndəriləcək sənəd siyirməsi"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Gələn sənəd siyirməsi"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Paket"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"E-poçt simvolu"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Gələn zərf"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Üstündə aşağı ox olan zərf"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Enmiş bayraqlı qapalı poçt qutusu"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Qalxmış bayraqlı qapalı poçt qutusu"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Qalxmış bayraqlı açıq poçt qutusu"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Enmiş bayraqlı açıq poçt qutusu"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Poçt qutusu"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Poçt buynuzu"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Qəzet"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Mobil telefon"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Solunda sağa oxlu mobil telefon"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Vibrasiya rejimi"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Sönmüş mobil telefon"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Mobil telefon qadağası"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Şəbəkə dalğası"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Kamera"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Videokamera"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Televiziya"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Radio"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Videokasset"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Sağa əyilmiş oxlar"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Saat əqrəbi istiqamətində sağa və sola açıq çevrəli oxlar"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Çevrəyə alınmış saat əqrəbi istiqamətində sağa və sola açıq çevrəli oxlar"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Saat əqrəbi istiqamətində aşağı və yuxarı açıq çevrəli oxlar"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Saat əqrəbi istiqaməti əksinə aşağı və yuxarı açıq çevrəli oxlar"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Alçaq parlaqlıq simvolu"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Yüksək parlaqlıq simvolu"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Ləğv edilmə xətli dinamik"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Dinamik"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Bir xətli dinamik"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Üç xətli dinamik"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Batareya"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Elektrik ştepseli"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Sola əyilmiş lupa"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Sağa əyilmiş lupa"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Dolma qələmli kilid"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Açarlı bağlı kilid"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Açar"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Kilid"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Açıq kilid"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Zəng"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Ləğv edilmiş zəng"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Əlfəcin"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Link simvolu"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Radio düyməsi"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Üzərində sola ox olan back simvolu"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"Üzərində sola ox olan end simvolu"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"Üzərində sola sağa ox olan nida işarəli on simvolu"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Üzərində sağa ox olan soon simvolu"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Üzərində yuxarı ox olan top simvolu"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"On səkkiz yaşdan aşağı qadağası"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Klaviş qapağı on"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Böyük latın hərfləri üçün daxiletmə simvolu"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Kiçik latın hərfləri üçün daxiletmə simvolu"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Rəqəmlər üçün daxiletmə simvolu"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Simvollar üçün daxiletmə simvolu"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Latın hərfləri üçün daxiletmə simvolu"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Alov"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Cib fənəri"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Qaz açarı"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Çəkic"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Bolt və qayka"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Hoço"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Tapança"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Mikroskop"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Teleskop"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Büllur kürə"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Ortasında nöqtə olan altı guşəli ulduz"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Başlayan üçün yapon simvolu"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Üçdişli emblem"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Qara kvadrat düyməsi"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Ağ kvadrat düyməsi"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Böyük qırmızı çevrə"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Böyük göy çevrə"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Böyük narıncı brilliant"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Böyük göy brilliant"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Kiçik narıncı brilliant"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Kiçik göy brilliant"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Yuxarı göstərən qırmızı üçbucaq"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Aşağı göstərən qırmızı üçbucaq"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Yuxarı göstərən kiçik qırmızı üçbucaq"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Aşağı göstərən kiçik qırmızı üçbucaq"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Saat bir"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Saat iki"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Saat üç"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Saat dörd"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Saat beş"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Saat altı"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Saat yeddi"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Saat səkkiz"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Saat doqquz"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Saat on"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Saat on bir"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Saat on iki"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Saat ikinin yarısı"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Saat üçün yarısı"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Saat dördün yarısı"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Saat beşin yarısı"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Saat altının yarısı"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Saat yeddinin yarısı"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Saat səkkizin yarısı"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Saat doqquzun yarısı"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Saat onun yarısı"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Saat on birin yarısı"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Saat on ikinin yarısı"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Saat birin yarısı"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Fudzi dağı"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Tokio qülləsi"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Azadlıq heykəli"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Yaponiya silueti"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Moyai"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Gülümsəyən sima"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Gülümsəyən gözlərlə sima"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Sevinc göz yaşları ilə sima"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Açıq ağızla gülümsəyən sima"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Açıq ağızlı, gülən gözlərlə gülümsəyən sima"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Açıq ağızlı, soyuq tərli gülümsəyən sima"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Açıq ağızlı, qapalı gözlü gülümsəyən sima"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Nimbalı gülümsəyən sima"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Buynuzlu gülümsəyən sima"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Göz vuran sima"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Gülən gözlərlə gülümsəyən sima"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Dadlı yemək yeyən sima"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Azad sima"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Ürək formalı gülümsəyən sima"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Gün eynəkli gülümsəyən sima"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Oğruncasına gülümsəyən sima"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Neytral sima"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"İfadəsiz sima"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Bezmiş sima"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Soyuq tərli sima"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Fikirli sima"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Çaşqın sima"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Məəttəl sima"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Öpən sima"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Öpüş atan sima"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Gülən gözlərlə öpən sima"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Bağlı gözlərlə öpən sima"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Dil çıxaran sima"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Dil çıxaran və göz vuran sima"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Qapalı gözlərlə dil çıxaran sima"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Məyus sima"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Narahat sima"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Hirsli sima"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"İncimiş sima"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Ağlayan sima"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"İnadlı sima"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Triumf baxışlı sima"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Məyus, lakin azad sima"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Mısmırıqlı sima"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Ağrılı sima"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Qorxan sima"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Bezmiş sima"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Yuxulu sima"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Yorğun sima"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Qırışdırılmış sima"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Hönkür-hönkür ağlayan sima"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Açıq ağızlı sima"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Susmağa məcbur sima"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Açıq ağızlı soyuq tərli sima"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Qorxudan qışqıran sima"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Heyran sima"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Qızarmış sima"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Yatan sima"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Gicəllənən sima"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Ağızsız sima"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Tibbi maskalı sima"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Gülən gözlərlə oğrun gülümsəyən pişik siması"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Sevinc göz yaşları ilə pişik siması"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Açıq ağızlı gülümsəyən pişik siması"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Ürəkli gözlərlə gülümsəyən pişik siması"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"İkrahlı təbəssümlü pişik siması"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Bağlı gözlərlə öpən pişik siması"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"İncimiş pişik siması"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Ağlayan pişik siması"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Bezmiş pişik siması"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Qadağa jestl sima"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Ok jestli sima"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Baş əyən adam"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Gözünü tutmuş meymun"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Qulağını tutmuş meymun"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Ağzını tutmuş meymun"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Əlini qaldırmış xoşbəxt adam"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Mərasimdə əllərini qaldırmış adam"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Mısmırıqlı adam"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"İncimiş adam"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Əllərini birləşdirmiş adam"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Raket"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Helikopter"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Parovoz"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Dəmiryol maşını"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Sürətli qatar"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Gülləburun sürətli qatar"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Qatar"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metro"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Monorels"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Stansiya"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Tramvay"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Tramvay avtomobil"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Avtobus"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Gələn avtobus"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Trolleybus"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Avtobus dayanacağı"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Minibus"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulans"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Yanğınsöndürən maşın"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Polis maşını"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Gələn polis maşını"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Taksi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Gələn taksi"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Avtomobil"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Gələn avtomobil"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Rahatlıq avtomobili"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Yükdaşıyan maşın"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Avtoqatar"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Traktor"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Monorels"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Dağ dəmir yolu"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Asma dəmiryol"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Dağ kabel yolu"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Asma kanat yolu"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Gəmi"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Avarlı qayıq"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Sürətli qayıq"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Horizontal svetofor"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Vertikal işıqfor"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Tikinti nişanı"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Yanar mayaklı polis maşınları"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Poçtda üçbucaq bayraq"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Qapı"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Giriş qadağası siqnalı"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Siqaret çəkmək simvolu"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Siqaret qadağası simvolu"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Zibili zibil yeşiyinə atmaq simvolu"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Zibilləmək qadağası simvolu"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"İçməli su simvolu"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Texniki su simvolu"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Velosiped"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Velosiped qadağası"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Velosipedçi"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Dağ velosipedçisi"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Piyada"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Piyada qadağası"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Yol keçən uşaqlar"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Kişi simvolu"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Qadın simvolu"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Dincəlmə otağı"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Uşaq simvolu"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Tualet"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"Unitaz"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Duş"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Hamam"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Vanna"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Pasport nəzarəti"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Gömrük"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Baqaj iddiası"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Qalmış baqaj"</string> +</resources> diff --git a/java/res/values-az-rAZ/strings-talkback-descriptions.xml b/java/res/values-az-rAZ/strings-talkback-descriptions.xml index c5abc5cf5..8ba8a6e7d 100644 --- a/java/res/values-az-rAZ/strings-talkback-descriptions.xml +++ b/java/res/values-az-rAZ/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Mətn daxil edilməyib"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> sözünü <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> sözü ilə əvəzləyərək düzəldir"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> avto-korreksiyanı həyata keçirir"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"%d açar kodu"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Sürüşdürmə"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Sürüşdürmə aktivdir (deaktiv etmək üçün klikləyin)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Böyük hərf kilidi aktivdir (deaktiv etmək üçün klikləyin)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Daha çox simvol"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Sürüşdürmə"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simvollar"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Sürüşdürmə"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Sil"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simvollar"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Hərflər"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Əvvəlki"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Sürüşdürmə aktivdir"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Böyük hərf kilidi aktivdir"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Sürüşdürmə deaktivdir"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Simvol rejimi"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Daha çox simvol rejimi"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Hərf rejimi"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefon rejimi"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefon simvol rejimi"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Yerlər"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simvollar"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikonlar"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml index 5a40e9f14..bf3990d6e 100644 --- a/java/res/values-az-rAZ/strings.xml +++ b/java/res/values-az-rAZ/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Daxiletmə seçimləri"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Araşdırma Jurnalı Əmrləri"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakt adlarına baxın"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Orfoqrafik yoxlanış kontakt siyahınızdakı qeydlərdən istifadə edir"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrasiyalı klikləmə"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakt adları təklif edin"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Təklif və korreksiya üçün Kontaktlardakı adlardan istifadə edin"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Fərdiləşmiş təkliflər"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"İkili boşluq periodu"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Boşluqdakı iki klik boşluqdan sonra pauza daxil edir"</string> <string name="auto_cap" msgid="1719746674854628252">"Avtomatik böyük hərfləşmə"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Daxiletmə dilləri"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Yadda saxlamaq üçün yenidən toxunun"</string> <string name="has_dictionary" msgid="6071847973466625007">"Lüğət mövcuddur"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"İstifadəçi əks əlaqəsini aktiv et"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"İstifadə statistikası və xəta haqqında hesabatları avtomatik göndərməklə daxiletmə metodu redaktəsini təkmilləşdirməyə kömək edin."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatura teması"</string> <string name="subtype_en_GB" msgid="88170601942311355">"İngilis (BK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"İngilis (ABŞ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktiv et"</string> <string name="not_now" msgid="6172462888202790482">"İndi yox"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Eyni daxiletmə üslubu artıq mövcuddur: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Rahat işləmə rejimi"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavişi uzun müddət basmada gecikmə"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasiyalı klikləmə müddəti"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Səsli klikləmə səsi"</string> diff --git a/java/res/values-bg/strings-action-keys.xml b/java/res/values-bg/strings-action-keys.xml index 13374a29c..3d295bba4 100644 --- a/java/res/values-bg/strings-action-keys.xml +++ b/java/res/values-bg/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Назад"</string> <string name="label_done_key" msgid="7564866296502630852">"Готово"</string> <string name="label_send_key" msgid="482252074224462163">"Изпр."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Пауза"</string> <string name="label_wait_key" msgid="5891247853595466039">"Изчакв."</string> </resources> diff --git a/java/res/values-bg/strings-talkback-descriptions.xml b/java/res/values-bg/strings-talkback-descriptions.xml index c944c579f..edd74b52c 100644 --- a/java/res/values-bg/strings-talkback-descriptions.xml +++ b/java/res/values-bg/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Няма въведен текст"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"„<xliff:g id="KEY_NAME">%1$s</xliff:g>“ коригира „<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>“ на „<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>“"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"„<xliff:g id="KEY_NAME">%1$s</xliff:g>“ изпълнява автоматично коригиране"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Код на клавишa %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"„Shift“ е включен (докоснете за деактивиране)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"„Caps lock“ е включен (докоснете за деактивиране)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Още символи"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Символи"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Delete"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Символи"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Букви"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Назад"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"„Shift“ е активиран"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"„Caps Lock“ е активиран"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"„Shift“ е деактивиран"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Режим за символи"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Режим с още символи"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Режим за букви"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Режим за телефонни номера"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Режим за символи на телефона"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Места"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Символи"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Емотикони"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index 562e71b4f..96c3a9560 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"За проверка на правописа се ползват записи от списъка с контакти"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Предложения за контакти"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Персонализ. предложения"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Точка чрез двоен интервал"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Двукр. докосване на клав. за интервал вмъква точка, следвана от интервал"</string> <string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Езици за въвеждане"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Докоснете отново, за да запазите"</string> <string name="has_dictionary" msgid="6071847973466625007">"Има достъп до речник"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Активиране на отзивите от потребителите"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Помогнете за подобряването на този редактор за въвеждане чрез автоматично изпращане на статистически данни за употребата и сигнали за сривове"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема на клавиатурата"</string> <string name="subtype_en_GB" msgid="88170601942311355">"английски (Великобритания)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"английски (САЩ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Активиране"</string> <string name="not_now" msgid="6172462888202790482">"Не сега"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Същият стил на въвеждане вече съществува: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за изучаване на използваемостта"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Забавяне при продълж. натискане"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Продълж. на вибриране при натискане"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Сила на звука при натиск. на клавиш"</string> diff --git a/java/res/values-ca/strings-action-keys.xml b/java/res/values-ca/strings-action-keys.xml index 9dcf219cc..5dcf4a337 100644 --- a/java/res/values-ca/strings-action-keys.xml +++ b/java/res/values-ca/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Ant."</string> <string name="label_done_key" msgid="7564866296502630852">"Fet"</string> <string name="label_send_key" msgid="482252074224462163">"Envia"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Atura"</string> <string name="label_wait_key" msgid="5891247853595466039">"Esp."</string> </resources> diff --git a/java/res/values-ca/strings-talkback-descriptions.xml b/java/res/values-ca/strings-talkback-descriptions.xml index 389200b43..5bbf65571 100644 --- a/java/res/values-ca/strings-talkback-descriptions.xml +++ b/java/res/values-ca/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"No s\'ha introduït cap text."</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> per <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> executa la correcció automàtica."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Clau de codi %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Maj"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Maj activat (toca per desactivar)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Bloq Maj activat (toca per desactivar)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Més símbols"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Maj"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Símbols"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Maj"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Suprimeix"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Símbols"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Lletres"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Maj activat"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Bloq Maj activat"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Maj desactivat"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Mode de símbols"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Mode de més símbols"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Mode de lletres"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Mode de telèfon"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Mode de símbols de telèfon"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Llocs"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Símbols"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticones"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index e946ba78b..3c7e09237 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortogràfic utilitza entrades de la llista de contactes"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms de contactes"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de contactes per fer suggeriments i correccions"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Suggeriments personalitz."</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punt amb doble espai"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Picar dues vegades la barra d\'espai insereix punt i espai blanc"</string> <string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Idiomes d\'introducció"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Torna a tocar per desar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Diccionari disponible"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Activa els comentaris de l\'usuari"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ajuda a millorar aquest editor de mètode d\'introducció de text mitjançant l\'enviament d\'estadístiques d\'ús i d\'informes d\'error."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclat"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Anglès (Regne Unit)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Anglès (EUA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Activa"</string> <string name="not_now" msgid="6172462888202790482">"Ara no"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ja existeix aquest estil d\'entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'estudi d\'usabilitat"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retard en mantenir premut"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durada vibració en prémer"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volum del so en prémer tecles"</string> diff --git a/java/res/values-cs/strings-action-keys.xml b/java/res/values-cs/strings-action-keys.xml index e9545feca..0ff3506b5 100644 --- a/java/res/values-cs/strings-action-keys.xml +++ b/java/res/values-cs/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Před."</string> <string name="label_done_key" msgid="7564866296502630852">"Hot."</string> <string name="label_send_key" msgid="482252074224462163">"Odes."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauza"</string> <string name="label_wait_key" msgid="5891247853595466039">"Čekat"</string> </resources> diff --git a/java/res/values-cs/strings-talkback-descriptions.xml b/java/res/values-cs/strings-talkback-descriptions.xml index 7ba691c99..50cc984b9 100644 --- a/java/res/values-cs/strings-talkback-descriptions.xml +++ b/java/res/values-cs/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Není zadán žádný text"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Klávesou <xliff:g id="KEY_NAME">%1$s</xliff:g> opravíte <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Klávesou <xliff:g id="KEY_NAME">%1$s</xliff:g> provedete automatickou opravu"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kód klávesy %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Klávesa Shift je zapnutá (vypnete ji klepnutím)."</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Klávesa Caps Lock je zapnutá (vypnete ji klepnutím)."</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Další symboly"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboly"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Smazat"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboly"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Písmena"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Předchozí"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Klávesa Shift je aktivní"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Klávesa Caps Lock je aktivní"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Klávesa Shift je neaktivní"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Režim symbolů"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Režim dalších symbolů"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Režim písmen"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Režim telefonu"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Režim telefonních symbolů"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Místa"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboly"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikony"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index c093d8c50..98a9a13d6 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Navrhovat jména kontaktů"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalizované návrhy"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Tečka dvojitým mezerníkem"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvojím klepnutím na mezerník vložíte tečku následovanou mezerou."</string> <string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Vstupní jazyky"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Opětovným dotykem provedete uložení"</string> <string name="has_dictionary" msgid="6071847973466625007">"Slovník k dispozici"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktivovat zasílání statistik užívání a zpráv o selhání"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Pomozte nám vylepšit tento editor pro zadávání dat automatickým zasíláním statistik využití a zpráv o selhání do Googlu."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motiv klávesnice"</string> <string name="subtype_en_GB" msgid="88170601942311355">"angličtina (Velká Británie)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"angličtina (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Povolit"</string> <string name="not_now" msgid="6172462888202790482">"Teď ne"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Tento styl zadávání již existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim studie použitelnosti"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Prodleva dlouhého stisknutí"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Délka vibrace u stisku klávesy"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitost stisknutí klávesy"</string> diff --git a/java/res/values-da/strings-action-keys.xml b/java/res/values-da/strings-action-keys.xml index 757dc00d4..96021e5a9 100644 --- a/java/res/values-da/strings-action-keys.xml +++ b/java/res/values-da/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Forr."</string> <string name="label_done_key" msgid="7564866296502630852">"Udfør"</string> <string name="label_send_key" msgid="482252074224462163">"Send"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Vent"</string> </resources> diff --git a/java/res/values-da/strings-talkback-descriptions.xml b/java/res/values-da/strings-talkback-descriptions.xml index 2d613d6c7..b34650e8b 100644 --- a/java/res/values-da/strings-talkback-descriptions.xml +++ b/java/res/values-da/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Der er ingen indtastet tekst"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> retter <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> til <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> udfører automatisk stavekontrol"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Tastekode %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift er aktiveret (tryk for at deaktivere)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock er slået til (tryk for at deaktivere)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Flere symboler"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift-tast"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboler"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift-tast"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Slet"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboler"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Bogstaver"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Forrige"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift er aktiveret"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock er aktiveret"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift er deaktiveret"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symboltilstand"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Tilstaden Flere symboler"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Bogstavtilstand"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefontilstand"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefonsymboltilstand"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Steder"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboler"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Humørikoner"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 65025b7a8..d7a005be4 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Anvend kontaktnavne"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruger ord fra dine kontaktpersondata"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå navne på kontakter"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Tilpassede forslag"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"To mellemrum for punktum"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"To tryk på mellemrumstasten indsætter et punktum og et mellemrum"</string> <string name="auto_cap" msgid="1719746674854628252">"Skriv automatisk med stort"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Inputsprog"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tryk igen for at gemme"</string> <string name="has_dictionary" msgid="6071847973466625007">"Ordbog er tilgængelig"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktivér brugerfeedback"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Vær med til at forbedre dette redigeringsværktøj til indtastningsmetode ved automatisk at sende anvendelsesstatistikker og rapporter om nedbrud til Google."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannien)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktivér"</string> <string name="not_now" msgid="6172462888202790482">"Ikke nu"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Denne inputstil findes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tilstand for brugsstudie"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinket langt tastetryk"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationstid ved tastetryk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetryk"</string> diff --git a/java/res/values-de/strings-action-keys.xml b/java/res/values-de/strings-action-keys.xml index 95d3d7119..b55c2802f 100644 --- a/java/res/values-de/strings-action-keys.xml +++ b/java/res/values-de/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Zurück"</string> <string name="label_done_key" msgid="7564866296502630852">"Fertig"</string> <string name="label_send_key" msgid="482252074224462163">"Senden"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Warten"</string> </resources> diff --git a/java/res/values-de/strings-talkback-descriptions.xml b/java/res/values-de/strings-talkback-descriptions.xml index 9fef63208..3ae624370 100644 --- a/java/res/values-de/strings-talkback-descriptions.xml +++ b/java/res/values-de/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Kein Text eingegeben"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Mit <xliff:g id="KEY_NAME">%1$s</xliff:g> wird \"<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>\" in \"<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>\" geändert."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Mit <xliff:g id="KEY_NAME">%1$s</xliff:g> erfolgt eine Autokorrektur."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Tastencode %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Umschalttaste"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Umschalttaste aktiviert (zum Deaktivieren berühren)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Feststelltaste aktiviert (zum Deaktivieren berühren)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Weitere Symbole"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift-Taste"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbole"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift-Taste"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Löschen"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbole"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Buchstaben"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Zurück"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Umschalttaste aktiviert"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Feststelltaste aktiviert"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Umschalttaste deaktiviert"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbolmodus"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modus \"Weitere Symbole\""</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Buchstabenmodus"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefonmodus"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefon-Symbolmodus"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Orte"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbole"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticons"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index f2074b762..d5bfec2b4 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung kann Einträge aus meiner Kontaktliste verwenden"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Bei Tastendruck vibrieren"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalisierte Vorschläge"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punkt plus Leerzeichen"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Für Punkt plus Leerzeichen zweimal auf die Leertaste tippen"</string> <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschreibung"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Eingabesprachen"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Zum Speichern erneut berühren"</string> <string name="has_dictionary" msgid="6071847973466625007">"Wörterbuch verfügbar"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Nutzer-Feedback aktivieren"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Tragen Sie zur Verbesserung dieses Eingabemethodeneditors bei, indem Sie automatisch Nutzungsstatistiken und Absturzberichte senden."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturdesign"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Englisch (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Englisch (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktivieren"</string> <string name="not_now" msgid="6172462888202790482">"Später"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Der gleiche Eingabestil ist bereits vorhanden: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Studie zur Benutzerfreundlichkeit"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Verzögerung für langes Drücken"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationsdauer bei Tastendruck"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tonlautstärke bei Tastendruck"</string> diff --git a/java/res/values-el/strings-action-keys.xml b/java/res/values-el/strings-action-keys.xml index a4093e3dc..8b5e395f0 100644 --- a/java/res/values-el/strings-action-keys.xml +++ b/java/res/values-el/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Προηγούμενο"</string> <string name="label_done_key" msgid="7564866296502630852">"Τέλος"</string> <string name="label_send_key" msgid="482252074224462163">"Αποστολή"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Παύση"</string> <string name="label_wait_key" msgid="5891247853595466039">"Αναμονή"</string> </resources> diff --git a/java/res/values-el/strings-talkback-descriptions.xml b/java/res/values-el/strings-talkback-descriptions.xml index 7393e630e..bb9875780 100644 --- a/java/res/values-el/strings-talkback-descriptions.xml +++ b/java/res/values-el/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Δεν υπάρχει κείμενο"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> διορθώνει το <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> σε <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> εκτελεί αυτόματη διόρθωση"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Κωδικός πλήκτρου %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Το Shift είναι ενεργοποιημένο (πατήστε για απενεργοποίηση)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Το Caps lock είναι ενεργοποιημένο (πατήστε για απενεργοποίηση)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Περισσότερα σύμβολα"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Σύμβολα"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Διαγραφή"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Σύμβολα"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Γράμματα"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Προηγούμενο"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Το Shift είναι ενεργοποιημένο"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Το Caps lock είναι ενεργοποιημένο"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Το Shift είναι απενεργοποιημένο"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Λειτουργία συμβόλων"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Λειτουργία περισσότερων συμβόλων"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Λειτουργία γραμμάτων"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Λειτουργία τηλεφώνου"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Λειτουργία συμβόλων τηλεφώνου"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Μέρη"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Σύμβολα"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticon"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 7f0e3f3d7..cfe652b2a 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Ο ορθογρ. έλεγχος χρησιμοπ. καταχωρίσεις από τη λίστα επαφών σας"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Πρόταση ονομάτων επαφών"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Εξατομικευμένες προτάσεις"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Τελεία με διπλό πάτημα πλήκτρ.διαστ."</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Το διπλό πάτημα του πλήκτρ.διαστ. εισάγει μια τελεία και ένα κενό"</string> <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Γλώσσες εισόδου"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Αγγίξτε ξανά για αποθήκευση"</string> <string name="has_dictionary" msgid="6071847973466625007">"Λεξικό διαθέσιμο"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Ενεργοποίηση σχολίων χρηστών"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Βοηθήστε μας να βελτιώσουμε αυτό το πρόγραμμα επεξεργασίας μεθόδου εισόδου, στέλνοντας αυτόματα στατιστικά στοιχεία και αναφορές σφαλμάτων."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Θέμα πληκτρολογίου"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Αγγλικά (Η.Β.)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Αγγλικά (Η.Π.Α)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Ενεργοποίηση"</string> <string name="not_now" msgid="6172462888202790482">"Όχι τώρα"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Το ίδιο στυλ εισόδου υπάρχει ήδη: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Λειτουργία μελέτης χρηστικότητας"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Καθυστέρηση παρατεταμένου πατήματος πλήκτρου"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Διάρκεια δόνησης πατήμ. πλήκτ."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ένταση ήχου πατήματος πλήκτρου"</string> diff --git a/java/res/values-en-rGB/strings-action-keys.xml b/java/res/values-en-rGB/strings-action-keys.xml index 366cf3cdf..b8b02e149 100644 --- a/java/res/values-en-rGB/strings-action-keys.xml +++ b/java/res/values-en-rGB/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Prev"</string> <string name="label_done_key" msgid="7564866296502630852">"Finished"</string> <string name="label_send_key" msgid="482252074224462163">"Send"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Wait"</string> </resources> diff --git a/java/res/values-en-rGB/strings-talkback-descriptions.xml b/java/res/values-en-rGB/strings-talkback-descriptions.xml index c9393ee79..3956777ea 100644 --- a/java/res/values-en-rGB/strings-talkback-descriptions.xml +++ b/java/res/values-en-rGB/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"No text entered"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> performs auto-correction"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Key code %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift on (tap to disable)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock on (tap to disable)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"More symbols"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbols"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Delete"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbols"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letters"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Previous"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift enabled"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock enabled"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift disabled"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbols mode"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"More symbols mode"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Letters mode"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Phone mode"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Phone symbols mode"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Places"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbols"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticons"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 89e978925..0053a257c 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on keypress"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalised suggestions"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Double-space full stop"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Double tap on spacebar inserts a full stop followed by a space"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Input languages"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Touch again to save"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionary available"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Enable user feedback"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Help improve this input method editor by automatically sending usage statistics and crash reports"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Keyboard theme"</string> <string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Enable"</string> <string name="not_now" msgid="6172462888202790482">"Not now"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> diff --git a/java/res/values-en-rIN/strings-action-keys.xml b/java/res/values-en-rIN/strings-action-keys.xml index 366cf3cdf..b8b02e149 100644 --- a/java/res/values-en-rIN/strings-action-keys.xml +++ b/java/res/values-en-rIN/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Prev"</string> <string name="label_done_key" msgid="7564866296502630852">"Finished"</string> <string name="label_send_key" msgid="482252074224462163">"Send"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Wait"</string> </resources> diff --git a/java/res/values-en-rIN/strings-talkback-descriptions.xml b/java/res/values-en-rIN/strings-talkback-descriptions.xml index c9393ee79..3956777ea 100644 --- a/java/res/values-en-rIN/strings-talkback-descriptions.xml +++ b/java/res/values-en-rIN/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"No text entered"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> performs auto-correction"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Key code %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift on (tap to disable)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock on (tap to disable)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"More symbols"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbols"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Delete"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbols"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letters"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Previous"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift enabled"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock enabled"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift disabled"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbols mode"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"More symbols mode"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Letters mode"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Phone mode"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Phone symbols mode"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Places"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbols"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticons"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index 89e978925..0053a257c 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on keypress"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalised suggestions"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Double-space full stop"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Double tap on spacebar inserts a full stop followed by a space"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Input languages"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Touch again to save"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionary available"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Enable user feedback"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Help improve this input method editor by automatically sending usage statistics and crash reports"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Keyboard theme"</string> <string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Enable"</string> <string name="not_now" msgid="6172462888202790482">"Not now"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string> diff --git a/java/res/values-es-rUS/strings-action-keys.xml b/java/res/values-es-rUS/strings-action-keys.xml index d375617f4..5921ff43d 100644 --- a/java/res/values-es-rUS/strings-action-keys.xml +++ b/java/res/values-es-rUS/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Ant."</string> <string name="label_done_key" msgid="7564866296502630852">"Listo"</string> <string name="label_send_key" msgid="482252074224462163">"Env."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausa"</string> <string name="label_wait_key" msgid="5891247853595466039">"Esp."</string> </resources> diff --git a/java/res/values-es-rUS/strings-talkback-descriptions.xml b/java/res/values-es-rUS/strings-talkback-descriptions.xml index ab4979fc7..75fa14c2b 100644 --- a/java/res/values-es-rUS/strings-talkback-descriptions.xml +++ b/java/res/values-es-rUS/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"No se ingresó texto."</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> por <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrige automáticamente."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Clave de código %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Mayúsculas"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Mayúsculas activado (presiona para desactivarlo)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Bloqueo de mayúsculas activado (presiona para desactivarlo)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Más símbolos"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Mayúscula"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Símbolos"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Mayúscula"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Eliminar"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Símbolos"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letras"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Mayúsculas activado"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Bloqueo de mayúsculas activado"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Mayúsculas desactivado"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modo Símbolos"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modo de más símbolos"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modo Letras"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modo Teléfono"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modo Símbolos del teléfono"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Lugares"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Símbolos"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticones"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index d440c0ce6..ffbd54364 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortográfico usa entradas de tu lista de contactos."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nombres de contacto"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Sugerenc. personalizadas"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punto y doble espacio"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocar dos veces la barra espaciadora inserta un punto y espacio."</string> <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Vuelve a tocar para guardar."</string> <string name="has_dictionary" msgid="6071847973466625007">"Diccionario disponible"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Activar los comentarios del usuario"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Envíanos automáticamente las estadísticas de uso y los informes sobre fallos para ayudarnos a mejorar este editor de método de entrada de texto."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema del teclado"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Inglés (Reino Unido)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Inglés (EE.UU.)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Activar"</string> <string name="not_now" msgid="6172462888202790482">"Ahora no"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudio de usabilidad"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Demora de presión prolongada"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durac. vibrac. al presionar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Vol. sonido al presionar tecla"</string> diff --git a/java/res/values-es/strings-action-keys.xml b/java/res/values-es/strings-action-keys.xml index 2701146c8..4b3039d29 100644 --- a/java/res/values-es/strings-action-keys.xml +++ b/java/res/values-es/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Anterior"</string> <string name="label_done_key" msgid="7564866296502630852">"Listo"</string> <string name="label_send_key" msgid="482252074224462163">"Enviar"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausar"</string> <string name="label_wait_key" msgid="5891247853595466039">"Espera"</string> </resources> diff --git a/java/res/values-es/strings-talkback-descriptions.xml b/java/res/values-es/strings-talkback-descriptions.xml index 72bb6f104..11422bc56 100644 --- a/java/res/values-es/strings-talkback-descriptions.xml +++ b/java/res/values-es/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"No se ha introducido texto"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> a <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corregirá la palabra automáticamente"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Código del teclado: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Mayús"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Mayúsculas activadas (tocar para inhabilitar)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Bloqueo de mayúsculas activado (tocar para inhabilitar)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Más símbolos"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Mayús"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Símbolos"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Mayús"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Eliminar"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Símbolos"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letras"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Mayúsculas habilitadas"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Bloq Mayús habilitado"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Mayúsculas inhabilitadas"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modo de símbolos"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modo de más símbolos"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modo de letras"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modo de teléfono"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modo de símbolos de teléfono"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Sitios"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Símbolos"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticonos"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 3476ac5dd..1174614c5 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Añadir nombres de tu lista de contactos al corrector"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir contactos"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Sugerencias personalizadas"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punto y espacio"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Si tocas dos veces el espacio, se inserta un punto seguido de un espacio"</string> <string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Idiomas de introducción"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toca otra vez para guardar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Hay un diccionario disponible"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Habilitar comentarios de usuarios"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ayuda a mejorar este editor de método de introducción de texto enviando estadísticas de uso e informes de error."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema de teclado"</string> <string name="subtype_en_GB" msgid="88170601942311355">"inglés (Reino Unido)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"inglés (EE.UU.)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Habilitar"</string> <string name="not_now" msgid="6172462888202790482">"Ahora no"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo estudio de usabilidad"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Retraso de pulsación prolongada"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duración vibración al pulsar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volumen sonido al pulsar tecla"</string> diff --git a/java/res/values-et-rEE/strings-action-keys.xml b/java/res/values-et-rEE/strings-action-keys.xml index 64ba6d6d7..fa054dff0 100644 --- a/java/res/values-et-rEE/strings-action-keys.xml +++ b/java/res/values-et-rEE/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Eelm."</string> <string name="label_done_key" msgid="7564866296502630852">"Valmis"</string> <string name="label_send_key" msgid="482252074224462163">"Saada"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Peata"</string> <string name="label_wait_key" msgid="5891247853595466039">"Oota"</string> </resources> diff --git a/java/res/values-et-rEE/strings-talkback-descriptions.xml b/java/res/values-et-rEE/strings-talkback-descriptions.xml index 5a359c1a3..5bbe63b17 100644 --- a/java/res/values-et-rEE/strings-talkback-descriptions.xml +++ b/java/res/values-et-rEE/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Teksti ei ole sisestatud"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> parandab sõna <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> järgmiselt: <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> teeb automaatse paranduse"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Klahvi kood: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Tõstuklahv"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Tõstuklahv sees (puudutage keelamiseks)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Suurtähelukk on sees (puudutage keelamiseks)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Rohkem sümboleid"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Tõstuklahv"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Sümbolid"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Tõstuklahv"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Kustutamine"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Sümbolid"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Tähed"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Eelmine"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Tõstuklahv on lubatud"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Suurtähelukk on lubatud"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Tõstuklahv on keelatud"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Sümbolite režiim"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Režiim Rohkem sümboleid"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Tähtede režiim"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefonirežiim"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefoni sümbolite režiim"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Kohad"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Sümbolid"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikonid"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml index 6fbc4b40c..00d17bc60 100644 --- a/java/res/values-et-rEE/strings.xml +++ b/java/res/values-et-rEE/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Õigekirjakontroll kasutab teie kontaktisikute loendi sissekandeid"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreeri klahvivajutusel"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Soovita kontaktkirjeid"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Kasuta soovitusteks ja parandusteks nimesid kontaktiloendist"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Isikupärast. soovitused"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punkt tühikuklahviga"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tühikuklahvi kaks korda puudutades sisestatakse punkt ja tühik"</string> <string name="auto_cap" msgid="1719746674854628252">"Automaatne suurtähtede kasutamine"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Sisestuskeeled"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Salvestamiseks puudutage uuesti"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sõnastik saadaval"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Luba kasutaja tagasiside"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Aidake seda sisestusmeetodi redigeerijat parandada, saates automaatselt kasutusstatistikat ja krahhiaruandeid."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatuuri teema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Inglise (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Inglise (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Luba"</string> <string name="not_now" msgid="6172462888202790482">"Mitte kohe"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sama sisendstiil on juba olemas: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kasutatavuse uurimisrežiim"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pika klahvivajutuse viide"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Klahvivajutuse vibreerimise kestus"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Klahvivajutuse helitugevus"</string> diff --git a/java/res/values-fa/strings-action-keys.xml b/java/res/values-fa/strings-action-keys.xml index 78faa8c0b..ae393bbf6 100644 --- a/java/res/values-fa/strings-action-keys.xml +++ b/java/res/values-fa/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"قبلی"</string> <string name="label_done_key" msgid="7564866296502630852">"اتمام"</string> <string name="label_send_key" msgid="482252074224462163">"ارسال"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"مکث"</string> <string name="label_wait_key" msgid="5891247853595466039">"انتظار"</string> </resources> diff --git a/java/res/values-fa/strings-talkback-descriptions.xml b/java/res/values-fa/strings-talkback-descriptions.xml index 26e99cb7b..c70040fae 100644 --- a/java/res/values-fa/strings-talkback-descriptions.xml +++ b/java/res/values-fa/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"نوشتاری وارد نشده است"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g>، <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> را به <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> تصحیح میکند"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> تصحیح خودکار را انجام میدهد"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"کد کلید %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift روشن است (برای غیرفعال کردن ضربه بزنید)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock روشن است (برای غیرفعال کردن ضربه بزنید)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"نمادهای بیشتر"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"تبدیل"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"نمادها"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"تبدیل"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"حذف"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"نمادها"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"حروف"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"قبلی"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift فعال شد"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock فعال شد"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift غیرفعال شد"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"حالت نمادها"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"حالت نمادهای بیشتر"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"حالت حروف"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"حالت تلفن"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"حالت نمادهای تلفن"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"مکانها"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"نمادها"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"شکلکها"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index 04c6a5aca..8a6e5a2dc 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"گزینههای ورودی"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"فرمانهای گزارشگیری پژوهش"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلطگیر املا از ورودیهای لیست مخاطبین شما استفاده میکند"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نامهای مخاطب"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"پیشنهادات شخصی شده"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"نقطه با دو فاصله"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"با دوبار ضربه روی دکمه فاصله نقطه با یک فاصله بعد آن درج میشود"</string> <string name="auto_cap" msgid="1719746674854628252">"بزرگکردن خودکار حروف"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"زبانهای ورودی"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"برای ذخیره دوباره لمس کنید"</string> <string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"فعال کردن بازخورد کاربر"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"با ارسال خودکار آمار استفاده و گزارشهای خرابی، به بهبود این ویرایشگر روش ورودی کمک کنید"</string> <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحهکلید"</string> <string name="subtype_en_GB" msgid="88170601942311355">"انگلیسی (بریتانیا)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"انگلیسی (امریکا)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"فعال کردن"</string> <string name="not_now" msgid="6172462888202790482">"الآن نه"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"سبک ورودی مشابهی در حال حاضر وجود دارد: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"حالت بررسی قابلیت استفاده"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"تأخیر فشار طولانی کلید"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"طول مدت لرزش در اثر فشردن کلید"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"میزان صدای فشردن کلید"</string> diff --git a/java/res/values-fi/strings-action-keys.xml b/java/res/values-fi/strings-action-keys.xml index da7f11197..5472fc31a 100644 --- a/java/res/values-fi/strings-action-keys.xml +++ b/java/res/values-fi/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Edel."</string> <string name="label_done_key" msgid="7564866296502630852">"Valm."</string> <string name="label_send_key" msgid="482252074224462163">"Läh."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Tauko"</string> <string name="label_wait_key" msgid="5891247853595466039">"Odota"</string> </resources> diff --git a/java/res/values-fi/strings-emoji-descriptions.xml b/java/res/values-fi/strings-emoji-descriptions.xml new file mode 100644 index 000000000..ad08bbda2 --- /dev/null +++ b/java/res/values-fi/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Copyright-merkki"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Rekisteröidyn tavaramerkin merkki"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Kaksi huutomerkkiä"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Huutomerkki ja kysymysmerkki"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Tavaramerkin merkki"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Tietolähde"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Nuoli vasemmalle ja oikealle"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Nuoli ylös ja alas"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Nuoli vasemmalle ylös"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Nuoli oikealle ylös"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Nuoli oikealle alas"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Nuoli vasemmalle alas"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Koukkunuoli vasemmalle"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Koukkunuoli oikealle"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Katso"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Tiimalasi"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Musta oikealle osoittava kaksoiskolmio"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Musta vasemmalle osoittava kaksoiskolmio"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Musta ylös osoittava kaksoiskolmio"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Musta alas osoittava kaksoiskolmio"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Herätyskello"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Tiimalasi ja hiekka"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Ympyröity iso M-kirjain"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Pieni musta neliö"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Pieni valkoinen neliö"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Musta oikealle osoittava kolmio"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Musta vasemmalle osoittava kolmio"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Suuri valkoinen neliö"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Suuri musta neliö"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Valkoinen neliö"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Musta neliö"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Musta aurinko"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Pilvi"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Musta puhelin"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Valittu kyselyruutu"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Sateenvarjo ja sadepisarat"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Kuuma juoma"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Valkoinen käsi, ylös osoittava etusormi"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Valkoinen hymyilevä naama"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Oinas"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Härkä"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Kaksoset"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Rapu"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Leijona"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Neitsyt"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Vaaka"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Skorpioni"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Jousimies"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Kauris"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Vesimies"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Kalat"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Musta patasymboli"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Musta ristisymboli"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Musta herttasymboli"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Musta ruutusymboli"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Kuuma lähde"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Musta kierrätysmerkki"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Pyörätuolin symboli"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Ankkuri"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Varoitusmerkki"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Korkean jännitteen symboli"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Valkoinen ympyrä"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Musta ympyrä"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Jalkapallo"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Pesäpallo"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Lumiukko ilman lunta"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Aurinko pilven takana"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Käärmeenkantaja"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Kielletty ajosuunta"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Kirkko"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Suihkulähde"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Lippu reiässä"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Purjevene"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Teltta"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Polttoainepumppu"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Mustat sakset"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Valkoinen valintamerkki"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Lentokone"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Kirjekuori"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Nyrkki"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Pystyssä oleva kämmen"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Voitonmerkki"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Lyijykynä"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Musta kynänkärki"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Paksu valintamerkki"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Paksu kertomerkki"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Tuikkivat tähdet"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Kahdeksansakarainen tähti"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Kahdeksansakarainen musta tähti"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Lumihiutale"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Kipinä"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Ristimerkki"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Valkoinen ristimerkki mustalla taustalla"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Musta koristeellinen kysymysmerkki"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Valkoinen koristeellinen kysymysmerkki"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Valkoinen koristeellinen huutomerkki"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Paksu huutomerkki"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Väritetty sydän"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Paksu plusmerkki"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Paksu miinusmerkki"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Paksu jakomerkki"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Musta nuoli oikealle"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Silmukka"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Kaksoissilmukka"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Oikealle ja ylös osoittava nuoli"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Oikealle ja alas osoittava nuoli"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Musta nuoli vasemmalle"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Musta nuoli ylös"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Musta nuoli alas"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Suuri musta neliö"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Suuri valkoinen neliö"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Valkoinen tähti"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Paksu suuri ympyrä"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Aaltoviiva"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Vuorottelumerkki"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Ideogrammi onnittelu ympyrän sisällä"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Ideogrammi salaisuus ympyrän sisällä"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Mahjong-laatta, punainen lohikäärme"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Pelikortti, musta jokeri"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Veriryhmä A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Veriryhmä B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Veriryhmä O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Pysäköintialue"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Veriryhmä AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"CL neliön sisällä"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Sana COOL neliön sisällä"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Sana FREE neliön sisällä"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"ID neliön sisällä"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"Sana NEW neliön sisällä"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"NG neliön sisällä"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"OK neliön sisällä"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"SOS neliön sisällä"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"Sana UP ja huutomerkki neliön sisällä"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"VS neliön sisällä"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Katakana täällä neliön sisällä"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Katakana palvelu neliön sisällä"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Ideogrammi maksuton neliön sisällä"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Ideogrammi varattu paikka neliön sisällä"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Ideogrammi kielto neliön sisällä"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Ideogrammi vapaita paikkoja neliön sisällä"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Ideogrammi hyväksyntä neliön sisällä"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Ideogrammi täynnä neliön sisällä"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Ideogrammi maksettu neliön sisällä"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Ideogrammi kuukausittain neliön sisällä"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Ideogrammi tilaus neliön sisällä"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Ideogrammi alennus neliön sisällä"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Ideogrammi liiketoiminta neliön sisällä"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Ideogrammi etu neliön sisällä"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Ideogrammi hyväksy ympyrän sisällä"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Pyörremyrsky"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Sumu"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Suljettu sateenvarjo"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Yömaisema"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Auringonnousu vuorilla"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Auringonnousu"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Kaupunkimaisema hämärässä"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Auringonlasku rakennusten taakse"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Sateenkaari"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Silta yöllä"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Vaahtopää"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Tulivuori"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Linnunrata"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Maapallo, Eurooppa – Afrikka"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Maapallo, Amerikat"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Maapallo Aasia – Australia"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Maapallo ja pituuspiirit"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Uudenkuun symboli"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Kasvavan kuunsirpin symboli"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Kuun ensimmäisen neljänneksen symboli"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Kasvavan puolikuun symboli"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Täysikuun symboli"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Vähenevän kuun symboli"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Kuun viimeisen neljänneksen symboli"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Vähenevän kuunsirpin symboli"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Kuunsirppi"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Uusikuu, jolla kasvot"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Ensimmäisen neljänneksen kuu, jolla kasvot"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Viimeisen neljänneksen kuu, jolla kasvot"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Täysikuu, jolla kasvot"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Aurinko, jolla kasvot"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Loistava tähti"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Tähdenlento"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Kastanja"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Taimi"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Havupuu"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Lehtipuu"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Palmu"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Kaktus"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Tulppaani"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Kirsikankukka"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Ruusu"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Kiinanruusu"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Auringonkukka"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Kukka"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Maissintähkä"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Riisintähkä"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Yrtti"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Neliapila"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Vaahteranlehti"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Pudonnut lehti"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Tuulessa leijaileva lehti"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Sieni"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Tomaatti"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Munakoiso"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Viinirypäleet"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Meloni"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Vesimeloni"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Mandariini"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Sitruuna"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Banaani"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Ananas"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Punainen omena"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Vihreä omena"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Päärynä"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Persikka"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Kirsikat"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Mansikka"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Hampurilainen"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Pitsapala"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Lihainen luu"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Kanankoipi"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Riisikeksi"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Riisipallo"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Keitetty riisi"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Curry ja riisi"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Höyryävä kulho"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Spagetti"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Leipä"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Ranskalaiset perunat"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Jamssi"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Dango"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oden"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Sushi"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Friteeratut katkaravut"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Surimiviipale, jossa pyörrekuvio"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Pehmytjäätelö"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Murskattu jää"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Jäätelö"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Donitsi"</string> + <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_1F36E" msgid="4630541402785165902">"Vanukas"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hunajapurkki"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakkuviipale"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Bento-lounaslaatikko"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Ruokapata"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Ruoanlaitto"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Haarukka ja veitsi"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Teekuppi ilman korvaa"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Sake-pullo ja kuppi"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Viinilasi"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Cocktail-lasi"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Trooppinen drinkki"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Oluttuoppi"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Kippistävät olutmukit"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Tuttipullo"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Rusetti"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Lahjapaketti"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Syntymäpäiväkakku"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Kurpitsalyhty"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Joulukuusi"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Joulupukki"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Ilotulitus"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Tähtisadetikku"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Ilmapallo"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Konfettipommi"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Konfettipallo"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Tanabata-puu"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Liput ristikkäin"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Japanilainen mäntykoriste"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Japanilaiset nuket"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Kalanmuotoiset tuulipussit"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Tuulikello"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Kuujuhla"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Koululaukku"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Valmistujaislakki"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Karusellihevonen"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Maailmanpyörä"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Vuoristorata"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Onki ja kala"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Mikrofoni"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Elokuvakamera"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Elokuvateatteri"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Kuulokkeet"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Taiteilijan paletti"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Silinterihattu"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Sirkusteltta"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Lippu"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Klaffi"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Näyttämötaide"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Videopeli"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Napakymppi"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Peliautomaatti"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Biljardi"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Arpakuutio"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Keilailu"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Hanafuda-pelikortit"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Nuotti"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Nuotteja"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Saksofoni"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Kitara"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Koskettimet"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Trumpetti"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Viulu"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Nuottiviivasto"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Juoksupaita"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Tennismaila ja pallo"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Suksi ja mono"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Koripallo ja kori"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Ruutulippu"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Lumilautailija"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Juoksija"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Lainelautailija"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Palkintopokaali"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Laukkakilpailut"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Amerikkalainen jalkapallo"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Rugby-pallo"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Uimari"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Talo"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Talo ja puutarha"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Toimistorakennus"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Japanilainen postitoimisto"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Eurooppalainen postitoimisto"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Sairaala"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Pankki"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Pankkiautomaatti"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Hotelli"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Japanilainen love-hotelli"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Päivittäistavarakauppa"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Koulu"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Tavaratalo"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Tehdas"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Izakaya-ravintolan lyhty"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Japanilainen linna"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Eurooppalainen linna"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Rotta"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Hiiri"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Härkä"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Vesipuhveli"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Lehmä"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Leopardi"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Jänis"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Kissa"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Lohikäärme"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Krokotiili"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Valas"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Etana"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Käärme"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Hevonen"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Pässi"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Vuohi"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Lammas"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Apina"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Kukko"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Kana"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Koira"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Sika"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Villisika"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Norsu"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Mustekala"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Simpukankuori"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Hyönteinen"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Muurahainen"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Mehiläinen"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Leppäkerttu"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Kala"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Trooppinen kala"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Pallokala"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Kilpikonna"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Kuoriutuva kananpoika"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Kananpoika"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Eteenpäin katsova kananpoika"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Lintu"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Pingviini"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Koala"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Villakoira"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Yksikyttyräinen kameli"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Kaksikyttyräinen kameli"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Delfiini"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Hiiren pää"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Lehmän pää"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Tiikerin pää"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Jäniksen pää"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Kissan pää"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Lohikäärmeen pää"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Valas ja vesisuihku"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Hevosen pää"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Apinan pää"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Koiran pää"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Sian pää"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Sammakon pää"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Hamsterin pää"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Suden pää"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Karhun pää"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Pandan pää"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Sian kärsä"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Tassunjäljet"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Silmät"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Korva"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Nenä"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Suu"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Kieli"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Valkoinen käsi, ylös osoittava etusormi"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Valkoinen käsi, alas osoittava etusormi"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Valkoinen käsi, vasemmalle osoittava etusormi"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Valkoinen käsi, oikealle osoittava etusormi"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Käsi nyrkissä"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Vilkuttava käsi"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"OK-merkki"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Peukalo pystyssä"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Peukalo alaspäin"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Taputtavat kädet"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Avoimet kämmenet"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Kruunu"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Naisen hattu"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Silmälasit"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Kravatti"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"T-paita"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Farkut"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Leninki"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Kimono"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Bikinit"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Naisen paita"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Kukkaro"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Käsilaukku"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Pussukka"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Miesten kenkä"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Urheilujalkine"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Korkokenkä"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Naisen sandaali"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Naisen saappaat"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Jalanjäljet"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Rintakuvan ääriviivat"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Rintakuvien ääriviivat"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Poika"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Tyttö"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Mies"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Nainen"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Perhe"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Mies ja nainen käsi kädessä"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Kaksi miestä käsi kädessä"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Kaksi naista käsi kädessä"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Poliisi"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Nainen, jolla on pupun korvat"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Morsian hunnussa"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Vaaleahiuksinen henkilö"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Mies, jolla aasialainen päähine"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Mies, jolla turbaani"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Vanha mies"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Vanha nainen"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Vauva"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Rakennustyöläinen"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Prinsessa"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Japanilainen hirviö"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Japanilainen menninkäinen"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Kummitus"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Enkelivauva"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Ulkoavaruuden olento"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Avaruusolio"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Pikkupiru"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Kallo"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Asiakaspalvelija"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Vartija"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Tanssija"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Huulipuna"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Kynsilakka"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Kasvohieronta"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Hiusten leikkaus"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Parturin pylväs"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Ruisku"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Pilleri"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Huulet"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Rakkauskirje"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Sormus"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Jalokivi"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Suudelma"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Kukkakimppu"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Pariskunta ja sydän"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Häät"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Sykkivä sydän"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Särkynyt sydän"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Kaksi sydäntä"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Säihkyvä sydän"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Kasvava sydän"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Sydän ja nuoli"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Sininen sydän"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Vihreä sydän"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Keltainen sydän"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Violetti sydän"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Sydän ja rusetti"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Pyörivät sydämet"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Sydänkoriste"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Vinoneliö, jossa on sisällä piste"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Hehkulamppu"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Vihan symboli"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Pommi"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Unen symboli"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Törmäyksen symboli"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Hikipisarat"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Pisara"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Liikkeen symboli"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Kakkakasa"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Hauis"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Huimauksen symboli"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Puhekupla"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Ajatuskupla"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Valkoinen kukka"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Sadan pisteen symboli"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Rahasäkki"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Valuutanvaihto"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Paksu dollarin symboli"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Luottokortti"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Seteli, jossa jenin symboli"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Seteli, jossa dollarin symboli"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Seteli, jossa euron symboli"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Seteli, jossa punnan symboli"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Setelinippu, jolla siivet"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Noususuuntainen kaavio, jossa jenin symboli"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Paikka"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Tietokone"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Salkku"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Minilevy"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Levyke"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Optinen levy"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD-levy"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Tiedostokansio"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Avaa tiedostokansio"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Alhaalta käpristynyt sivu"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Sivu edestä"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Kalenteri"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Kalenteri, irtirevittävät sivut"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Kortihakemisto"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Noususuuntainen kaavio"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Laskusuuntainen kaavio"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Pylväskaavio"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Leikepöytä"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Nasta"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Pyöreäpäinen nasta"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Paperiliitin"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Suora viivain"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Kolmioviivain"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Kirjanmerkit"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Kierreselkäinen vihko"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Muistikirja"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Muistikirja, jossa koristekansi"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Suljettu kirja"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Avoin kirja"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Vihreä kirja"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Sininen kirja"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Oranssi kirja"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Kirjat"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Nimilappu"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Rulla"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Muistio"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Puhelimen kuuloke"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Hakulaite"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Faksi"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Satelliittiantenni"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Julkisen tilan kovaääninen"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Megafoni"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Lähtevien lokero"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Saapuvien lokero"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Paketti"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"Sähköpostin symboli"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Saapuva kirjekuori"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Kirjekuori alaspäin osoittavan nuolen alla"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Suljettu postilaatikko lippu alhaalla"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Suljettu postilaatikko lippu ylhäällä"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Avattu postilaatikko lippu ylhäällä"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Avattu postilaatikko lippu alhaalla"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Postilaatikko"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Postitorvi"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Sanomalehti"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Matkapuhelin"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Matkapuhelin, jonka vasemmalla puolella on sitä osoittava nuoli"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Värinätila"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Matkapuhelin pois käytöstä"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Ei matkapuhelimia"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Antennisymboli ja palkit"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Kamera"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Videokamera"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Televisio"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Radio"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Videokasetti"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Kaksi ristikkäistä oikealle osoittavaa nuolta"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Ympyrä, jossa myötäpäivään oikealle ja vasemmalle osoittavat nuolet"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Ympyrä, jossa myötäpäivään oikealle ja vasemmalle osoittavat nuolet ja jonka päällä ympyröity numero yksi"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Ympyrä, jossa myötäpäivään ylös ja alas osoittavat nuolet"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Ympyrä, jossa vastapäivään ylös ja alas osoittavat nuolet"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Alhaisen kirkkauden symboli"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Suuren kirkkauden symboli"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Yliviivattu kaiutin"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Kaiutin"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Kaiutin ja yksi ääniaalto"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Kaiutin ja kolme ääniaaltoa"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Akku"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Pistoke"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Vasemmalle osoittava suurennuslasi"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Oikealle osoittava suurennuslasi"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Lukko ja mustekynä"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Suljettu lukko ja avain"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Avain"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Lukko"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Avoin lukko"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Soittokello"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Yliviivattu soittokello"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Kirjanmerkki"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Linkin symboli"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Valintanappi"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Sana BACK, jonka yläpuolella vasemmalle osoittava nuoli"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"Sana END, jonka yläpuolella vasemmalle osoittava nuoli"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"Sana ON ja huutomerkki, yläpuolella vasemmalle ja oikealle osoittava nuoli"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Sana SOON, jonka yläpuolella oikealle osoittava nuoli"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Sana TOP, jonka yläpuolella ylös osoittava nuoli"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Ikäraja 18 vuotta"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Näppäin 10"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Latinalaisten isojen kirjainten symboli"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Latinalaisten pienten kirjainten symboli"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Numeroiden symboli"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Symbolien symboli"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Latinalaisten kirjainten symboli"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Tuli"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Taskulamppu"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Jakoavain"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Vasara"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Mutteri ja pultti"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Hochoveitsi"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Pistooli"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Mikroskooppi"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Kaukoputki"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Kristallipallo"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Kuusisakarainen tähti, jossa keskellä piste"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Japanilainen aloittelijan merkki"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Trident-symboli"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Musta neliönmuotoinen painike"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Valkoinen neliönmuotoinen painike"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Suuri punainen ympyrä"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Suuri sininen ympyrä"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Suuri oranssi vinoneliö"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Suuri sininen vinoneliö"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Pieni oranssi vinoneliö"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Pieni sininen vinoneliö"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Ylöspäin osoittava punainen kolmio"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Alaspäin osoittava punainen kolmio"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Ylöspäin osoittava pieni punainen kolmio"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Alaspäin osoittava pieni punainen kolmio"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Kellotaulu, kello yksi"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Kellotaulu, kello kaksi"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Kellotaulu, kello kolme"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Kellotaulu, kello neljä"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Kellotaulu, kello viisi"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Kellotaulu, kello kuusi"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Kellotaulu, kello seitsemän"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Kellotaulu, kello kahdeksan"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Kellotaulu, kello yhdeksän"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Kellotaulu, kello kymmenen"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Kellotaulu, kello yksitoista"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Kellotaulu, kello kaksitoista"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Kellotaulu, kello puoli kaksi"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Kellotaulu, kello puoli kolme"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Kellotaulu, kello puoli neljä"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Kellotaulu, kello puoli viisi"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Kellotaulu, kello puoli kuusi"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Kellotaulu, kello puoli seitsemän"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Kellotaulu, kello puoli kahdeksan"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Kellotaulu, kello puoli yhdeksän"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Kellotaulu, kello puoli kymmenen"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Kellotaulu, kello puoli yksitoista"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Kellotaulu, kello puoli kaksitoista"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Kellotaulu, kello puoli yksi"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Fuji-vuori"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Tokio-torni"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Vapaudenpatsas"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Japanin kartta"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Moai-kivipatsas"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Virnistävä naama"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Virnistävä naama silmät sirrillään"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Naama ja ilon kyyneleet"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Hymyilevä naama, suu auki"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Hymyilevä naama, suu auki ja silmät sirrillään"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Hymyilevä naama, suu auki ja kylmä hiki"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Hymyilevä naama, suu auki ja silmät kiinni"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Hymyilevä naama ja sädekehä"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Hymyilevä naama ja sarvet"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Silmää iskevä naama"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Hymyilevä naama, silmät sirrillään"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Naama ja huulia lipova kieli"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Helpottunut naama"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Hymyilevä naama ja sydämenmuotoiset silmät"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Hymyilevä naama ja aurinkolasit"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Virnistävä naama"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Naamalla perusilme"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Ilmeetön naama"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Kyllästynyt naama"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Naama ja kylmä hiki"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Mietteliäs naama"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Ymmällä oleva naama"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Hämmentynyt naama"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Naama ja pusuhuulet"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Lentosuukko ja silmänisku"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Naama ja pusuhuulet, silmät sirrillään"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Naama ja pusuhuulet, silmät kiinni"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Kieltä näyttävä naama"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Silmää iskevä ja kieltä näyttävä naama"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Kieltä näyttävä naama, silmät kiinni"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Pettynyt naama"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Huolestunut naama"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Vihainen naama"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Mököttävä naama"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Itkevä naama"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Sinnikäs naama"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Voitokas naama"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Pettynyt mutta huojentunut naama"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Surullinen naama, suu auki"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Tuskainen naama"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Pelokas naama"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Uupunut naama"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Uninen naama"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Väsynyt naama"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Irvistävä naama"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Rajusti itkevä naama"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Naama, suu auki"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Pelokas naama"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Naama, suu auki ja kylmä hiki"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Pelosta kirkuva naama"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Hämmästynyt naama"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Punastunut naama"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Nukkuva naama"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Naama taju kankaalla"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Naama ilman suuta"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Naama ja suumaski"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Virnistävä kissan naama, silmät sirrillään"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Kissan naama ja ilon kyyneleet"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Hymyilevä kissan naama, suu auki"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Hymyilevä kissan naama, sydämen muotoiset silmät"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Kissan naama, kuivakka hymy"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Kissan naama, pusuhuulet ja silmät kiinni"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Mököttävä kissan naama"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Itkevä kissan naama"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Uupunut kissan naama"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Naama ja epäonnistumisen ele"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Naama, OK-ele"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Syvään kumartava henkilö"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Silmänsä peittänyt apina"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Korvansa peittänyt apina"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Suunsa peittänyt apina"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Iloinen henkilö yksi käsi pystyssä"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Juhliva henkilö molemmat kädet pystyssä"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Henkilö otsa kurtussa"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Henkilö ja mököttävä ilme"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Henkilö, jolla kämmenet yhdessä"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Raketti"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Helikopteri"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Höyryveturi"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Junanvaunu"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Pikajuna"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Luotijuna"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Juna"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metro"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Kevyt raideliikenne"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Asema"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Raitiovaunu"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Raitiovaunun vaunu"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Bussi"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Bussi edestä"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Johdinauto"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Bussipysäkki"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Pikkubussi"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulanssi"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Paloauto"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Poliisiauto"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Poliisiauto edestä"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Taksi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Taksi edestä"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Auto"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Auto edestä"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Maastoauto"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Kuorma-auto"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Kuorma-auto"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Traktori"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Yksiraiteinen rautatie"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Vuoristojuna"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Riippuva rautatie"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Köysirata"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Köysirata"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Laiva"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Soutuvene"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Pikavene"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Vaakasuuntaiset liikennevalot"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Pystysuuntaiset liikennevalot"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Rakennustyömaan merkki"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Poliisiauton hälytysvalo"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Kolmiolippu"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Ovi"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Kulku kielletty -merkki"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Tupakointisymboli"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Tupakointi kielletty"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Roskat roskakoriin -symboli"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Älä roskaa -symboli"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Juomaveden symboli"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Juomakelvottoman veden symboli"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Polkupyörä"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Pyöräily kielletty"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Pyöräilijä"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Maastopyöräilijä"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Jalankulkija"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Jalankulku kieletty"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Lapsia-varoitusmerkki"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Miehen symboli"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Naisen symboli"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"WC"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Vauvan symboli"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"WC"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"WC"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Suihku"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Kylpy"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Kylpyamme"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Passintarkastus"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Tulli"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Matkatavaroiden nouto"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Matkatavaroiden säilytys"</string> +</resources> diff --git a/java/res/values-fi/strings-talkback-descriptions.xml b/java/res/values-fi/strings-talkback-descriptions.xml index 2d0d7a782..e2e66e3fe 100644 --- a/java/res/values-fi/strings-talkback-descriptions.xml +++ b/java/res/values-fi/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Ei kirjoitettua tekstiä"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> korjaa sanan <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> sanaksi <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> suorittaa automaattisen korjauksen"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Näppäimen koodi %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Vaihto"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Vaihto päällä (poista käytöstä napauttamalla)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock päällä (poista käytöstä napauttamalla)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Lisää symboleita"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Vaihto"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbolit"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Vaihto"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Delete"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbolit"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Kirjaimet"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Edellinen"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Vaihto päällä"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock päällä"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Vaihto pois päältä"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbolit-tila"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Lisää symboleita -tila"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Näppäimistötila"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Puhelintila"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Puhelinsymbolit-tila"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Paikat"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbolit"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Hymiöt"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index 756684ae0..de9866acb 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae yht.tietojen nimiä"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Oikeinkirjoituksen tarkistus käyttää yhteystietojasi."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Ehdota yht.tietojen nimiä"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Räätälöidyt ehdotukset"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Kaksoisvälilyönti = piste"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Välilyönnin kaksoisnapautus lisää tekstiin pisteen ja välilyönnin"</string> <string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Syöttökielet"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tallenna koskettamalla uudelleen"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sanakirja saatavilla"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Ota käyttäjäpalaute käyttöön"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Auta parantamaan tätä syöttötavan muokkausohjelmaa lähettämällä automaattisesti käyttötietoja ja kaatumisraportteja."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Näppäimistöteema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"englanti (Iso-Britannia)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"englanti (Yhdysvallat)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Ota käyttöön"</string> <string name="not_now" msgid="6172462888202790482">"Ei nyt"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Syöttötyyli on jo olemassa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Käytettävyystutkimustila"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Pitkän painalluksen viive"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Painalluksen värinän kesto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Näppäinpainalluksen äänenvoim."</string> diff --git a/java/res/values-fr-rCA/strings-action-keys.xml b/java/res/values-fr-rCA/strings-action-keys.xml index bb5568e79..b35dd61bd 100644 --- a/java/res/values-fr-rCA/strings-action-keys.xml +++ b/java/res/values-fr-rCA/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Préc."</string> <string name="label_done_key" msgid="7564866296502630852">"Term."</string> <string name="label_send_key" msgid="482252074224462163">"Env."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Att."</string> </resources> diff --git a/java/res/values-fr-rCA/strings-talkback-descriptions.xml b/java/res/values-fr-rCA/strings-talkback-descriptions.xml index b632edca3..2a0bec474 100644 --- a/java/res/values-fr-rCA/strings-talkback-descriptions.xml +++ b/java/res/values-fr-rCA/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Aucun texte entré"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"La touche <xliff:g id="KEY_NAME">%1$s</xliff:g> permet de remplacer <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> par <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"La touche <xliff:g id="KEY_NAME">%1$s</xliff:g> permet d\'effectuer une correction automatique"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Code touche %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Majuscule"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Touche Majuscule activée (toucher pour désactiver)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Verrouillage des majuscules activé (toucher pour désactiver)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Autres symboles"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Majuscule"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboles"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Majuscule"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Supprimer"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboles"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Lettres"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Précédente"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Touche Maj activée"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Verrouillage des majuscules activé"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Touche Majuscule désactivée"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Mode Symboles"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Mode Autres symboles"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Mode Lettres"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Mode Téléphone"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Mode Symboles du téléphone"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Lieux"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboles"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Émoticônes"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 5b1308166..357210f0c 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Suggestions personnalisées"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Point et espace"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et une espace"</string> <string name="auto_cap" msgid="1719746674854628252">"Majuscules automatiques"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Langues de saisie"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Appuyer de nouveau pour enregistrer"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Autoriser les commentaires des utilisateurs"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Contribuer à l\'amélioration de cet éditeur du mode de saisie grâce à l\'envoi automatique de statistiques d\'utilisation et de rapports d\'erreur"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (britannique)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Activer"</string> <string name="not_now" msgid="6172462888202790482">"Pas maintenant"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume pression de touche"</string> diff --git a/java/res/values-fr/donottranslate-config-spacing-and-punctuations.xml b/java/res/values-fr/donottranslate-config-spacing-and-punctuations.xml index d72f72b92..5a4914245 100644 --- a/java/res/values-fr/donottranslate-config-spacing-and-punctuations.xml +++ b/java/res/values-fr/donottranslate-config-spacing-and-punctuations.xml @@ -22,6 +22,8 @@ <string name="symbols_preceded_by_space">([{&;:!?</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> <string name="symbols_followed_by_space">.,;:!?)]}&</string> + <!-- Symbols that behave like a single punctuation when typed next to each other --> + <string name="symbols_clustering_together">!?</string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> <string name="symbols_word_separators">"	 
 "()[]{}*&<>+=|.,;:!?/_\"</string> diff --git a/java/res/values-fr/strings-action-keys.xml b/java/res/values-fr/strings-action-keys.xml index 1c36552d4..e69351c07 100644 --- a/java/res/values-fr/strings-action-keys.xml +++ b/java/res/values-fr/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Préc."</string> <string name="label_done_key" msgid="7564866296502630852">"OK"</string> <string name="label_send_key" msgid="482252074224462163">"Envoyer"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Attente"</string> </resources> diff --git a/java/res/values-fr/strings-talkback-descriptions.xml b/java/res/values-fr/strings-talkback-descriptions.xml index efa140b49..a79fd2107 100644 --- a/java/res/values-fr/strings-talkback-descriptions.xml +++ b/java/res/values-fr/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Aucun texte saisi"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"La touche <xliff:g id="KEY_NAME">%1$s</xliff:g> permet de remplacer \"<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>\" par \"<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>\"."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"La touche <xliff:g id="KEY_NAME">%1$s</xliff:g> permet d\'effectuer une correction automatique."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Code touche %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Maj"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Touche Maj activée (appuyer pour désactiver)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Verrouillage des majuscules activé (appuyer pour désactiver)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Autres symboles"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Maj"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboles"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Maj"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Supprimer"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboles"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Lettres"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Précédent"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"La touche Maj a bien été activée."</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Le verrouillage des majuscules a bien été activé."</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"La touche Maj a bien été désactivée."</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Mode Symboles"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Mode Autres symboles"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Mode Lettres"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Mode Téléphone"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Mode Symboles du téléphone"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Lieux"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboles"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Émoticônes"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index 1bb856783..ea7eed688 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Suggestions personnalisées"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Point et espace"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et un espace."</string> <string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Langues de saisie"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Appuyer de nouveau pour enregistrer"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Autoriser les commentaires des utilisateurs"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Contribuer à l\'amélioration de cet éditeur du mode de saisie grâce à l\'envoi automatique de statistiques d\'utilisation et de rapports d\'erreur"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (Royaume-Uni)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Activer"</string> <string name="not_now" msgid="6172462888202790482">"Pas maintenant"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume son pression de touche"</string> diff --git a/java/res/values-hi/strings-action-keys.xml b/java/res/values-hi/strings-action-keys.xml index 92cb194ea..a90434329 100644 --- a/java/res/values-hi/strings-action-keys.xml +++ b/java/res/values-hi/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"पिछला"</string> <string name="label_done_key" msgid="7564866296502630852">"पूर्ण"</string> <string name="label_send_key" msgid="482252074224462163">"भेजें"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"रोकें"</string> <string name="label_wait_key" msgid="5891247853595466039">"प्रतीक्षा करें"</string> </resources> diff --git a/java/res/values-hi/strings-talkback-descriptions.xml b/java/res/values-hi/strings-talkback-descriptions.xml index df9511905..78e4c8d6e 100644 --- a/java/res/values-hi/strings-talkback-descriptions.xml +++ b/java/res/values-hi/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"कोई पाठ नहीं डाला गया"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> को सुधार कर <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> करता है"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> स्वत: सुधार करता है"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"कुंजी कोड %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"शिफ़्ट"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"शिफ़्ट चालू (अक्षम करने के लिए टैप करें)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"कैप्स लॉक चालू (अक्षम करने के लिए टैप करें)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"और प्रतीक"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"शिफ़्ट"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"प्रतीक"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"शिफ़्ट"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"डिलीट"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"प्रतीक"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"अक्षर"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"पिछला"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"शिफ़्ट सक्षम किया गया"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"कैप्स लॉक सक्षम किया गया"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"शिफ़्ट अक्षम किया गया"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"प्रतीक मोड"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"और प्रतीक मोड"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"अक्षर मोड"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"फ़ोन मोड"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"फ़ोन प्रतीक मोड"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"स्थान"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"प्रतीक"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"इमोटिकॉन्स"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index 3129d86cc..26d9595ef 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्प"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"वर्तनी परीक्षक आपकी संपर्क सूची की प्रविष्टियों का उपयोग करता है"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"कुंजी दबाने पर कंपन करता है"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"संपर्क नाम सुझाएं"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव और सुधार के लिए संपर्क से नामों का उपयोग करें"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"वैयक्तिकृत सुझाव"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"दोहरे स्पेस वाला पीरियड"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबार पर डबल टैप करने से पीरियड शामिल हो जाता है जिसके बाद एक रिक्ति होती है"</string> <string name="auto_cap" msgid="1719746674854628252">"स्वत: अक्षर बड़े करना"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाएं"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"सहेजने के लिए पुन: स्पर्श करें"</string> <string name="has_dictionary" msgid="6071847973466625007">"शब्दकोश उपलब्ध है"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"उपयोगकर्ता फ़ीडबैक सक्षम करें"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"उपयोग के आंकड़े और क्रैश रिपोर्ट अपने आप भेजकर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें."</string> <string name="keyboard_layout" msgid="8451164783510487501">"कीबोर्ड थीम"</string> <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेज़ी (यूके)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेज़ी (यूएस)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"सक्षम करें"</string> <string name="not_now" msgid="6172462888202790482">"अभी नहीं"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ऐसी ही इनपुट शैली पहले से मौजूद है: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"उपयोगिता अध्ययन मोड"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुंजी को देर तक दबाने का विलंब"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुंजी-स्पर्श कंपन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुंजी-स्पर्श ध्वनि आवाज़"</string> diff --git a/java/res/values-hr/strings-action-keys.xml b/java/res/values-hr/strings-action-keys.xml index 564f70363..c13da1897 100644 --- a/java/res/values-hr/strings-action-keys.xml +++ b/java/res/values-hr/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Pret."</string> <string name="label_done_key" msgid="7564866296502630852">"Got."</string> <string name="label_send_key" msgid="482252074224462163">"Poš."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauz."</string> <string name="label_wait_key" msgid="5891247853595466039">"Čekaj"</string> </resources> diff --git a/java/res/values-hr/strings-talkback-descriptions.xml b/java/res/values-hr/strings-talkback-descriptions.xml index 31c7eb5f8..b3d5f707c 100644 --- a/java/res/values-hr/strings-talkback-descriptions.xml +++ b/java/res/values-hr/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nije unesen tekst"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ispravlja <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> u <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> vrši samoispravljanje"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kôd tipke %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Uključena je tipka Shift (dodirnite za onemogućivanje)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Uključeno je pisanje velikim slovima (Caps Lock) (dodirnite za onemogućivanje)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Više simbola"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboli"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Brisanje"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simboli"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Slova"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Prethodna"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Omogućena je tipka Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Omogućen je Caps Lock"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Tipka Shift onemogućena je"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Način unosa simbola"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Način s više simbola"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Način unosa slova"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefonski način rada"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Način unosa telefonskih simbola"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Mjesta"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboli"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikoni"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index 0882faad4..3d749e111 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Provjera pravopisa upotrebljava unose iz vašeg popisa kontakata"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Predlaži imena kontakata"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Prilagođeni prijedlozi"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Točka s dva razmaka"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvostrukim dodirivanjem razmaknice umeću se točka i razmak"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Jezici unosa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dodirnite ponovo za spremanje"</string> <string name="has_dictionary" msgid="6071847973466625007">"Rječnik je dostupan"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Omogući korisničke povratne informacije"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Pomozite u poboljšanju ovog uređivača načina unosa automatskim slanjem statistike upotrebe i izvješća o padu programa"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engleski (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engleski (SAD)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Omogući"</string> <string name="not_now" msgid="6172462888202790482">"Ne sada"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Već postoji isti stil unosa: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način studije upotrebljivosti"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Odgoda dugog pritiska tipke"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibracije pritiska"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasnoća pritiska tipke"</string> diff --git a/java/res/values-hu/strings-action-keys.xml b/java/res/values-hu/strings-action-keys.xml index 257f30f34..94445f58b 100644 --- a/java/res/values-hu/strings-action-keys.xml +++ b/java/res/values-hu/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Előző"</string> <string name="label_done_key" msgid="7564866296502630852">"Kész"</string> <string name="label_send_key" msgid="482252074224462163">"Küld"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Állj"</string> <string name="label_wait_key" msgid="5891247853595466039">"Vár"</string> </resources> diff --git a/java/res/values-hu/strings-talkback-descriptions.xml b/java/res/values-hu/strings-talkback-descriptions.xml index ec2c353ab..cf505c472 100644 --- a/java/res/values-hu/strings-talkback-descriptions.xml +++ b/java/res/values-hu/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nincs szöveg megadva"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> billentyű – <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> szóra javítja a következőt: <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> billentyű automatikus javítást végez"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Billentyűkód: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"A Shift be van kapcsolva (érintse meg a kikapcsoláshoz)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"A Caps lock be van kapcsolva (érintse meg a kikapcsoláshoz)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"További szimbólumok"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Szimbólumok"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Törlés"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Szimbólumok"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Betűk"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Előző"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift bekapcsolva"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock bekapcsolva"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift kikapcsolva"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"„Szimbólumok” mód"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"További szimbólumok mód"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"„Betű” mód"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"„Telefon” mód"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"„Telefonos szimbólumok” mód"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Helyek"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Szimbólumok"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Hangulatjelek"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index 8a953ab9d..5691bf703 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"A helyesírás-ellenőrző használja a névjegyek bejegyzéseit"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés gombnyomásra"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Javasolt névjegyek"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Testreszabott javaslatok"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dupla szóköz: pont"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"A szóköz kétszeri megérintése beszúr egy pontot, majd egy szóközt"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Beviteli nyelvek"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Érintse meg újból a mentéshez"</string> <string name="has_dictionary" msgid="6071847973466625007">"Van elérhető szótár"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Felhasználói visszajelzés engedélyezése"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Segíthet a beviteli módszer szerkesztőjének javításában, ha engedélyezi a használati statisztikák és a hibajelentések automatikus elküldését."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Billentyűzettéma"</string> <string name="subtype_en_GB" msgid="88170601942311355">"angol (brit)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"angol (amerikai)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Engedélyezés"</string> <string name="not_now" msgid="6172462888202790482">"Most nem"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ugyanez a bemenetstílus már létezik: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Használhatósági teszt"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Hosszú nyomás késleltetése"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Gombnyomás rezgési időtartama"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Gombnyomás hangereje"</string> diff --git a/java/res/values-hy-rAM/strings-action-keys.xml b/java/res/values-hy-rAM/strings-action-keys.xml index af9dd5d9d..574ae5a42 100644 --- a/java/res/values-hy-rAM/strings-action-keys.xml +++ b/java/res/values-hy-rAM/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Նախորդը"</string> <string name="label_done_key" msgid="7564866296502630852">"Պատրաստ է"</string> <string name="label_send_key" msgid="482252074224462163">"Ուղարկել"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Դադարեցնել"</string> <string name="label_wait_key" msgid="5891247853595466039">"Սպասել"</string> </resources> diff --git a/java/res/values-hy-rAM/strings-emoji-descriptions.xml b/java/res/values-hy-rAM/strings-emoji-descriptions.xml new file mode 100644 index 000000000..f41f2fc96 --- /dev/null +++ b/java/res/values-hy-rAM/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Հեղինակային իրավունքի նշան"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Գրանցված ապրանքանիշի նշան"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Կրկնակի բացականչական նշան"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Բացականչական հարցական նշան"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Ապրանքանշանի նշան"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Տեղեկատվական աղբյուր"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Ձախ-աջ սլաք"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Վեր-վար սլաք"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Սլաք դեպի հյուսիս-արևմուտք"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Սլաք դեպի հյուսիս-արևելք"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Սլաք դեպի հարավ-արևելք"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Սլաք դեպի հարավ-արևմուտք"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Կեռ ձախ սլաք"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Կեռ աջ սլաք"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Ժամացույց"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Ավազի ժամացույց"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Դեպի աջ ուղղված սև կրկնակի եռանկյուն"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Դեպի ձախ ուղղված սև կրկնակի եռանկյուն"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Դեպի վեր ուղղված սև կրկնակի եռանկյուն"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Դեպի վար ուղղված սև կրկնակի եռանկյուն"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Զարթուցիչ"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Հոսող ավազով ժամացույց"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Շրջանաձև լատիներեն մեծատառ m"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Փոքր սև քառակուսի"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Փոքր սպիտակ քառակուսի"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Դեպի աջ ուղղված սև եռանկյուն"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Դեպի ձախ ուղղված սև եռանկյուն"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Միջին սպիտակ քառակուսի"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Միջին սև քառակուսի"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Միջին փոքր սպիտակ քառակուսի"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Միջին փոքր սև քառակուսի"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Սև արև` ճառագայթներով"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Ամպ"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Սև հեռախոս"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Քվեատուփ` նշանով"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Անձրևանոց` անձրևի կաթիլներով"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Տաք ըմպելիք"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Սպիտակ դեպի վեր ուղղված դասիչ"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Սպիտակ ժպտացող դեմք"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Խոյ"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Ցուլ"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Երկվորյակ"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Խեցգետին"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Առյուծ"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Կույս"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Կշեռք"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Կարիճ"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Աղեղնավոր"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Այծեղջյուր"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Ջրհոս"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Ձկներ"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Սև ագռավ"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Սև խաչ"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Սև սիրտ"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Սև աղյուս"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Տաք աղբյուրներ"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Սև վերամշակման նշան"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Սայլակի նշան"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Խարիսխ"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Զգուշացման նշան"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Բարձր լարման նշան"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Միջին սպիտակ շրջան"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Միջին սև շրջան"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Ֆուտբոլի գնդակ"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Բեյսբոլ"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Առանց ձյան ձնեմարդ"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Արևն ամպի ետևում"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Օձակալ"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Մուտք չկա"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Եկեղեցի"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Շատրվան"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Դրոշը փոսում"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Առագաստանավ"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Վրան"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Վառելիքի պոմպ"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Սև մկրատ"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Սպիտակ խոշոր ստուգանիշ"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Ինքնաթիռ"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Ծրար"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Բարձրացրած բռունցք"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Բարձրացրած ձեռք"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Հաղթանշան"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Մատիտ"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Սև գրչածայր"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Խոշոր ստուգանիշ"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Խոշոր բազմապատկում x"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Կայծեր"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Ութանկյուն աստղանիշ"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Ութ կետով սև աստղ"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Ձյան փաթիլ"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Կայծ"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Խաչանշան"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Բացասական քառակուսի խաչանշան"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Սև հարցական նշաններով նախշազարդ"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Սպիտակ հարցական նշաններով նախշազարդ"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Սպիտակ բացականչական նշաններով նախշազարդ"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Խոշոր բացականչական նշան"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Սև սիրտ"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Խոշոր գումարման նշան"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Խոշոր հանման նշան"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Խոշոր բաժանման նշան"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Սև դեպի աջ սլաք"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Խուճուճ հանգույց"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Կրկնակի խուճուճ հանգույց"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Դեպի աջ ուղղված, ապա դեպի վեր ծռված սլաք"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Դեպի աջ ուղղված, ապա դեպի վար ծռված սլաք"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Դեպի ձախ սև սլաք"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Դեպի վեր սև սլաք"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Դեպի վար սև սլաք"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Մեծ սև քառակուսի"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Մեծ սպիտակ քառակուսի"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Միջին սպիտակ քառակուսի"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Խոշոր շրջան"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Ալիքաձև գծիկ"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Երաժշտական փոփոխականի նշան"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Շնորհավորանքի շրջանաձև խորհրդանշան"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Գաղտնիքի շրջանաձև խորհրդանշան"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Մահջոնգի սալիկ՝ կարմիր վիշապով"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Սև ջոկեր խաղաքարտ"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Արյան Ա խումբ"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Արյան Բ խումբ"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Արյան Օ խումբ"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Ավտոկայանատեղի"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Արյան ԱԲ խումբ"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"Քառակուսի CL"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Քառակուսի COOL"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Քառակուսի FREE"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"Քառակուսի ID"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"Քառակուսի NEW"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"Քառակուսի NG"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"Քառակուսի OK"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"Քառակուսի SOS"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"Քառակուսի UP բացականչական նշանով"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"Քառակուսի vs"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Քառակուսի Կատականա այստեղ"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Քառակուսի Կատականա ծառայություն"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Քառակուսի խորհրդանշան անվճար"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Քառակուսի խորհրդանշան պահված տեղ"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Քառակուսի խորհրդանշան արգելում"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Քառակուսի խորհրդանշան թափուր աշխատատեղ"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Քառակուսի խորհրդանշան ընդունված"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Քառակուսի խորհրդանշան լրիվ զբաղվածություն"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Քառակուսի խորհրդանշան վճարված"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Քառակուսի խորհրդանշան ամսական"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Քառակուսի խորհրդանշան դիմում"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Քառակուսի խորհրդանշան զեղչ"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Քառակուսի խորհրդանշան աշխատում է"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Շրջանաձև խորհրդանշան առավելություն"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Շրջանաձև խորհրդանշան ընդունում"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Ցիկլոն"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Մառախլապատ"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Փակ անձրևանոց"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Աստղազարդ գիշեր"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Արևածագը լեռներում"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Արևածագ"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Քաղաքը մթնշաղում"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Մայրամուտը շենքերի վրա"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Ծիածան"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Կամուրջը գիշերը"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Ծովի ալիք"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Հրաբուխ"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Ծիր կաթին"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Երկրագունդը՝ Եվրոպա-Աֆրիկա"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Երկրագունդը՝ Ամերիկաներ"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Երկրագունդը՝ Ավստրալիա"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Երկրագունդը միջօրեականներով"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Նորալուսնի նշան"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Աճող կիսալուսնի նշան"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Լուսնի առաջին քառորդի նշան"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Աճող ուռուցիկ լուսնի նշան"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Լիալուսնի նշան"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Փոքրացող ուռուցիկ լուսնի նշան"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Լուսնի վերջին քառորդի նշան"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Փոքրացող կիսալուսնի նշան"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Կիսալուսին"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Դեմքով նորալուսին"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Առաջին քառորդ լուսին՝ դեմքով"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Վերջին քառորդ լուսին՝ դեմքով"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Դեմքով լիալուսին"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Դեմքով արև"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Փայլուն աստղ"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Կրակող աստղ"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Շագանակ"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Սածիլ"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Մշտադալար ծառ"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Տերևաշատ ծառ"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Պալմա"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Կակտուս"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Կակաչ"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Բալենու ծաղիկ"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Վարդ"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Հիբիսկուս"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Արևածաղիկ"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Ծաղիկ"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Եգիպտացորեն"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Բրինձ"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Խոտաբույս"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Քառատերև երեքնուկ"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Թխկու տերև"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Թափված տերև"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Քամուց թռչող տերև"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Սունկ"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Լոլիկ"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Սմբուկ"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Խաղող"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Սեխ"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Ձմերուկ"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Մանդարին"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Կիտրոն"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Բանան"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Արքայախնձոր"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Կարմիր խնձոր"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Կանաչ խնձոր"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Տանձ"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Դեղձ"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Բալ"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Ելակ"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Համբուրգեր"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Պիցցայի կտոր"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Ոսկորով միս"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Հավի բուդ"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Բրնձի կրեկեր"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Բրնձի կլորակ"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Եփած բրինձ"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Քարի և բրինձ"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Գոլորշիացող բաժակ"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Սպագետտի"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Հաց"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Տապակած կարտոֆիլ"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Տապակած քաղցր կարտոֆիլ"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Դանգո"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Օդեն"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Սուշի"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Տապակած ծովախեցգետին"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Ձկան տորթ՝ օղակաձև ձևավորմամբ"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Լցնովի պաղպաղակ"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Սառցե դեսերտ"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Պաղպաղակ"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Դոնաթ"</string> + <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_1F36E" msgid="4630541402785165902">"Եփովի կրեմ"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Մեղրանոթ"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Տորթի կտոր"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Բենտոյով տուփ"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Սնունդով լի ափսե"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Խոհարարություն"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Դանակ և պատառաքաղ"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Թեյի անպոչ բաժակ"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Սակեի շիշ և գավաթ"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Գինու գավաթ"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Կոկտեյլի բաժակ"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Արևադարձային խմիչք"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Գարեջրի բաժակ"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Զրնգացող գարեջրի բաժակներ"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Մանկական շիշ"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Ժապավեն"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Փաթեթավորված նվեր"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Ծննդյան տորթ"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Դդմի լապտեր"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Տոնածառ"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Ձմեռ պապ"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Հրավառություն"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Բենգալյան կրակ"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Փուչիկ"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Ճայթուկ"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Կոնֆետտի գնդակ"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Տանաբատա ծառ"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Հատվող դրոշներ"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Սոճու ձևավորում"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Ճապոնական տիկնիկներ"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Կարպ նավադրոշ"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Քամու երգ"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Լուսնի դիտման արարողություն"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Դպրոցական պայուսակ"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Ավարտական գլխարկ"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Ձի կարուսել"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Սատանայի անիվ"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Ուրախ սարեր"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Ձկնորսական կարթ և ձուկ"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Բարձրախոս"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Տեսախցիկ"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Կինոթատրոն"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Ականջակալ"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Ներկապնակ"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Ցիլինդր"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Կրկեսային վրան"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Տոմս"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Ծափափեղկ"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Կատարողական արվեստ"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Տեսախաղ"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Ուղղակի հարված"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Խաղասարք"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Բիլիարդ"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Խաղազառ"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Բոուլինգ"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Ծաղկով խաղաքարտեր"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Երաժշտական ձայնանիշ"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Մի քանի երաժշտական ձայնանիշ"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Սաքսոֆոն"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Կիթառ"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Երաժշտական ստեղնաշար"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Շեփոր"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Ջութակ"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Նոտագրում"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Վազքի վերնաշապիկ"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Թենիսի թիակ և գնդակ"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Դահուկ և դահուկի կոշիկ"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Բասկետբոլի գնդակ և զամբյուղ"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Վերջնակետի դրոշ"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Ձնատախտակորդ"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Վազորդ"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Ալեսահորդ"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Մրցանակ"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Ձիարշավ"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Ամերիկյան ֆուտբոլ"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Ռեգբի"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Լողորդ"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Բնակելի շենք"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Այգով տուն"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Գրասենյակային շենք"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Ճապոնական փոստատուն"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Եվրոպական փոստատուն"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Հիվանդանոց"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Բանկ"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Բանկոմատ"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Հյուրանոց"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Սիրո հյուրանոց"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Պարենային խանութ"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Դպրոց"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Հանրախանութ"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Գործարան"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Ճապոնական լապտեր"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Ճապոնական ամրոց"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Եվրոպական ամրոց"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Առնետ"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Մուկ"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Եզ"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Գոմեշ"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Կով"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Ընձառյուծ"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Ճագար"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Կատու"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Վիշապ"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Կոկորդիլոս"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Կետ"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Խխունջ"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Օձ"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Ձի"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Խոյ"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Այծ"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Ոչխար"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Կապիկ"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Աքաղաղ"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Հավ"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Շուն"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Խոզ"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Վարազ"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Փիղ"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Ութոտնուկ"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Պարուրաձև խեցի"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Բզեզ"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Մրջյուն"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Մեղու"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Զատիկ"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Ձուկ"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Արևադարձային ձուկ"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Գնդաձուկ"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Կրիա"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Թուխսի ճուտ"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Ճուտիկ"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Դիմահայաց ճուտիկ"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Թռչուն"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Պինգվին"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Կոալա"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Պուդել"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Դրոմադեր ուղտ"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Երկսապատանի ուղտ"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Դելֆին"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Մկան մռութ"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Կովի մռութ"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Վագրի մռութ"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Ճագարի մռութ"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Կատվի մռութ"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Վիշապի մռութ"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Թռչող կետ"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Ձիու մռութ"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Կապիկի մռութ"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Շան մռութ"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Խոզի մռութ"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Գորտի մռութ"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Գերմանամկան դեմք"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Գայլի մռութ"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Արջի մռութ"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Պանդայի մռութ"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Խոզի քիթ"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Թաթի հետքեր"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Աչքեր"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Ականջ"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Քիթ"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Բերան"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Լեզու"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Վեր ուղղված սպիտակ ցուցամատ"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Վար ուղղված սպիտակ ցուցամատ"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Ձախ ուղղված սպիտակ ցուցամատ"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Աջ ուղղված սպիտակ ցուցամատ"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Բռունցքի նշան"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Թափահարող ձեռքի նշան"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Ok-ի նշան"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Բութ մատները վեր նշան"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Բութ մատները վար նշան"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Ծափահարող ձեռքերի նշան"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Բաց ափերով ձեռքերի նշան"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Թագ"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Կնոջ գլխարկ"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Ակնոց"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Փողկապ"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Շապիկ"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Ջինս"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Զգեստ"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Կիմոնո"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Բիկինի"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Կնոջ հագուստ"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Դրամապանակ"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Պայուսակ"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Քսակ"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Տղամարդու կոշիկ"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Մարզակոշիկ"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Բարձրակրունկ կոշիկ"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Կնոջ սանդալներ"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Կնոջ երկարաճիտ կոշիկներ"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Ոտնահետքեր"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Կիսանդրու ուրվագիծ"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Կիսանդրիների ուրվագիծ"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Տղա"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Աղջիկ"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Տղամարդ"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Կին"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Ընտանիք"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Միացված ձեռքերով տղամարդ և կին"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Միացված ձեռքերով երկու տղամարդ"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Միացված ձեռքերով երկու կին"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Ոստիկան"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Նապաստակի ականջներով կին"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Քողով հարսնացու"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Շիկահեր մարդ"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Չինական գլխարկով տղամարդ"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Գլխաշորով տղամարդ"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Ծեր տղամարդ"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Ծեր կին"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Մանուկ"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Շինարար"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Արքայադուստր"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Ճապոնական օգր"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Ճապոնական գոբլին"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Ուրվական"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Մանուկ հրեշտակ"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Այլմոլորակային"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Այլմոլորակային հրեշ"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Սատանայի ճուտ"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Գանգ"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Տեղեկատու անձ"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Պահնորդ"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Պարող"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Շրթներկ"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Եղունգաներկ"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Դեմքի մերսում"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Սանրվածք"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Վարսավիրի նշան"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Ներարկիչ"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Հաբ"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Համբույրի նշան"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Սիրային նամակ"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Մատանի"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Թանկարժեք քար"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Համբույր"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Ծաղկեփունջ"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Զույգը սրտի մեջ"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Հարսանիք"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Բաբախող սիրտ"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Կոտրված սիրտ"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Երկու սրտեր"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Պսպղացող սիրտ"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Աճող սիրտ"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Խոցված սիրտ"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Կապույտ սիրտ"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Կանաչ սիրտ"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Դեղին սիրտ"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Մանուշակագույն սիրտ"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Ժապավենով սիրտ"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Միացող սրտեր"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Սրտիկներով ձևավորում"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Շեղանկյուն` կետիկով"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Էլեկտրական լամպ"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Ջղայնության նշան"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Ռումբ"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Քնի նշան"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Բախման նշան"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Հոսող քրտինքի նշան"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Կաթիլ"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Գծիկի նշան"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Կեղտի կույտ"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Լարված երկգլուխ մկան"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Գլխապտույտի նշան"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Խոսքի փուչիկ"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Մտքի փուչիկ"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Սպիտակ ծաղիկ"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Հարյուր միավորի նշան"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Փողի քսակ"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Արտարժույթի փոխանակում"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Խոշոր դոլարի նշան"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Վարկային քարտ"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Իենի նշանով թղթադրամ"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Դոլարի նշանով թղթադրամ"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Եվրոյի նշանով թղթադրամ"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Ֆունտի նշանով թղթադրամ"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Թևավոր թղթադրամ"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Աճման միտումով և իենի նշանով գրաֆիկ"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Նստատեղ"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Անհատական համակարգիչ"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Թղթապանակ"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Փոքր սկավառակ"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Ճկուն սկավառակ"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Օպտիկական սկավառակ"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Ֆայլերի թղթապանակ"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Ֆայլերի բաց թղթապանակ"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Ոլորված ծայրով էջ"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Թեքված էջ"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Օրացույց"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Պոկվող էջերով օրացույց"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Քարտային դասիչ"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Աճման միտումով գրաֆիկ"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Նվազման միտումով գրաֆիկ"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Ձողային գրաֆիկ"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Սեղմատախտակ"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Սևեռակ"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Կլոր սևեռակ"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Ամրակ"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Ուղիղ քանոն"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Եռանկյուն քանոն"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Էջանիշ"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Մատյան"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Նոթատետր"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Ձևավոր կազմով նոթատետր"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Փակ գիրք"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Բաց գիրք"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Կանաչ գիրք"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Կապույտ գիրք"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Նարնջագույն գիրք"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Գրքեր"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Անվանաքարտ"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Գալար"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Հիշեցում"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Լսափող"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Փեյջեր"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Հեռապատճենի սարք"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Արբանյակային ալեհավաք"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Հրապարակային բարձրախոս"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Ուրախ բարձրախոս"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Ելքային արկղ"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Մուտքային արկղ"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Փաթեթ"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"Էլփոստի նշան"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Մուտքային ծրար"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Վերևից դեպի վար սլաքով ծրար"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Իջեցված դրոշով փակ փոստարկղ"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Բարձրացված դրոշով փակ փոստարկղ"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Բարձրացված դրոշով փակ փոստարկղ"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Իջեցված դրոշով բաց փոստարկղ"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Փոստարկղ"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Փոստային եղջյուր"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Թերթ"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Բջջային հեռախոս"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Բջջային հեռախոս՝ ձախ կողմում դեպի վեր սլաքով"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Թրթռոցի ռեժիմ"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Բջջային հեռախոսը` անջատած"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Բջջային հեռախոսներ չկան"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Ձողերով ալեհավաք"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Ֆոտոխցիկ"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Տեսախցիկ"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Հեռուստատեսություն"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Ռադիո"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Տեսաժապավեն"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Պտտված դեպի աջ սլաքներ"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Ժամացույցի սլաքի ուղղությամբ դեպի աջ և ձախ բաց շրջանաձև սլաքներ"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Ժամացույցի սլաքի ուղղությամբ դեպի աջ և ձախ բաց շրջանաձև սլաքներ՝ շրջանաձև մեկ վերադրումով"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Ժամացույցի սլաքի ուղղությամբ դեպի վար և վեր բաց շրջանաձև սլաքներ"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Ժամացույցի սլաքի հակառակ ուղղությամբ դեպի վար և վեր բաց շրջանաձև սլաքներ"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Ցածր պայծառության նշան"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Բարձր պայծառության նշան"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Չեղարկման գծով բարձրախոս"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Բարձրախոս"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Մեկ ձայնային ալիքով բարձրախոս"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Երեք ձայնային ալիքով բարձրախոս"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Մարտկոց"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Էլեկտրական խրոցակ"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Ձախ թեքված խոշորացույց"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Աջ թեքված խոշորացույց"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Կողպեք և թանաքի գրիչ"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Փակված կողպեք՝ բանալիով"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Բանալի"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Կողպեք"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Բաց կողպեք"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Զանգ"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Չեղարկման գծով զանգ"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Էջանիշ"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Հղման նշան"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Կետակոճակ"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Հետ՝ վերևում դեպի ձախ սլաքով"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"Վերջ՝ վերևում դեպի ձախ սլաքով"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"Միացված է՝ բացականչական նշանով և վերևում դեպի աջ ու ձախ սլաքով"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Շուտով՝ վերևում դեպի աջ սլաքով"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Վերև՝ վերևում դեպի վեր սլաքով"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Տասնութից ցածրի արգելման նշան"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Ստեղնատասնյակ"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Լատիներեն մեծատառերով մուտքագրման նշան"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Լատիներեն փոքրատառերով մուտքագրման նշան"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Թվային մուտքագրման նշան"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Նշանների ներածման նշան"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Լատինատառ մուտքագրման նշան"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Կրակ"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Էլեկտրական լապտեր"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Ոլորակ"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Մուրճ"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Մանեկ ու պտուտակ"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Ճապոնական դանակ"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Ատրճանակ"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Մանրադիտակ"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Հեռադիտակ"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Ապակե գնդակ"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Միջակետով վեցանկյուն աստղ"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Ճապոնական սկսնակի նշան"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Եռաժանու նշան"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Սև քառակուսի կոճակ"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Սպիտակ քառակուսի կոճակ"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Մեծ կարմիր շրջան"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Մեծ կապույտ շրջան"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Մեծ նարնջագույն շեղանկյուն"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Մեծ կապույտ շեղանկյուն"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Փոքր նարնջագույն շեղանկյուն"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Փոքր կապույտ շեղանկյուն"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Վեր ուղղված կարմիր եռանկյուն"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Վար ուղղված կարմիր եռանկյուն"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Վեր ուղղված փոքր կարմիր եռանկյուն"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Վար ուղղված փոքր կարմիր եռանկյուն"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Թվատախտակ՝ ժամը մեկով"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Թվատախտակ՝ ժամը երկուսով"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Թվատախտակ՝ ժամը երեքով"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Թվատախտակ՝ ժամը չորսով"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Թվատախտակ՝ ժամը հինգով"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Թվատախտակ՝ ժամը վեցով"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Թվատախտակ՝ ժամը յոթով"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Թվատախտակ՝ ժամը ութով"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Թվատախտակ՝ ժամը ինով"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Թվատախտակ՝ ժամը տասով"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Թվատախտակ՝ ժամը տասնմեկով"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Թվատախտակ՝ ժամը տասներկուսով"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Թվատախտակ՝ ժամը մեկն անց երեսուն"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Թվատախտակ՝ ժամը երկուսն անց երեսուն"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Թվատախտակ՝ ժամը երեքն անց երեսուն"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Թվատախտակ՝ ժամը չորսն անց երեսուն"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Թվատախտակ՝ ժամը հինգն անց երեսուն"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Թվատախտակ՝ ժամը վեցն անց երեսուն"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Թվատախտակ՝ ժամը յոթն անց երեսուն"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Թվատախտակ՝ ժամը ութն անց երեսուն"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Թվատախտակ՝ ժամը ինն անց երեսուն"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Թվատախտակ՝ ժամը տասն անց երեսուն"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Թվատախտակ՝ ժամը տասնմեկն անց երեսուն"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Թվատախտակ՝ ժամը տասներկուսն անց երեսուն"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Ֆուձի լեռ"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Տոկիոյի աշտարակ"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Ազատության արձան"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Ճապոնիայի ուրվագիծ"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Մայաների քարե արձան"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Քմծիծաղով դեմք"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Քմծիծաղով դեմք՝ ժպտացող աչքերով"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Ուրախության արցունքներով դեմք"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Ժպտացող դեմք՝ բաց բերանով"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Ժպտացող դեմք՝ բաց բերանով և ժպտացող աչքերով"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Ժպտացող դեմք՝ բաց բերանով և սառը քրտինքով"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Ժպտացող դեմք՝ բաց բերանով և պինդ փակված աչքերով"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Ժպտացող դեմք` լուսապսակով"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Ժպյտացող դեմք` պոզերով"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Աչքով անող դեմք"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Ժպտացող դեմք` ժպտացող աչքերով"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Համեղ ուտեստներ վայելող դեմք"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Հանգստացած դեմք"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Ժպտացող դեմք` սրտաձև աչքերով"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Ժպտացող դեմք` արևային ակնոցով"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Հեգնական ժպիտով դեմք"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Չեզոք դեմք"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Արտահայտությունից զուրկ դեմք"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Չհետաքրքրված դեմք"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Սառը քրտինքով դեմք"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Մտախոհ դեմք"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Շփոթված դեմք"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Մոլորված դեմք"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Համբուրող դեմք"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Օդային համբույր ուղարկող դեմք"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Համբուրող դեմք` ժպտացող աչքերով"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Համբուրող դեմք` փակ աչքերով"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Լեզուն հանած դեմք"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Լեզուն հանած և աչքով անող դեմք"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Լեզուն հանած և աչքերը փակած դեմք"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Հիասթափված դեմք"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Մտահոգ դեմք"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Ջղային դեմք"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Շրթունքներն ուռած դեմք"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Լացող դեմք"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Կամակոր դեմք"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Հաղթական արտահայտությամբ դեմք"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Հիասթափված, բայց հանգստացած դեմք"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Բաց բերանով խոժոռ դեմք"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Տանջված դեմք"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Վախեցած դեմք"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Հոգնած դեմք"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Քնաթաթախ դեմք"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Հոգնած դեմք"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Ծամածռված դեմք"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Բարձրաձայն լացող դեմք"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Բաց բերանով դեմք"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Լռող դեմք"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Բաց բերանով և սառը քրտինքով դեմք"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Վախից ճչացող դեմք"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Ապշած դեմք"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Շիկնած դեմք"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Քնած դեմք"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Գլխապտույտով դեմք"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Առանց բերանի դեմք"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Բժշկական դիմակով դեմք"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Քմծիծաղող կատվի դեմք՝ ժպտացող աչքերով"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Կատվի դեմք՝ երջանկության արցունքներով"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Ժպտացող կատվի դեմք՝ բաց բերանով"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Ժպտացող կատվի դեմք՝ սրտաձև աչքերով"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Կատվի դեմք՝ ծուռ ժպիտով"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Համբուրող կատվի դեմք՝ փակ աչքերով"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Շրթունքներն ուռած կատվի դեմք"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Լացող կատվի դեմք"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Հոգնած կատվի դեմք"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"«Ոչ լավ» արտահայտությամբ դեմք"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"«Լավ» արտահայտությամբ դեմք"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Կռացած դեմք"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Աչքերը ձեռքերով փակած կապիկ"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Ականջները ձեռքերով փակած կապիկ"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Բերանը ձեռքերով փակած կապիկ"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Երջանիկ մարդ` բարձրացրած ձեռքով"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Երկու ձեռքը ողջույնի համար բարձրացրած մարդ"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Դժգոհ մարդ"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Շրթունքներն ուռեցրած դեմքով մարդ"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Ծալված ձեռքերով մարդ"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Հրթիռ"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Ուղղաթիռ"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Շոգեքարշ"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Վագոն"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Ճեպընթաց գնացք"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Ճեպընթաց գնացք` փամփուշտաձև քթով"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Գնացք"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Մետրո"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Մոնոռելս"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Կայան"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Տրամվայ"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Տրամվայ"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Ավտոբուս"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Հանդիպակաց ավտոբուս"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Տրոլեյբուս"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Ավտոբուսի կանգառ"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Միկրոավտոբուս"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Շտապ օգնություն"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Հրշեջ մեքենա"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Ոստիկանական մեքենա"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Հանդիպակաց եկող ոստիկանական մեքենա"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Տաքսի"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Հանդիպակաց եկող տաքսի"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Ավտոմեքենա"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Հանդիպակաց եկող ավտոմեքենա"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Ավտոտնակ"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Առաքման մեքենա"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Ինքնագնաց"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Տրակտոր"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Մոնոռելս"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Լեռնային երկաթուղի"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Երկաթուղային կախոց"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Լեռնային ճոպանուղի"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Ճոպանուղի"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Նավ"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Լաստանավ"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Մոտորանավ"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Հորիզոնական լուսացույց"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Ուղղահայաց լուսացույց"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Շինարարության նշան"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Ոստիկականան մեքենայի ազդանշանային լույսեր"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Եռանկյուն դրոշ"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Դուռ"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Չմտնելու նշան"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Ծխելու նշան"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Չծխելու նշան"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Աղբը իր տեղում թափելու նշան"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Աղբ չթափելու նշան"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Խմելու ջրի նշան"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Ոչ խմելու ջրի նշան"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Հեծանիվ"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Հեծանիվների արգելք"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Հեծանվորդ"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Լեռնային հեծանվորդ"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Հետիոտն"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Հետիոտների արգելք"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Հատող երեխաներ"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Տղամարդկանց նշան"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Կանանց նշան"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Զուգարան"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Մանուկի նշան"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Զուգարան"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"Արտաքնոց"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Ցնցուղ"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Բաղնիք"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Լոգարան"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Անձնագրերի ստուգում"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Մաքսատուն"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Ուղեբեռի ստացում"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Ձախ ուղեբեռ"</string> +</resources> diff --git a/java/res/values-hy-rAM/strings-talkback-descriptions.xml b/java/res/values-hy-rAM/strings-talkback-descriptions.xml index e5b1ce6d9..5b7e4b522 100644 --- a/java/res/values-hy-rAM/strings-talkback-descriptions.xml +++ b/java/res/values-hy-rAM/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Տեքստ չի մուտքագրվել"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g>-ը շտկում է <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>-ը՝ դարձնելով <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ստեղնը ինքնաշտկում է կատարում"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Բանալու կոդը՝ %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift-ը միացված է (հպել անջատելու համար)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock-ը միացված է (հպել՝ անջատելու համար)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Հավելյալ նշաններ"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Նշաններ"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Ջնջել"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Նշաններ"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Տառեր"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Նախորդը"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift-ը միացված է"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock-ը միացված է"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift-ն անջատված է"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Նշանների ռեժիմ"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Հավելյալ նշանների ռեժիմ"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Տառերի ռեժիմ"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Հեռախոսային ռեժիմ"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Հեռախոսի նշանների ռեժիմ"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Վայրեր"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Նշաններ"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Զմայլիկներ"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml index 03d56a626..d7851b540 100644 --- a/java/res/values-hy-rAM/strings.xml +++ b/java/res/values-hy-rAM/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Ներածման ընտրանքներ"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Հետազոտական գրառումների հրամաններ"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Փնտրել կոնտակտային անուններ"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Տառասխալների ուղղիչն օգտագործում է ձեր կոնտակտների ցանկի տվյալները"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Թրթռալ սեղմման ժամանակ"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Առաջարկել կոնտակտների անունները"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Օգտագործել կոնտակտների անունները՝ առաջարկների և ուղղումների համար"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Անհատականացված առաջարկներ"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Կրկնաբացակի վերջակետ"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Բացակի ստեղնի կրկնակի հպումը բացակից հետո վերջակետ է դնում"</string> <string name="auto_cap" msgid="1719746674854628252">"Ավտոմատ գլխատառացում"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Մուտքագրման լեզուներ"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Պահպանելու համար կրկին հպեք"</string> <string name="has_dictionary" msgid="6071847973466625007">"Բառարանն առկա է"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Միացնել օգտվողի արձագանքը"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Օգնել բարելավել այս մուտքագրման եղանակի խմբագրիչը՝ ինքնուրույն ուղարկելով Google-ին օգտագործման վիճակագրությունն ու վթարների հաշվետվությունները:"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Ստեղնաշարի թեման"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Անգլերեն (ՄԹ)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Անգլերեն (ԱՄՆ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Միացնել"</string> <string name="not_now" msgid="6172462888202790482">"Ոչ հիմա"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Մուտքագրման այսպիսի ոճ արդեն գոյություն ունի՝ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Հարմարավետության ուսումնասիրության ռեժիմ"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ստեղնի երկար սեղմման ուշացում"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Սեղմման թրթռոցի տևողություն"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Սեղմման ձայնի բարձրությունը"</string> diff --git a/java/res/values-in/strings-action-keys.xml b/java/res/values-in/strings-action-keys.xml index 26f32254d..052798d02 100644 --- a/java/res/values-in/strings-action-keys.xml +++ b/java/res/values-in/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Balik"</string> <string name="label_done_key" msgid="7564866296502630852">"Beres"</string> <string name="label_send_key" msgid="482252074224462163">"Kirim"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Jeda"</string> <string name="label_wait_key" msgid="5891247853595466039">"Tunggu"</string> </resources> diff --git a/java/res/values-in/strings-talkback-descriptions.xml b/java/res/values-in/strings-talkback-descriptions.xml index 73bf712f3..6f2970795 100644 --- a/java/res/values-in/strings-talkback-descriptions.xml +++ b/java/res/values-in/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Tidak ada teks yang dimasukkan"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> mengoreksi <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> melakukan koreksi otomatis"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kode tombol %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift aktif (ketuk untuk menonaktifkan)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock aktif (ketuk untuk menonaktifkan)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Simbol lainnya"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simbol"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Hapus"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simbol"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Huruf"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Sebelumnya"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift diaktifkan"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock diaktifkan"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift dinonaktifkan"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Mode simbol"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Mode simbol lainnya"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Mode huruf"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Mode telepon"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Mode simbol telepon"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Tempat"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simbol"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikon"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index e3c551629..3205a5db4 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pemeriksa ejaan menggunakan entri dari daftar kontak Anda"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sarankan nama Kontak"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kontak untuk saran dan koreksi"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Saran hasil personalisasi"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Titik spasi ganda"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Mengetuk tombol spasi dua kali akan memasukkan titik diikuti satu spasi"</string> <string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Bahasa masukan"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Sentuh lagi untuk menyimpan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamus yang tersedia"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktifkan masukan pengguna"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Bantu tingkatkan editor metode masukan dengan mengirim statistik penggunaan dan laporan kerusakan secara otomatis"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema keyboard"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Inggris (Inggris)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Inggris (AS)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktifkan"</string> <string name="not_now" msgid="6172462888202790482">"Nanti saja"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sudah ada gaya masukan yang sama: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode studi daya guna"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Penundaan tekan lama tombol"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durasi getar saat tekan tombol"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume suara saat tekan tombol"</string> diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml index 1588534c9..ab2f2625a 100644 --- a/java/res/values-is/strings.xml +++ b/java/res/values-is/strings.xml @@ -22,7 +22,6 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- no translation found for english_ime_input_options (3909945612939668554) --> <skip /> - <!-- no translation found for english_ime_research_log (8492602295696577851) --> <skip /> <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) --> <skip /> @@ -140,9 +139,7 @@ <skip /> <!-- no translation found for has_dictionary (6071847973466625007) --> <skip /> - <!-- no translation found for prefs_enable_log (6620424505072963557) --> <skip /> - <!-- no translation found for prefs_description_log (7525225584555429211) --> <skip /> <!-- no translation found for keyboard_layout (8451164783510487501) --> <skip /> @@ -198,7 +195,6 @@ <skip /> <!-- no translation found for custom_input_style_already_exists (8008728952215449707) --> <skip /> - <!-- no translation found for prefs_usability_study_mode (1261130555134595254) --> <skip /> <!-- no translation found for prefs_key_longpress_timeout_settings (6102240298932897873) --> <skip /> diff --git a/java/res/values-it/strings-action-keys.xml b/java/res/values-it/strings-action-keys.xml index 02e7b9c94..0a0cf9b7b 100644 --- a/java/res/values-it/strings-action-keys.xml +++ b/java/res/values-it/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Prec."</string> <string name="label_done_key" msgid="7564866296502630852">"Fine"</string> <string name="label_send_key" msgid="482252074224462163">"Invia"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausa"</string> <string name="label_wait_key" msgid="5891247853595466039">"Attendi"</string> </resources> diff --git a/java/res/values-it/strings-talkback-descriptions.xml b/java/res/values-it/strings-talkback-descriptions.xml index 760db696c..f9eeefea0 100644 --- a/java/res/values-it/strings-talkback-descriptions.xml +++ b/java/res/values-it/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nessun testo inserito"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> con <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> esegue la correzione automatica"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Codice tasto %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Maiusc"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Maiusc attivo (tocca per disattivare)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Blocco maiuscole attivo (tocca per disattivare)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Altri simboli"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Maiusc"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboli"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Maiusc"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Elimina"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simboli"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Lettere"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Precedente"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Maiusc attivo"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Bloc Maiusc attivo"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Maiusc disattivato"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modalità simboli"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modalità altri simboli"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modalità lettere"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modalità telefono"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modalità simboli telefono"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Luoghi"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboli"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticon"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 6685095be..e51a87184 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"La funzione di controllo ortografico usa voci dell\'elenco contatti"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Suggerisci nomi di contatti"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Suggerimenti personalizz."</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Doppio spazio per punto"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocca due volte barra spaziatr. per inserire punto seguito da spazio"</string> <string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Lingue comandi"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Tocca di nuovo per salvare"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dizionario disponibile"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Attiva commenti degli utenti"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Contribuisci a migliorare l\'editor del metodo di immissione inviando automaticamente statistiche sull\'utilizzo e rapporti sugli arresti anomali"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema della tastiera"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Inglese (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Inglese (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Attiva"</string> <string name="not_now" msgid="6172462888202790482">"Non ora"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Esiste già uno stile di inuput uguale: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modalità Studio sull\'usabilità"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ritardo pressione lunga tasti"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durata vibraz. pressione tasto"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume audio a pressione tasto"</string> diff --git a/java/res/values-iw/strings-action-keys.xml b/java/res/values-iw/strings-action-keys.xml index f0f466b8e..398c081d6 100644 --- a/java/res/values-iw/strings-action-keys.xml +++ b/java/res/values-iw/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"הקודם"</string> <string name="label_done_key" msgid="7564866296502630852">"בוצע"</string> <string name="label_send_key" msgid="482252074224462163">"שלח"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"השהה"</string> <string name="label_wait_key" msgid="5891247853595466039">"המתן"</string> </resources> diff --git a/java/res/values-iw/strings-talkback-descriptions.xml b/java/res/values-iw/strings-talkback-descriptions.xml index e6344fb04..435aaba51 100644 --- a/java/res/values-iw/strings-talkback-descriptions.xml +++ b/java/res/values-iw/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"לא הוזן טקסט"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> מתקן את <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ל-<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> מבצע תיקון אוטומטי"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"קוד מקש %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift פועל (הקש כדי להשבית)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock פועל (הקש כדי להשבית)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"סמלים נוספים"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"סמלים"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"מחק"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"סמלים"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"אותיות"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"הקודם"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift פועל"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock פועל"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift מושבת"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"מצב סמלים"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"מצב \'סמלים נוספים\'"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"מצב אותיות"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"מצב טלפון"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"מצב סמלי טלפון"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"מקומות"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"סמלים"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"רגשונים"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index dab2d8ed8..36cb66330 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"בודק האיות משתמש בערכים מרשימת אנשי הקשר שלך"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט בלחיצה על מקשים"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"הצע שמות של אנשי קשר"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מרשימת אנשי הקשר עבור הצעות ותיקונים"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"הצעות מותאמות אישית"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"רווח כפול לנקודה"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"הקשה פעמיים על מקש הרווח מזינה נקודה ואחריה רווח"</string> <string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"שפות קלט"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"גע שוב כדי לשמור"</string> <string name="has_dictionary" msgid="6071847973466625007">"מילון זמין"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"הפעל משוב ממשתמשים"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"עזור לשפר את עורך שיטת הקלט על ידי שליחה אוטומטית של סטטיסטיקת שימוש ודוחות קריסה."</string> <string name="keyboard_layout" msgid="8451164783510487501">"עיצוב מקלדת"</string> <string name="subtype_en_GB" msgid="88170601942311355">"אנגלית (בריטניה)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"אנגלית (ארה\"ב)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"הפעל"</string> <string name="not_now" msgid="6172462888202790482">"לא עכשיו"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"סגנון קלט זהה כבר קיים: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"מצב לחקירת שימושיות"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"השהיית לחיצה ארוכה על מקש"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"משך רטט של לחיצת מקש"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"עוצמת קול של לחיצת מקש"</string> diff --git a/java/res/values-ja/strings-action-keys.xml b/java/res/values-ja/strings-action-keys.xml index 68aa6479e..ba8d06c78 100644 --- a/java/res/values-ja/strings-action-keys.xml +++ b/java/res/values-ja/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"前へ"</string> <string name="label_done_key" msgid="7564866296502630852">"完了"</string> <string name="label_send_key" msgid="482252074224462163">"送信"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"停止"</string> <string name="label_wait_key" msgid="5891247853595466039">"待機"</string> </resources> diff --git a/java/res/values-ja/strings-talkback-descriptions.xml b/java/res/values-ja/strings-talkback-descriptions.xml index 990774e16..a3b4c8faa 100644 --- a/java/res/values-ja/strings-talkback-descriptions.xml +++ b/java/res/values-ja/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"テキストが入力されていません"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g>は<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>を<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>に修正します"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g>で自動修正が実行されます"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"キーコード: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift有効(タップして解除)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"CapsLock有効(タップして解除)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"記号拡張"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"記号"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"削除"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"記号"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"英字"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"前へ"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift有効"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"CapsLock有効"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift解除"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"記号モード"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"記号拡張モード"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"英数モード"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"電話モード"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"電話記号モード"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"場所"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"記号"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"絵文字"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index 7d749a6a2..b6eea69dd 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"スペルチェッカーでは連絡先リストのエントリを使用します"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"連絡先の名前を候補に表示"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"入力候補のカスタマイズ"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"ダブルスペースピリオド"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"スペースバーをダブルタップするとピリオドとスペースを挿入できます"</string> <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"入力言語"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"保存するにはもう一度タップ"</string> <string name="has_dictionary" msgid="6071847973466625007">"辞書を利用できます"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"ユーザーフィードバックを有効にする"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"IMEの機能向上のため、使用統計状況やクラッシュレポートをGoogleに自動送信します。"</string> <string name="keyboard_layout" msgid="8451164783510487501">"キーボードのテーマ"</string> <string name="subtype_en_GB" msgid="88170601942311355">"英語 (英国)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"英語 (米国)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"有効にする"</string> <string name="not_now" msgid="6172462888202790482">"後で行う"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"同じ入力スタイルが既に存在します: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使いやすさの研究モード"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"キーの長押し時間"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"キー操作バイブの振動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"キー操作音の音量"</string> diff --git a/java/res/values-ka-rGE/strings-action-keys.xml b/java/res/values-ka-rGE/strings-action-keys.xml index e2dd05f7b..3ad6c3395 100644 --- a/java/res/values-ka-rGE/strings-action-keys.xml +++ b/java/res/values-ka-rGE/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"წინა"</string> <string name="label_done_key" msgid="7564866296502630852">"დასრ."</string> <string name="label_send_key" msgid="482252074224462163">"გაგზ."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"პაუზა"</string> <string name="label_wait_key" msgid="5891247853595466039">"მოცდა"</string> </resources> diff --git a/java/res/values-ka-rGE/strings-emoji-descriptions.xml b/java/res/values-ka-rGE/strings-emoji-descriptions.xml new file mode 100644 index 000000000..1840efc10 --- /dev/null +++ b/java/res/values-ka-rGE/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"საავტორო უფლების ნიშანი"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"რეგისტრირებული სავაჭრო ნიშნის სიმბოლო"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"ორმაგი ძახილის ნიშანი"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"ძახილის და კითხვის ნიშანი"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"სავაჭრო ნიშნის ნიშანი"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"ინფორმაციის წყარო"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"მარცხენა და მარჯვენა ისარი"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"ზედა და ქვედა ისარი"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"ჩრდილო დასავლეთის ისარი"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"ჩრდილო აღმოსავლეთის ისარი"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"სამხრეთ აღმოსავლეთის ისარი"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"სამხრეთ დასავლეთის ისარი"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"მარცხნივ მიმართული ისარი კაუჭით"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"მარჯვნივ მიმართული ისარი კაუჭით"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"საათი"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"ქვიშის საათი"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"მარჯვნივ მიმართული შავი ორმაგი სამკუთხედი"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"მარცხნივ მიმართული შავი ორმაგი სამკუთხედი"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"ზევით მიმართული შავი ორმაგი სამკუთხედი"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"ქვევით მიმართული შავი ორმაგი სამკუთხედი"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"მაღვიძარა"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"ქვიშის საათი ჩამოყრილი ქვიშით"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"წრეში ჩასმული ლათინური ასო m"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"შავი პატარა კვადრატი"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"თეთრი პატარა კვადრატი"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"შავი მარჯვნივ მიმართული სამკუთხედი"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"შავი მარცხნივ მიმართული სამკუთხედი"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"თეთრი საშუალო კვადრატი"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"შავი საშუალო კვადრატი"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"თეთრი საშუალო პატარა კვადრატი"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"შავი საშუალო პატარა კვადრატი"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"შავი მზე სხივებით"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"ღრუბელი"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"შავი ტელეფონი"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"მონიშნული საარჩევნო ბიულეტენი"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"ქოლგა წვიმის წვეთებით"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"ცხელი სასმელი"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"თეთრი ზევით მიმართული საჩვენებელი"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"თეთრი მომღიმარი სახე"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"ვერძი"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"კურო"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"ტყუპები"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"კირჩხიბი"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"ლომი"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"ქალწული"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"სასწორი"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"მორიელი"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"მშვილდოსანი"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"თხის რქა"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"მერწყული"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"თევზები"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"ბანქოს შავი ყვავი"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"ბანქოს შავი ჯვარი"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"ბანქოს შავი გული"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"ბანქოს შავი აგური"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"ცხელი წყარო"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"უნივერსალური გადამუშავების შავი სიმბოლო"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"სატარებელი სავარძლის სიმბოლო"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"ღუზა"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"გაფრთხილების ნიშანი"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"მაღალი ძაბვის ნიშანი"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"საშუალო თეთრი წრე"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"საშუალო შავი წრე"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"ფეხბურთის ბურთი"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"ბეისბოლის ბურთი"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"თოვლის კაცი თოვლის გარეშე"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"ღრუბელს მიფარებული მზე"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"გველისმჭერის თანავარსკვლავედი"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"შესვლა აკრძალულია"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"ეკლესია"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"შადრევანი"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"ალამი ორმოში"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"იალქნიანი გემი"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"კარავი"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"საწვავის სატუმბი"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"შავი მაკრატელი"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"თეთრი მკვეთრი მონიშვნის ნიშანი"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"თვითმფრინავი"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"კონვერტი"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"აღმართული მუშტი"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"აღმართული ხელი"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"გამარჯვების აღმნიშვნელი ხელი"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"ფანქარი"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"შავი კალმის წვერი"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"მკვეთრი მონიშვნის ნიშანი"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"მკვეთრი გამრავლების x"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"ნაპერწკლები"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"რვაწვერა ვარსკვლავი"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"რვაქიმიანი შავი ვარსკვლავი"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"ფიფქი"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"ნაპერწკალი"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"გადახაზვის ნიშანი"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"კვადრატში ჩასმული გადახაზული ნიშანი ნეგატივში"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"შავი კითხვის ნიშნის ორნამენტი"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"თეთრი კითხვის ნიშნის ორნამენტი"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"თეთრი ძახილის ნიშნის ორნამენტი"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"მკვეთრი ძახილის ნიშნის სიმბოლო"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"დიდი შავი გული"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"მკვეთრი პლუსის ნიშანი"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"მკვეთრი მინუსის ნიშანი"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"მკვეთრი გაყოფის ნიშანი"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"შავი მარჯვნივ მიმართული ისარი"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"მარყუჟი"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"ორმაგი მარყუჟი"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"მარჯვნივ მიმართული და ზევით მოხვეული ისარი"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"მარჯვნივ მიმართული და ქვევით მოხვეული ისარი"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"მარცხნივ მიმართული შავი ისარი"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"ზევით მიმართული შავი ისარი"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"მარცხნივ ქვევით მიმართული შავი ისარი"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"დიდი შავი კვადრატი"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"დიდი თეთრი კვადრატი"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"თეთრი საშუალო ვარსკვლავი"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"მკვეთრი დიდი წრე"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"ტალღოვანი ტირე"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"იორიტენი"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"მილოცვის იდეოგრაფი წრეში"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"საიდუმლოების იდეოგრაფი წრეში"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Mahjong მოზაიკის წითელი დრაკონი"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"ბანქოს შავი ჯოკერი"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"სისხლის ტიპი A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"სისხლის ტიპი B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"სისხლის ტიპი O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"პარკინგის ადგილი"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"სისხლის ტიპი AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"CL კვადრატში"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"cool კვადრატში"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"free კვადრატში"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"ID კვადრატში"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"new კვადრატში"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"N G კვადრატში"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"OK კვადრატში"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"SOS კვადრატში"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"up ძახილის ნიშნით კვადრატში"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"vs კვადრატში"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"katakana koko კვადრატში"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"katakana sa კვადრატში"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"CJK უნიფიცირებული იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"დაჯავშნილი ადგილის იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"პროჰიბიციის იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"ვაკანსიის იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"მიღების იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"დაკავებულობის იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"იდეოგრაფი გადახდილია კვადრატში"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"იდეოგრაფი ყოველთვიურად კვადრატში"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"იდეოგრაფი აპლიკაცია კვადრატში"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"ფასდაკლების იდეოგრაფი კვადრატში"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"იდეოგრაფი მუშაობს კვადრატში"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"უპირატესობის იდეოგრაფი წრეში"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"მიღების იდეოგრაფი წრეში"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"ციკლონი"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"ნისლიანი"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"დახურული ქოლგა"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"ვარსკვლავიანი ღამე"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"მზის ამოსვლა მთებზე"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"მზის ამოსვლა"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"ქალაქის პეიზაჟი შებინდებისას"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"მზის ჩასვლა შენობების ფონზე"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"ცისარტყელა"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"ხიდი ღამით"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"წყლის ტალღა"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"ვულკანი"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"ირმის ნახტომი"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"გლობუსი ევროპა-აფრიკა"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"გლობუსი ამერიკა"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"გლობუსი აზია-ავსტრალია"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"გლობუსი მერიდიანებით"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"ახალი მთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"შევსებადი ნახევარმთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"პირველი მეოთხედის მთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"მილევადი ნახევარმთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"სავსე მთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"შევსებადი კუზიანი მთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"ბოლო მეოთხედის მთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"შევსებადი ნახევარმთვარის სიმბოლო"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"ნახევარმთვარე"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"ახალი მთვარი სახით"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"პირველი მეოთხედის მთვარე სახით"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"ბოლო მეოთხედის მთვარე სახით"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"სავსე მთვარე სახით"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"მზე სახით"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"მოკიაფე ვარსკვლავი"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"ჩამოვარდნილი ვარსკვლავი"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"წაბლი"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"ნერგი"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"მარადმწვანე ხე"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"ფოთლოვანი ხე"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"პალმის ხე"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"კაქტუსი"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"ტიტა"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"აყვავებული ალუბალი"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"ვარდი"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"ჰიბისკუსი"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"მზესუმზირა"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"ყვავილობა"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"სიმინდის ტარო"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"ბრინჯის თავთავი"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"ბალახი"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"ოთხფოთლიანი სამყურა"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"ნეკერჩხლის ფოთოლი"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"ჩამოვარდნილი ფოთოლი"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"ქარში მოფრიალე ფოთოლი"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"სოკო"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"პომიდორი"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"ბადრიჯანი"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"ყურძენი"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"ნესვი"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"საზამთრო"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"მანდარინი"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"ლიმონი"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"ბანანი"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"ანანასი"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"წითელი ვაშლი"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"მწვანე ვაშლი"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"მსხალი"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"ატამი"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"ალუბალი"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"მარწყვი"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"ჰამბურგერი"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"პიცას ნაჭერი"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"ხორცი ძვალზე"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"ქათმის ბარკალი"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"ბრინჯის კრეკერი"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"ბრინჯის გუნდა"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"მოხარშული ბრინჯი"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"კარი და ბრინჯი"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"ორთქლიანი თასი"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"სპაგეტი"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"პური"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"კარტოფილი ფრი"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"შემწვარი ტკბილი კარტოფილი"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"დანგო"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"ოდენი"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"სუში"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"შემწვარი კრევეტი"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"ტორტი ლოკოკინის დიზაინით"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"ჩამოსასხმელი ნაყინი"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"გაცრილი ყინული"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"ნაყინი"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"დონატი"</string> + <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_1F36E" msgid="4630541402785165902">"ნადუღის კრემი"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"თაფლის ქოთანი"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ფხვიერი ნამცხვარი"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"ბენტოს ყუთი"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"საკვების თასი"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"სამზარეულო"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"დანა-ჩანგალი"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"ჩაის"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"საკეს ბოთლი და ჭიქა"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"ღვინის ჭიქა"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"კოქტეილის ჭიქა"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"ტროპიკული სასმელი"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"ლუდის კათხა"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"ლუდის კათხების მიჭახუნება"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"ჩვილის ბოთლი"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"ლენტი"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"შეფუთული საჩუქარი"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"დაბადების დღის ტორტი"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"ჯეკის ფარანი"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"ნაძვის ხე"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"თოვლის ბაბუა"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"ფეიერვერკი"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"ფეიერვერკის შუშხუნი"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"ბუშტი"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"სადღესასწაულო სატკაცუნო"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"კონფეტის ბურთი"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"ტანაბატას ტოტი"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"გადაჯვარედინებული დროშები"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"ფიჭვის დეკორაცია"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"იაპონური სათამაშოები"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"კარპას ალამი"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"ქარის ზარი"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"მთვარის ჭვრეტის ცერემონიალი"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"სკოლის ჩანთა"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"ოქსფორდის ქუდი"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"კარუსელის ცხენი"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"ეშმაკის ბორბალი"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"როლერკოსტერი"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"თევზი და ანკესი"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"მიკროფონი"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"კინოკამერა"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"კინოთეატრი"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"ყურსასმენი"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"მხატვრის პალიტრა"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"ცილინდრი"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"ცირკის კარავი"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"ბილეთი"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"კინოს ტკაცუნა"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"საშემსრულებლო ხელოვნება"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"ვიდეოთამაში"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"მიზანში გარტყმა"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"სლოტ-ავტომატი"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"ბილიარდი"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"კამათელი"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"ბოულინგი"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"ჰანაფუდა"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"მუსიკალური ნოტი"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"მუსიკალური ნოტები"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"საქსოფონი"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"გიტარა"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"კლავიშებიანი მუსიკალური ინსტრუმენტი"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"საყვირი"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"ვიოლინო"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"მუსიკალური ნოტები"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"სარბენი მაისური ლენტით"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"ტენისის ჩოგანი და ბურთი"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"თხილამური და ჩექმა"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"ბურთი და კალათა"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"კუბოკრული დროშა"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"სნოუბორდერი"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"მორბენალი"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"სერფერი"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"თასი"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"ცხენების დოღი"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"ამერიკული ფეხბურთის ბურთი"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"რაგბის ბურთი"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"მოცურავე"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"სახლის შენება"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"სახლი ბაღით"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"საოფისე შენობა"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"იაპონური ფოსტა"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"ევროპული ფოსტა"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"საავადმყოფო"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"ბანკი"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"ბანკომატი"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"სასტუმრო"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"სიყვარულის სასტუმრო"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"სადღეღამისო მაღაზია"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"სკოლა"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"უნივერსალური მაღაზია"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"ქარხანა"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"იზაკაიას ფარანი"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"იაპონური ციხე-სიმაგრე"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"ევროპული ციხე-სიმაგრე"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"ვირთხა"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"თაგვი"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"ხარი"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"აზიური კამეჩი"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"ძროხა"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"ლეოპარდი"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"კურდღელი"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"კატა"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"დრაკონი"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"ნიანგი"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"ვეშაპი"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"ლოკოკინა"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"გველი"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"ცხენი"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"ვერძი"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"თხა"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"ცხვარი"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"მაიმუნი"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"მამალი"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"ქათამი"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"ძაღლი"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"ღორი"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"დათვი"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"სპილო"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"რვაფეხა"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"სპირალური ნიჟარა"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"ხოჭო"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"ჭიანჭველა"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"ფუტკარი"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"ჭიამაია"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"თევზი"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"ტროპიკული თევზი"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"ზღარბთევზა"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"კუ"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"წიწილა და ნაჭუჭი"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"წიწილა"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"წიწილას სახე"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"ჩიტი"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"პინგვინი"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"კოალა"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"პუდელი"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"ცალკუზა აქლემი"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"ორკუზა აქლემი"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"დელფინი"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"თაგვის სახე"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"ძროხის სახე"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"ვეფხვის სახე"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"კურდღლის სახე"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"კატის სახე"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"დრაკონის სახე"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"მჩქეფარე ვეშაპი"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"ცხენის სახე"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"მაიმუნის სახე"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"ძაღლის სახე"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"ღორის სახე"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"ბაყაყის სახე"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"ზაზუნას სახე"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"მგლის სახე"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"დათვის სახე"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"პანდას სახე"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"ღორის ცხვირი"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"თათების კვალი"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"თვალები"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"ყური"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"ცხვირი"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"პირი"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"ენა"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"თეთრი ზევით მიმართული შეტრიალებული საჩვენებელი"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"თეთრი ქვევით მიმართული შეტრიალებული საჩვენებელი"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"თეთრი მარცხნივ მიმართული შეტრიალებული საჩვენებელი"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"თეთრი მარჯვნივ მიმართული შეტრიალებული საჩვენებელი"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"მომუშტული ხელი"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"ხელის დაქნევა"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"ხელის ნიშანი OK"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"მაღლა აწეული ცერი"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"დაბლა დაწეული ცერი"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"ტაშის დაკვრა"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"ღია ხელები"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"გვირგვინი"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"ქალის ქუდი"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"სათვალეები"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"ჰალსტუხი"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"მაისური"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"ჯინსი"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"კაბა"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"კიმონო"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"ბიკინი"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"ქალის ტანსაცმელი"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"საფულე"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"ხელჩანთა"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"ქისა"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"კაცის ფეხსაცმელი"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"სპორტული ფეხსაცმელი"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"მაღალქუსლიანი ფეხსაცმელი"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"ქალის სანდალი"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"ქალის ჩექმა"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"ფეხის კვალი"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"ბიუსტი სილუეტში"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"ბიუსეტები სილუეტში"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"ბიჭი"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"გოგო"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"კაცი"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"ქალი"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"ოჯახი"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"ხელჩაკიდებული ქალი და კაცი"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"ხელჩაკიდებული ორი კაცი"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"ხელჩაკიდებული ორი ქალი"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"პოლიციელი"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"ქალი კურდღის ყურებით"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"პატარძალი პირბადით"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"ქერათმიანი ადამიანი"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"კაცი გუა-პი-მაოს ქუდით"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"კაცი ჩალმით"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"ხანდაზმული ადამიანი"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"ხანდაზმული ქალი"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"ჩვილი"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"მშენებელი"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"პრინცესა"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"იაპონელი კაციჭამია"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"იაპონელი გობლინი"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"მოჩვენება"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"ჩვილი ანგელოზი"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"უცხოპლანეტელი"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"უცხოპლანეტელი მონსტრი"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"ჭინკა"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"თავის ქალა"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"ინფორმაციის პუნქტის თანამშრომელი"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"მცველი"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"მოცეკვავე"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"პომადა"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"მანიკური"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"სახის მასაჟი"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"ვარცხნილობა"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"დალაქის ბოძი"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"შპრიცი"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"აბი"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"კოცნის ნიშანი"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"სასიყვარულო წერილი"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"დარეკვა"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"ძვირფასი ქვა"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"კოცნა"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"ბუკეტი"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"წყვილი გულით"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"ქორწილი"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"მფეთქავი გული"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"გატეხილი გული"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"ორი გული"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"მღელვარე გული"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"დიდი გული"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"გული ისრით"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"ლურჯი გული"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"მწვანე გული"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"ყვითელი გული"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"მეწამული გული"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"გული ლენტით"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"მბრუნავი გულები"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"გულის დეკორაცია"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"ალმასის ფორმა შიგნით წერტილით"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"ნათურა"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"სიბრაზის სიმბოლო"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"ბომბი"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"ძილის სიმბოლო"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"დაჯახების სიმბოლო"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"დაღვრილი ოფლის სიმბოლო"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"წვეთი"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"შხაპის სიმბოლო"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"განავალი"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"დაჭიმული კუნთი"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"თავბრუსხვევის სიმბოლო"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"დიალოგის ბუშტი"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"ფიქრის ბუშტი"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"თეთრი ყვავილი"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"ასქულიანი სიმბოლო"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"ფულის ჩანთა"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"ვალუტის გაცვლა"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"დოლარის მუქი ნიშანი"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"საკრედიტო ბარათი"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"ბანკნოტი იენის ნიშნით"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"ბანკნოტი დოლარის ნიშნით"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"ბანკნოტი ევროს ნიშნით"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"ბანკნოტი ფუნტის ნიშნით"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"ფრთიანი ფული"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"მატებადი დიაგრამა იენის ნიშნით"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"ადგილი"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"პერსონალური კომპიუტერი"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"ქეისი"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Minidisc"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Floppy დიკსი"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"ოპტიკური დისკი"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"ფაილის საქაღალდე"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"ღია საქაღალდე"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"მოღუნული ქაღალდი"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"გვერდი პირაღმა"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"კალენდარი"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"მოსახევი კალენდარი"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"ბარათების ინდექსი"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"მატებადი დიაგრამა"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"კლებადი დიაგრამა"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"სვეტებიანი დიაგრამა"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"ფურცლის სამაგრი დაფა"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"საკანცელარიო ჭიკარტი"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"მომრგვალებული ჭიკარტი"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"ფურცლის საკინძი"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"სწორი სახაზავი"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"სამკუთხა სახაზავი"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"სანიშნის ყურები"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"დავთარი"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"რვეული"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"რვეული დეკორატიული ყდით"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"დახურული წიგნი"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"ღია წიგნი"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"მწვანე წიგნი"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"ლურჯი წიგნი"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"ნარინჯისფერი წიგნი"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"წიგნები"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"ბეჯი"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"გრაგნილი"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"ჩანაწერი"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"ყურმილი"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"პეიჯერი"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"ფაქსის აპარატი"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"სატელიტური ანტენა"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"რუპორი"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"გამამხნევებელი მეგაფონი"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"გამავალ წერილთა სათავსო"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"შემოსულების სათავსო"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"ამანათი"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"ელფოსტის სიმბოლო"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"შემომავალი კონვერტი"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"კონვერტი ქვედა ისრით ზევით"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"დახურული საფოსტო ყუთი დაშვებული ალმით"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"დახურული საფოსტო ყუთი აღმართული ალმით"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"ღია საფოსტო ყუთი აღმართული ალმით"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"ღია საფოსტო ყუთი დაშვებული ალმით"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"საფოსტო ყუთი"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"საფოსტო საყვირი"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"გაზეთი"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"მობილური ტელეფონი"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"მობილური ტელეფონი მარჯვნივ მიმართული ისრით მარცხნივ"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"ვიბრაციის რეჟიმი"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"მობილური ტელეფონის გამორთვა"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"მობილური ტელეფონები აკრძალულია"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"ანტენა სვეტებით"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"ფოტოაპარატი"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"ვიდეოკამერა"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"ტელევიზია"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"რადიო"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"ვიდეოკასეტა"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"გადაჯვარედინებული მარჯვნივ მიმართული ისრები"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"საათის მიმართულებით მარჯვენა და მარცხენა ღია წრიული ისრები"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"საათის მიმართულებით მარჯვენა და მარცხენა ღია წრიული ისრები, ერთიანით წრეში"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"საათის მიმართულებით ქვედა და ზედა ღია წრიული ისრები"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"საათის საწინააღმდეგო მიმართულებით ქვედა და ზედა ღია წრიული ისრები"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"დაბალი სიკაშკაშის სიმბოლო"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"მაღალი სიკაშკაშის სიმბოლო"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"ხაზგადასმული სპიკერი"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"სპიკერი"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"სპიკერი ხმის ერთი ტალღით"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"სპიკერი ხმის სამი ტალღით"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"ბატარეა"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"ელექტრონული შესაერთებელი"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"მარცხნივ მიმართული გამადიდებელი ლუპა"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"მარჯვნივ მიმართული გამადიდებელი ლუპა"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"საკეტი მელნის კალმით"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"დახურული საკეტი გასაღებით"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"გასაღები"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"საკეტი"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"ღია საკეტი"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"ზარი"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"ხაზგადასმული ზარი"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"სანიშნე"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"ბმულის სიმბოლო"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"არჩევანის ღილაკი"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Back მარცხენა ისრით ზევით"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"End მარცხენა ისრით ზევით"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"On ძახილის ნიშნით, მარცხენა მარჯვენა ისრით ზევით"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Soon მარჯვენა ისრით ზევით"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Top ზედა ისრით ზევით"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"18 წლამდე აკრძალვის სიმბოლო"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"კლავიში ათი"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"ლათინური დიდი ასოების შეყვანის სიმბოლო"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"ლათინური პატარა ასოების შეყვანის სიმბოლო"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"რიცხვების შეყვანის სიმბოლო"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"სიმბოლოთა შეყვანის სიმბოლო"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"ლათინური ასოების შეყვანის სიმბოლო"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"ხანძარი"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"ელექტრო ლამპარი"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"ქანჩი"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"ჩაქუჩი"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"თხილი და სამტვრევი"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"ჰოჩოს დანა"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"პისტოლეტი"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"მიკროსკოპი"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"ტელესკოპი"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"კრისტალის ბურთი"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"ექვსქიმა ვარსკვლავი შუა წერტილით"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"დამწყების იაპონური სიმბოლო"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"სამკაპის ემბლემა"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"შავი კვადრატული ღილაკი"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"თეთრი კვადრატული ღილაკი"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"დიდი წითელი წრე"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"დიდი ლურჯი წრე"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"დიდი ნარინჯისფერი ალმასი"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"დიდი ლურჯი ალმასი"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"პატარა ნარინჯისფერი ალმასი"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"პატარა ლურჯი ალმასი"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"ზევით მიმართული წითელი სამკუთხედი"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"ქვევით მიმართული წითელი სამკუთხედი"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"ზევით მიმართული პატარა წითელი სამკუთხედი"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"ქვევით მიმართული პატარა წითელი სამკუთხედი"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"საათი, რომელიც აჩვენებს პირველ საათს"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"საათი, რომელიც აჩვენებს ორ საათს"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"საათი, რომელიც აჩვენებს სამ საათს"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"საათი, რომელიც აჩვენებს ოთხ საათს"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"საათი, რომელიც აჩვენებს ხუთ საათს"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"საათი, რომელიც აჩვენებს ექვს საათს"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"საათი, რომელიც აჩვენებს შვიდ საათს"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"საათი, რომელიც აჩვენებს რვა საათს"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"საათი, რომელიც აჩვენებს ცხრა საათს"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"საათი, რომელიც აჩვენებს ათ საათს"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"საათი, რომელიც აჩვენებს თერთმეტ საათს"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"საათი, რომელიც აჩვენებს თორმეტ საათს"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"საათი, რომელიც აჩვენებს ორის ნახევარს"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"საათი, რომელიც აჩვენებს სამის ნახევარს"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"საათი, რომელიც აჩვენებს ოთხის ნახევარს"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"საათი, რომელიც აჩვენებს ხუთის ნახევარს"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"საათი, რომელიც აჩვენებს ექვსის ნახევარს"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"საათი, რომელიც აჩვენებს შვიდის ნახევარს"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"საათი, რომელიც აჩვენებს რვის ნახევარს"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"საათი, რომელიც აჩვენებს ცხრის ნახევარს"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"საათი, რომელიც აჩვენებს ათის ნახევარს"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"საათი, რომელიც აჩვენებს თერთმეტის ნახევარს"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"საათი, რომელიც აჩვენებს თორმეტის ნახევარს"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"საათი, რომელიც აჩვენებს პირველის ნახევარს"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"ფუძიამა"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"ტოკიოს ანძა"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"თავისუფლების ქანდაკება"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"იაპონიის სილუეტი"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"მოიანის ქვის ქანდაკება"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"სასტიკი სახე"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"სასტიკი სახე მომღიმარი თვალებით"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"სახე სიხარულის ცრემლებით"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"მომღიმარი პირდაღებული სახე"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"მომღიმარი სახე, ღია პირით და მოცინარი თვალებით"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"მომღიმარი სახე, ღია პირით და ცივი ოფლით"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"მომღიმარი სახე, ღია პირით და კარგად დახუჭული თვალებით"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"მომღიმარი სახე მნათი წრით"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"მომღიმარი სახე რქებით"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"თვალის ჩაკვრა"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"მომღიმარი სახე მომღიმარი თვალებით"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"გემრიელი საჭმლის დამაგემოვნებელი სახე"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"შებამოგვრილი სახე"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"მომღიმარი სახე გულის ფორმის თვალებით"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"მომღიმარი სახე მზის სათვალეებით"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"თავმომწონე სახე"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"ნეიტრალური სახე"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"უემოციო სახე"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"უკმაყოფილო სახე"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"სახე ცივი ოფლით"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"ჩაფიქრებული სახე"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"დაბნეული სახე"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"შეცბუნებული სახე"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"კოცნის სახე"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"კოცნის გაგზავნის სახე"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"კოცნის სახე მომღიმარი თვალებით"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"კოცნის სახე დახუჭული თვალებით"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"ენაგამოყოფილი სახე"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"ენაგამოყოფილი სახე თვალის ჩაკვრით"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"ენაგამოყოფილი სახე მჭიდროდ დახუჭული თვალებით"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"იმედგაცრუებული სახე"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"შეწუხებული სახე"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"ბრაზიანი სახე"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"გაბუტული სახე"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"მტირალი სახე"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"შეუპოვარი სახე"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"ტრიუმფალური სახე"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"იმედგაცრუებული მაგრამ შვებამოგვრილი სახე"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"წარბშეჭმუხნული პირდაღებული სახე"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"გატანჯული სახე"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"შიშნარევი სახე"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"დაქანცული სახე"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"მომძინარე სახე"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"დაღლილი სახე"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"დამანჭული სახე"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"ხმამაღლა მტირალი სახე"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"პირდაღებული სახე"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"გაჩუმებული სახე"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"სახე ღია პირით და ცივი ოფლით"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"შიშისგან მყვირალი სახე"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"გაოცებული სახე"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"წამოწითლებული სახე"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"მძინარი სახე"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"თავბრუდახვეული სახე"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"უპირო სახე"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"სახე სამედიცინო ნიღბით"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"სასტიკი კატის სახე მომღიმარი თვალებით"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"კატის სახე სიხარულის ცრემლებით"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"მომღიმარი კატის პირდაღებული სახე"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"მომღიმარი კატის სახე გულის ფორმის თვალებით"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"კატის სახე ირონიული ღიმილით"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"კოცნის სახე დახუჭული თვალებით"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"გაბუტული კატის სახე"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"მტირალი კატის სახე"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"დაქანცული კატის სახე"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"სახე უვარგისობის ჟესტით"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"სახე ok ჟესტით"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"ადამიანი თავის დაკვრით"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"არც-თუ-ისე ეშმაკი მაიმუნი"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"არ-მესმის მაიმუნი"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"არ ვლაპარაკობ მაიმუნი"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"ბედნიერი, ხელაწეული ადამიანი"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"სიხარულით ხელაპყრობილი ადამიანი"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"წარბშეკრული ადამიანი"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"ადამიანი გაბუტული სახით"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"ადამიანი დაწყობილი ხელებით"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"რაკეტა"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"ვერტმფრენი"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"ორთქლის ლოკომოტივი"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"რკინიგზის ვაგონი"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"ჩქაროსნული მატარებელი"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"ჩქაროსნული მატარებელი ტყვიის წვერით"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"მატარებელი"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"მეტრო"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"მსუბუქი რკინიგზა"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"სადგური"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"ტრამვაი"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"ტრამვაის ვაგონი"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"ავტობუსი"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"მომავალი ავტობუსი"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"ტროლეიბუსი"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"ავტობუსის გაჩერება"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"მიკროავტობუსი"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"სასწრაფო"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"სახანძრო"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"პოლიციის მანქანა"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"მომავალი პოლიციის მანქანა"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"ტაქსი"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"მომავალი ტაქსი"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"ავტომობილი"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"მომავალი ავტომობილი"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"რეკრეაციული ავტომანქანა"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"სატვირთო მანქანა"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"გადაბმული სატვირთო"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"ტრაქტორი"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"მონორელსი"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"მთის რკინიგზა"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"შეჩერებული რკინიგზა"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"მთის საბაგირო"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"საბაგირო"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"გემი"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"სანიჩბოსნო ნავი"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"სწრაფმავალი მოტორიანი გემი"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"ჰორიზონტალური შუქნიშანი"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"ვერტიკალური შუქნიშანი"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"მშენებლობის ნიშანი"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"პოლიციის მანქანა შუქის სიგნალით"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"სამკუთხა ალამი პოსტზე"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"კარი"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"შესვლის აკრძალვის ნიშანი"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"მოწევის ნიშანი"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"მოწევის აკრძალვის ნიშანი"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"ნაგვის გამოყოფილ ადგილზე ჩაყრის სიმბოლო"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"სიმბოლო ნაგავი არ დაყაროთ"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"დასალევად ვარგისი წყლის სიმბოლო"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"დასალევად უვარგისი წყლის სიმბოლო"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"ველოსიპედი"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"ველოსიპედები არ არის"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"ველოსიპედისტი"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"სამთო ველოსიპედისტი"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"ფეხით მოსიარულე"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"ფეხით სიარული აკრძალულია"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"ბავშვების გადასასვლელი"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"კაცების ნიშანი"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"ქალების ნიშანი"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"საპირფარეშო"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"ჩვილების ნიშანი"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"ტუალეტი"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"უნიტაზი"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"შხაპი"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"სააბაზანო"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"აბაზანა"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"პასპორტის კონტროლი"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"საბაჟო"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"ბარგის აღება"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"ბარგის ჩაბარება"</string> +</resources> diff --git a/java/res/values-ka-rGE/strings-talkback-descriptions.xml b/java/res/values-ka-rGE/strings-talkback-descriptions.xml index c4fd65939..0cf7e7ce4 100644 --- a/java/res/values-ka-rGE/strings-talkback-descriptions.xml +++ b/java/res/values-ka-rGE/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"ტექსტი შეყვანილი არ არის"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> შეასწორებს <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>-ს <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>-ად"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ასრულებს ავტოკორექციას"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"კლავიატურის კოდი %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift ჩართულია (შეეხეთ გამოსართავად)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"ჩართულია Caps (შეეხეთ გამოსართავად)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"დამატებითი სიმბოლოები"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"სიმბოლოები"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"წაშლა"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"სიმბოლოები"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"ასოები"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"წინა"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift ჩართულია"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"ჩართულია Caps"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift გამორთულია"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"სიმბოლოების რეჟიმი"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"დამატებითი სიმბოლოების რეჟიმი"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"ასოების რეჟიმი"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"ტელეფონის რეჟიმი"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"ტელეფონის სიმბოლოების რეჟიმი"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"ადგილები"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"სიმბოლოები"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"სიცილაკები"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index 547bf29ca..3360c6209 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"შეყვანის მეთოდები"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"კვლევის აღრიცხვის ბრძანებები"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"კონტაქტებში ძებნა"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"კონტაქტების სიის გამოყენება მართლწერის შემოწმებისას"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"ვიბრაცია კლავიშზე დაჭერისას"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"კონტაქტის სახელების შეთავაზება"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"კონტაქტებიდან სახელების გამოყენება შეთავაზებებისთვის და კორექციისთვის"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"პერსონალიზებული შეთავაზებები"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"წერტილი ორმაგი შორისით"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"შორისზე ორჯერ შეხება დაწერს წერტილს და შორისის სიმბოლოს"</string> <string name="auto_cap" msgid="1719746674854628252">"ავტო-კაპიტალიზაცია"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"შეყვანის ენები"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"შეეხეთ ისევ შესანახად"</string> <string name="has_dictionary" msgid="6071847973466625007">"ხელმისაწვდომია ლექსიკონი"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"მომხმარებლის უკუკავშირის ჩართვა"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"შეიტანეთ წვლილი შეყვანის ამ მეთოდის გაუმჯობესებაში — გააგზავნეთ მოხმარების სტატისტიკა და ავარიული გათიშვების ანგარიშები"</string> <string name="keyboard_layout" msgid="8451164783510487501">"კლავიატურის თემა"</string> <string name="subtype_en_GB" msgid="88170601942311355">"ინგლისური (გართ. სამ.)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"ინგლისური (აშშ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"ჩართვა"</string> <string name="not_now" msgid="6172462888202790482">"ახლა არა"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"შეყვანის იგივე სტილი უკვე არსებობს: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"გამოყენებადობის კვლევის რეჟიმი"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"კლავიშზე გრძელი დაჭერის დაყოვნება"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"კლავიშზე დაჭერის ვიბრაციის ხანგრძლივობა"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"კლავიშზე დაჭერის ხმა"</string> diff --git a/java/res/values-kk/strings-talkback-descriptions.xml b/java/res/values-kk/strings-talkback-descriptions.xml index 13adf830d..39388a4d7 100644 --- a/java/res/values-kk/strings-talkback-descriptions.xml +++ b/java/res/values-kk/strings-talkback-descriptions.xml @@ -27,7 +27,8 @@ <skip /> <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) --> <skip /> - <string name="spoken_description_unknown" msgid="3197434010402179157">"Перне коды %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string> <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift қосулы (өшіру үшін түрту)"</string> <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock қосулы (өшіру үшін түрту)"</string> diff --git a/java/res/values-kk/strings.xml b/java/res/values-kk/strings.xml index 83ac0daf7..29780513c 100644 --- a/java/res/values-kk/strings.xml +++ b/java/res/values-kk/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Енгізу опциялары"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Журнал пәрмендерін зерттеу"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Контакт аттарын іздеу"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Емлені тексеру құралы контактілер тізімінің жазбаларын пайдаланады"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Пернені басқан кездегі діріл"</string> @@ -82,8 +81,6 @@ <string name="select_language" msgid="3693815588777926848">"Енгізу тілдері"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Сақтау үшін қайта түртіңіз"</string> <string name="has_dictionary" msgid="6071847973466625007">"Сөздік қолжетімді"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Пайдаланушының кері байланысын қосу"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Қолданыс статистикасын және бұзылыс есептерін автоматты түрде жіберу арқылы осы енгізу әдісінің редакторын арттыруға көмектесу"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Пернетақта тақырыбы"</string> <string name="subtype_en_GB" msgid="88170601942311355">"ағылшын (ҰБ)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"ағылшын (АҚШ)"</string> @@ -113,7 +110,6 @@ <string name="enable" msgid="5031294444630523247">"Қосу"</string> <string name="not_now" msgid="6172462888202790482">"Қазір емес"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Бірдей енгізу стилі бұрыннан бар: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Қолайлылықты зерттеу режимі"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Пернені ұзақ басу кідірісі"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Пернені басқан кездегі діріл ұзақтығы"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Пернені басқан кездегі дыбыс деңгейі"</string> diff --git a/java/res/values-km-rKH/strings-action-keys.xml b/java/res/values-km-rKH/strings-action-keys.xml index ff747d9f5..d6b11b7ce 100644 --- a/java/res/values-km-rKH/strings-action-keys.xml +++ b/java/res/values-km-rKH/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"មុន"</string> <string name="label_done_key" msgid="7564866296502630852">"រួចរាល់"</string> <string name="label_send_key" msgid="482252074224462163">"ផ្ញើ"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"ផ្អាក"</string> <string name="label_wait_key" msgid="5891247853595466039">"រង់ចាំ"</string> </resources> diff --git a/java/res/values-km-rKH/strings-emoji-descriptions.xml b/java/res/values-km-rKH/strings-emoji-descriptions.xml new file mode 100644 index 000000000..757df50e7 --- /dev/null +++ b/java/res/values-km-rKH/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"សញ្ញារក្សាសិទ្ធ"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"សញ្ញាចុះបញ្ជី"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"សញ្ញាឧទានពីរ"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"សញ្ញាឧទានសញ្ញាសួរ"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"សញ្ញានិក្ខិត្តសញ្ញា"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"ប្រភពព័ត៌មាន"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"ព្រួញឆ្វេងស្ដាំ"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"ព្រួញឡើងលើចុះក្រោម"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"ព្រួញទិសពាយព្យ"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"ព្រួញទិសឥសាន្តឦសាន្ត"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"ព្រួញទិសអាគ្នេយ៍"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"ព្រួញទិសនិរតី"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"ព្រួញទៅឆ្វេងមានទំពក់"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"ព្រួញទៅស្ដាំមានទំពក់"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"នាឡិកាដៃ"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"នាឡិកាពិសោធន៍"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"ត្រីកោណខ្មៅពីរចង្អុលទៅស្ដាំ"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"ត្រីកោណខ្មៅពីរចង្អុលទៅឆ្វេង"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"ត្រីកោណខ្មៅពីរចង្អុលឡើងលើ"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"ត្រីកោណខ្មៅពីរចង្អុលចុះក្រោម"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"នាឡិការោទ៍"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"កែវពិសោធន៍មានខ្សាច់ហូរ"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"អក្សរអឹមធំក្នុងរង្វង់"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"ការ៉េតូចពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"ការ៉េតូចពណ៌ស"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"ត្រីកោណចង្អុលស្ដាំពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"ត្រីកោណចង្អុលឆ្វេងពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"ការ៉េមធ្យមពណ៌ស"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"ការ៉េមធ្យមពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"ការ៉េតូចមធ្យមពណ៌ស"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"ការ៉េតូចមធ្យមពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"ព្រះអាទិត្យពណ៌ខ្មៅបញ្ចេញពន្លឺ"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"ពពក"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"ទូរស័ព្ទពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"ប្រអប់មានសញ្ញាធីក"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"ឆត្រមានតំណក់ទឹកភ្លៀង"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"ភេសជ្ជៈក្ដៅ"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"ចង្អុលដៃចង្អុលឡើងពណ៌ស"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"មុខញញឹមពណ៌ស"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"មេសៈរាសី"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"តៅរ៉ូស"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"ហ្គិមីនី"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"សញ្ញាទី៤នៃរាសីចក្រ"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"លីអូ"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"សញ្ញារាសី"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"លីប្រា"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"រាសីវច្ចៈ"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"រាសីធ្នូ"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"រាសីសេះ"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Aquarius"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"រាសីអាប់អួ"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"ឈុតប៊ិចពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"ឈុតជួងពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"ឈុតកឺពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"ឈុតការ៉ូពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Hot springs"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"និមិត្តសញ្ញាធុងសំរាមសកលពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"និមិត្តសញ្ញារទេះរុញ"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"យុថ្កា"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"សញ្ញាព្រមាន"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"សញ្ញាតង់ស្យុងខ្ពស់"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"រង្វង់ពណ៌សមធ្យម"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"រង្វង់ពណ៌ខ្មៅមធ្យម"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"បាល់ទាត់"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"បាល់បោះ"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"អ្នកលេងព្រិលដោយគ្មានព្រិល"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"ព្រះអាទិត្យនៅក្រោយពពក"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"សញ្ញា (⛎)"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"ហាមចូល"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"វិហារសាសនា"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"បាញ់ទឹក"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"ទង់ជាតិក្នុងរន្ធ"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"ទូកក្ដោង"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"តង់"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"បូមប្រេង"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"កន្ត្រៃពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"សញ្ញាធីកពណ៌សធំ"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"យន្តហោះ"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"ស្រោមសំបុត្រ"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"លើកកណ្ដាប់ដៃ"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"លើកដៃ"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"លើកដៃជ័យជម្នះ"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"ខ្មៅដៃ"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"ចុងខ្មៅដៃ"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"សញ្ញាធីកដិត"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"សញ្ញាគុណដិត"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"បញ្ចេញពន្លឺ"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"ផ្កាយប្រាំបីជ្រុង"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"ផ្កាយប្រាំបីជ្រុងដិត"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"សញ្ញា (❄)"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"បញ្ចេញពន្លឺ"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"សញ្ញាខ្វែង"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"សញ្ញាខ្វែងក្នុងប្រអប់"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"សញ្ញាសួរពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"សញ្ញាសួរពណ៌ស"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"សញ្ញាឧទានពណ៌ស"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"សញ្ញាឧទានពណ៌ខ្មៅដិត"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"បេះដូងពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"សញ្ញាបូកដិត"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"សញ្ញាដកដិត"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"សញ្ញាចែកដិត"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"ព្រួញទៅស្ដាំពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"រង្វង់អង្កាញ់"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"រង្វង់អង្កាញ់ពីរ"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"ព្រួញចង្អុលទៅស្ដាំបន្ទាប់មកបត់ឡើងលើ"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"ព្រួញចង្អុលទៅស្ដាំបន្ទាប់មកបត់ចុះក្រោម"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"ព្រួញពណ៌ខ្មៅទៅឆ្វេង"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"ព្រួញពណ៌ខ្មៅឡើងលើ"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"ព្រួញពណ៌ខ្មៅចុះក្រោម"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"ប្រអប់ការ៉េធំពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"ប្រអប់ការ៉េធំពណ៌ស"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"ផ្កាយមធ្យមពណ៌ស"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"រង្វង់ធំដិត"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"សញ្ញារលកដិត"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"សញ្ញា (〽)"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"សញ្ញា (㊗)"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"សញ្ញា (㊙)"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"សញ្ញា (🀄)"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"សញ្ញា (🃏)"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"ឈាមប្រភេទ A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"ឈាមប្រភេទ B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"ឈាមប្រភេទ O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"ចំណត"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"ឈាមប្រភេទ AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"ប្រអប់ CL"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"ប្រអប់ cool"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"ប្រអប់ free"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"ប្រអប់ ID"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"ប្រអប់ new"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"ប្រអប់ N G"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"ប្រអប់ OK"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"ប្រអប់ SOS"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"ប្រអប់ up!"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"ប្រអប់ vs"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"សញ្ញា (🈁)"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"សញ្ញា (🈂)"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"សញ្ញា (🈚)"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"សញ្ញា (🈯)"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"សញ្ញា (🈲)"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"សញ្ញា (🈳)"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"សញ្ញា (🈴)"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"សញ្ញា (🈵)"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"សញ្ញា (🈶)"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"សញ្ញា (🈷)"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"សញ្ញា (🈸)"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"សញ្ញា (🈹)"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"សញ្ញា (🈺)"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"សញ្ញា (🉐)"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"សញ្ញា (🉑)"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"ស៊ីក្លូន"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"អ័ភ្រ"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"ឆត្របិទ"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"រាត្រីមានផ្កាយ"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"ព្រះអាទិត្យលើភ្នំ"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"ព្រះអាទិត្យរះ"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"ទីក្រុងពេលព្រលប់"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"ព្រះអាទិត្យលិចចន្លោះអាគារ"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"ឥន្ទធនូ"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"ស្ពានពេលរាត្រី"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"ទឹករលក"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"ភ្នំភ្លើង"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"មេឃស្រទំ"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"ភូគោលអឺរ៉ុប-អាហ្រិក"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"ភូគោលអាមេរិក"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"ភូគោលអាស៊ី-អូស្ត្រាលី"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"ភូគោលមានខ្សែវណ្ឌ"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"ព្រះចន្ទ"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"ព្រះចន្ទមួយចំណិតស្ដាំ"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"ព្រះចន្ទពាក់កណ្ដាល"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"ព្រះចន្ទមួយចំណិតឆ្វេង"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"ព្រះចន្ទពេញវង់"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"ព្រះចន្ទភ្លឺមួយចំហៀង"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"ព្រះចន្ទភ្លឺពាក់កណ្ដាល"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"ព្រះចន្ទភ្លឺមួយចំណិតឆ្វេង"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"ព្រះចន្ទមួយចំណិត"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"ព្រះចន្ទមានមុខមនុស្ស"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"ព្រះចន្ទមានមុខមនុស្សពាក់កណ្ដាលស្ដាំ"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"ព្រះចន្ទមានមុខមនុស្សពាក់កណ្ដាលឆ្វេង"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"ព្រះចន្ទមានមុខមនុស្សពេញវង់"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"ព្រះអាទិត្យមានមុខមនុស្ស"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"ផ្កាយ"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"បាញ់ផ្កាយ"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"ឆេសណាត់"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"កូនឈើ"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"ដើមឈើបៃតង"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"ដើមឈើមិនខៀវជានិច្ច"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"ដើមត្នោត"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"ដំបងយក្ស"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"ផ្កា"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"ផ្កាឈឺរី"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"ផ្កាកុលាប"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"ផ្ការំយោល"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"ផ្កាឈូករ័ត្ន"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"ផ្កា"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"ស្នៀតពោត"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"គួរស្រូវ"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"រុក្ខជាតិ"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"ដើមដង្ហិត"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"ស្លឹកឈើ"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"ស្លឹកឈើជ្រុះ"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"ស្លឹកឈើបក់តាមខ្យល់"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"ផ្សិត"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"ប៉េងប៉ោះ"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"ត្រប់"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"ទំពាំងបាយជូរ"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"ត្រសក់ស្រូវ"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"ឪឡឹក"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"ក្រូចឃ្វិច"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"ក្រូចឆ្មារ"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"ចេក"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"ម្នាស់"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"ប៉ោមក្រហម"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"ប៉ោមបៃតង"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"ផ្លែព័រ"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"ផ្លែប៉ែស"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"ផ្លែឈឺរី"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"ផ្លែស្ត្របេរី"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"ហាំប៊ឺហ្គឺ"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"ភីហ្សាមួយស្លាយ"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"សាច់ចង្កាក់"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"ភ្លៅមាន់"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"ប៊ីស្គីបាយ"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"ដុំបាយ"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"បាយមួយចាន"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"ការី និងបាយ"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"ចានចំហុយ"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"ស្ប៉ាហ្គាទី"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"នំប៉័ង"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"ដំឡូងបំពង"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"ដំឡូងដុត"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"ប្រហិតដោតចង្កាក់"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"ម្ហូបសមុទ្រដោតចង្កាក់"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"ស៊ូស៊ី"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"កំពឹសចៀន"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"នំត្រីរាងមូល"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"ការ៉េមបំពង់"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"ការ៉េមកែវ"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"ការ៉េម"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"ដូណាត់"</string> + <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_1F36E" msgid="4630541402785165902">"សង់ខ្យា"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ថូ"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"នំខេក"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"ប្រអប់បេនតូ"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"ឆ្នាំងអាហារ"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"ចម្អិនអាហារ"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"សម និងកាំបិត"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"ពែងតែអត់ដៃកាន់"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"ដប និងពែង"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"កែវស្រា"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"កែវស្រាក្រឡុក"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"ភេសជ្ជៈត្រូពិក"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"កែវស្រាបៀ"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"ជល់កែវស្រាបៀ"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"ដបទឹកដោះគោ"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"ខ្សែបូ"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"ខ្ចប់កាដូរ"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"នំខួបកំណើត"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"គោម"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"ដើមឈើបុណ្យណូអែល"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"តាណូអែល"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"កាំជ្រួច"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"ពន្លឺកាំជ្រួច"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"ប៉េងប៉ោង"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"ឧបករណ៍ផ្ទុះពេលជប់លៀង"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"ក្រដាសពណ៌តូចៗ"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"ដើមតាណាប៉ាតា"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"ទង់ជាតិខ្វែង"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"ការតុបតែងដោយផ្លែស្រល់"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"តុក្កតាជប៉ុន"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"ខ្សែបូត្រីគល់រាំង"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"កណ្ដឹងខ្យល់"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"ពិធីមើលព្រះចន្ទ"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"កាបូបទៅសាលារៀន"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"មួកទទួលសញ្ញាប័ត្រ"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"សេះក្មេងជិះលេង"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"កន្ត្រកវិល"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"រថយន្តសម្រាប់ពាក់ជិះរំអិល"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"ដងសន្ទូច និងត្រី"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"មីក្រូហ្វូន"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"ម៉ាស៊ីនថតភាពយន្ត"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"ភាពយន្ត"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"កាស"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"ក្ដារលាយពណ៌វិចិត្រករ"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"មួកសម្ដែងសិល្បៈ"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"តង់សៀក"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"សំបុត្រ"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"បន្ទះកណ្ដឹង"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"សម្ដែងសិល្បៈ"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"ហ្គេមវីដេអូ"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"វាយចំគោលដៅ"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"ម៉ាស៊ីនដាក់កាក់"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"ល្បែងបុកប៊ីយ៉ា"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"ហ្គេមឡុកឡាក់"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"ប៊ូលីង"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"សន្លឹកបៀរមានផ្កា"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"ណោតតន្ត្រី"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"ណោតតន្ត្រីច្រើន"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"ត្រែ"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"ហ្គីតា"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"ខ្ទង់សម្រាប់ចុចតន្ត្រី"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"ត្រុំប៉ែត"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"វីយូឡុង"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"និមិត្តសញ្ញាតន្ត្រី"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"អាវកីឡាមានខ្សែឆៀង"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"រ៉ាកែត និងបាល់"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"ជិះស្គី និងក្ដារស្គី"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"បាល់បោះ និងវណ្ណមូល"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"ទង់ជាតិប្រណាំងម៉ូតូ"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"អ្នកជិះក្ដាររំអិលលើព្រិល"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"អ្នករត់"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"អ្នកជិះទូករអិលលើទឹក"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"ពានរង្វាន់"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"ប្រណាំងសេះ"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"បាល់ទាត់អាមេរិក"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"បាល់អោប"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"អ្នកហែលទឹក"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"ការសាងសង់ផ្ទះ"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"ផ្ទះមានសួនច្បារ"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"អគារការិយាល័យ"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"ការិយាល័យប្រៃសណីយ៍ជប៉ុន"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"ការិយាល័យប្រៃសណីយ៍អឺរ៉ុប"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"មន្ទីរពេទ្យ"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"ធនាគារ"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"ម៉ាស៊ីនអេធីអឹម"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"សណ្ឋាគារ"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"សណ្ឋាគារក្ដីស្រឡាញ់"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"ហាងទំនិញ ២៤ម៉ោង"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"សាលារៀន"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"ហាងទំនិញធំៗ"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"រោងចក្រ"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"ចង្កៀងគោម Izakaya"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"វិមានជប៉ុន"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"វិមានអឺរ៉ុប"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"កណ្ដុរប្រែង"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"កណ្ដុរប្រមះ"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"គោ"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"ក្របីទឹក"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"គោ"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"ខ្លារខិន"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"ទន្សាយ"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"ឆ្មា"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"នាគ"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"ក្រពើ"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"ត្រីបាឡែន"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"ខ្យង់ក្ដក់"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"ពស់"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"សេះ"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"ចៀមឈ្មោល"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"ពពែ"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"ចៀម"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"ស្វា"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"មាន់ឈ្មោល"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"មាន់"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"ឆ្កែ"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"ជ្រូក"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"ជ្រូកព្រៃ"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"ដំរី"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"មឹក"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"ខ្យងគ្រែង"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"សង្កើច"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"ស្រមោច"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"ឃ្មុំ"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"អណ្ដើកមាស"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"ត្រី"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"ត្រីតំបន់ត្រូពិក"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"ត្រីក្រពត"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"អណ្ដើក"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"កូនមាន់ញាស់"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"កូនមាន់"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"មុខកូនមាន់"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"បក្សី"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"ភេនឃ្វីន"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"ខ្លាឃ្មុំ"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"ឆ្កែ"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"អូដ្ឋបូកមួយ"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"អូដ្ឋបូកពីរ"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"ត្រីផ្សោត"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"មុខកណ្ដុរ"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"មុខគោ"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"មុខខ្លា"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"មុខទន្សាយ"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"មុខឆ្មា"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"មុខនាគ"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"ព្រុយត្រីបាឡែន"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"មុខសេះ"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"មុខស្វា"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"មុខឆ្កែ"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"មុខជ្រូក"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"មុខកង្កែប"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"មុខកណ្ដុរ"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"មុខចចក"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"មុខខ្លាឃ្មុំ"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"មុខខ្លាឃ្មុំផេនដា"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"ច្រមុះជ្រូក"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"ក្រញាំជើង"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"ភ្នែក"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"ត្រចៀក"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"ច្រមុះ"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"មាត់"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"អណ្ដាត"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"ចង្អុលដៃឡើងលើ"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"ចង្អុលដៃចុះក្រោម"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"ចង្អុលដៃទៅឆ្វេង"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"ចង្អុលដៃទៅស្ដាំ"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"កណ្ដាប់ដៃ"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"គ្រវីបាតដៃ"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"សញ្ញាអូខេ"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"មេដៃឡើងលើ"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"មេដៃចុះក្រោម"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"ទះដៃ"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"លាដៃ"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"មកុដ"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"មួកស្ត្រី"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"វ៉ែនតា"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"ក្រវ៉ាត់ករ"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"អាវយឺត"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"ខោខោវប៊យ"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"សំលៀកបំពាក់"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"គីម៉ូណូ"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"ឈុតហែលទឹក"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"សំលៀកបំពាក់នារី"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"កាបូប"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"កាបូបដៃ"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"ថង់"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"ស្បែកជើងបុរស"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"ស្បែកជើងកីឡា"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"ស្បែកជើងកែង"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"ស្បែកជើងស៊កស្រ្តី"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"ស្បែកជើងកវែងស្ត្រី"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"ដានជើង"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"គណនីភ្ញៀវ"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"គណនី"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"ក្មេងប្រុស"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"ក្មេងស្រី"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"បុរស"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"ស្ត្រី"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"គ្រួសារ"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"បុរស និងស្ត្រីកាន់ដៃគ្នា"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"បុរសពីរនាក់កាន់ដៃគ្នា"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"ស្ត្រីពីរកាន់ដៃគ្នា"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"មន្ត្រីប៉ូលិស"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"ស្ត្រីចងសក់ទីទុយ"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"កូនក្រមុំពាក់ស្បៃមុខ"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"មនុស្សមានសក់ពណ៌ទង់ដែង"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"បុរសពុកមាត់ឆ្មា"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"បុរសពាក់ឈ្នួត"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"បុរសចំណាស់"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"ស្ត្រីចំណាស់"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"ទារក"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"កម្មករសំណង់"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"ព្រះអង្គម្ចាស់ក្សត្រី"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"សត្វចម្លែកជប៉ុន"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"ម្រេញគង្វាលជប៉ុន"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"ខ្មោច"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"អប្សរាទារក"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"មនុស្សក្រៅភព"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"សត្វចម្លែកក្រៅភព"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"ប្រែត"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"លលាដ៍ក្បាល"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"អ្នកផ្ដល់ព័ត៌មាន"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"អ្នកយាម"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"អ្នករាំ"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"ក្រេមលាបបបូរមាត់"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"ថ្នាំលាបក្រចក"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"ម៉ាស្សាមុខ"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"កាត់សក់"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"ស្លាកសញ្ញាកាត់សក់"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"ស៊ីរ៉ាំង"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"ថ្នាំគ្រាប់"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"ស្នាមថើប"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"លិខិតស្នេហា"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"រោទ៍"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"ត្បូងថ្ម"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"ថើប"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"បាច់ផ្កា"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"ប្ដីប្រពន្ធ និងរូបបេះដូង"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"អាពាហ៍ពិពាហ៍"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"សំឡេងបេះដូងលោត"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"ខូចចិត្ត"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"បេះដូងពីរ"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"បេះដូងមានពន្លឺ"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"បេះដូងលូតលាស់"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"បេះដូងជាប់ព្រួញ"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"បេះដូងពណ៌ខៀវ"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"បេះដូងពណ៌បៃតង"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"បេះដូងពណ៌លឿង"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"បេះដូងពណ៌ស្វាយ"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"បេះដូងជាប់ខ្សែបូ"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"បេះដូងវិល"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"ការតុបតែងបេះដូង"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"រាងពេជ្រមានចំណុចខាងក្នុង"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"អំពូលអគ្គិសនី"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"និមិត្តសញ្ញាកំហឹង"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"គ្រាប់បែក"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"និមិត្តសញ្ញាដេក"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"និមិត្តសញ្ញាប៉ះទង្គិចគ្នា"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"និមិត្តសញ្ញាស្រក់ញើស"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"ដំណក់ទឹក"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"និមិត្តសញ្ញាដកឃ្លា"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"គំនរធូលី"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"ដំឡើងសាច់ដុំដើមដៃ"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"និមិត្តសញ្ញាវិលមុខ"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"ប្រអប់បញ្ចេញយោបល់"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"ប្រអប់គំនិត"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"ផ្កាពណ៌ស"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"និមិត្តសញ្ញាមួយរយពិន្ទុ"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"កាបូបលុយ"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"ប្ដូររូបិយប័ណ្ណ"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"សញ្ញាដុល្លារ"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"កាតឥណទាន"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាយ៉េន"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"លុយដុល្លារ"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាអឺរ៉ូ"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"ក្រដាសប្រាក់ធនាគារមានសញ្ញាផោន"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"លុយមានស្លាប"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"ក្រាហ្វិកនិន្នាការឡើងមានសញ្ញាយ៉េន"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"កៅអី"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"កុំព្យូទ័រផ្ទាល់ខ្លួន"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"វ៉ាលី"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"ឌីសតូច"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"ថាសទន់"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"ថាស"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"ឌីវីឌី"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"ថតឯកសារ"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"ថតឯកសារបើក"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"ទំព័រកោង"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"ទំព័របញ្ឈរ"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"ប្រតិទិន"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"ហែកប្រតិទិន"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"កាតរៀបតាមអក្សរ"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"ក្រាហ្វិកមាននិន្នាការឡើង"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"ក្រាហ្វិកមាននិន្នាការចុះ"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"គំនូសតាងរបារ"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"ក្ដារតម្បៀតខ្ទាស់"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"ម្ជុល"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"ម្ជុលក្បាលមូល"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"ដង្កៀបក្រដាស"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"បន្ទាត់ត្រង់"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"បន្ទាត់រៀងត្រីកោណ"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"ផ្ទាំងចំណាំ"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"បញ្ជីកត់ត្រា"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"សៀវភៅ"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"សៀវភៅមានក្របពណ៌"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"សៀវភៅបិទ"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"សៀវភៅបើក"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"សៀវភៅពណ៌បៃតង"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"សៀវភៅពណ៌ខៀវ"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"សៀវភៅពណ៌ទឹកក្រូច"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"សៀវភៅ"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"ស្លាកឈ្មោះ"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"ក្រដាសរមូរ"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"កំណត់ចំណាំ"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"អ្នកទទួលទូរស័ព្ទ"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"ភេយ័រ"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"ម៉ាស៊ីនទូរសារ"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"អង់តែនផ្កាយរណប"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"អូប៉ាល័រប្រកាសសាធារណៈ"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"ឧបករណ៍បំពងសំឡេង"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"ថាសចេញ"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"ថាសចូល"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"កញ្ចប់"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"និមិត្តសញ្ញាអ៊ីមែល"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"ស្រោមសំបុត្រចូល"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"ស្រោមសំបុត្រមានសញ្ញាព្រួញពីលើ"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"ប្រអប់សំបុត្របិទមានទង់ចុះក្រោម"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"ប្រអប់សំបុត្របិទមានទង់ឡើងលើ"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"ប្រអប់សំបុត្របើកមានទង់ឡើងលើ"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"ប្រអប់សំបុត្របើកមានទង់ចុះក្រោម"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"ប្រអប់ប្រៃសណីយ៍"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"ស្នែងប្រៃសណីយ៍"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"កាសែត"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"ទូរស័ព្ទចល័ត"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"ទូរស័ព្ទចល័តមានសញ្ញាព្រួញចូលពីឆ្វេង"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"របៀបញ័រ"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"បិទទូរស័ព្ទចល័ត"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"មិនមានទូរស័ព្ទចល័ត"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"អង់តែនមានរបារ"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"ម៉ាស៊ីនថត"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"ម៉ាស៊ីនថតវីដេអូ"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"ទូរទស្សន៍"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"វិទ្យុ"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"ការសែតវីដេអូ"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"ព្រួញរមួលទៅស្ដាំ"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"ព្រួញរាងជារង្វង់បើកស្របទិសទ្រនិចនាឡិកាទៅឆ្វេង-ស្ដាំ"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"ព្រួញរាងជារង្វង់បើកស្របទិសទ្រនិចនាឡិកាទៅឆ្វេង-ស្ដាំមានលេខ ១ក្នុងរង្វង់នៅពីលើ"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"ព្រួញរាងជារង្វង់បើកស្របទិសទ្រនិចនាឡិកាចុះក្រោម-ឡើងលើ"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"ព្រួញរាងជារង្វង់បើកច្រាសទិសទ្រនិចនាឡិកាចុះក្រោម-ឡើងលើ"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"និមិត្តសញ្ញាពន្លឺទាប"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"និមិត្តសញ្ញាពន្លឺខ្ពស់"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"អូប៉ាល័រមានបន្ទាត់កាត់"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"អូប៉ាល័រ"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"អូប៉ាល័រមានរលកសំឡេងមួយ"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"អូប៉ាល័រមានរលកសំឡេងបី"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"ថ្ម"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"ដុយអគ្គិសនី"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"កែវពង្រីកចង្អុលខាងឆ្វេង"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"កែវពង្រីកចង្អុលខាងស្ដាំ"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"ចាក់សោដោយប្រើប៊ិច"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"បិទសោដោយប្រើកូនសោ"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"សោ"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"ចាក់សោ"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"បើកសោ"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"កណ្ដឹង"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"កណ្ដឹងមានបន្ទាត់កាត់"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"ចំណាំ"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"និមិត្តសញ្ញាតំណ"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"ប៊ូតុងមូល"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"ថយក្រោយមានព្រួញទៅឆ្វេងខាងលើ"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"បញ្ចប់មានព្រួញទៅឆ្វេងខាងលើ"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"បើកមានសញ្ញាឧទានជាមួយនឹងព្រួញទៅឆ្វេងខាងលើ"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"ឆាប់ៗមានព្រួញទៅស្ដាំខាងលើ"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"កំពូលមានព្រួញឡើងលើនៅខាងលើ"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"និមិត្តសញ្ញាគ្មាននរណាម្នាក់អាយុក្រោមដប់ប្រាំបី"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"គ្រាប់ចុច ១០"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំងធំ"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំងតូច"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់លេខ"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"ការបញ្ចូលនិមិត្តសញ្ញា"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"និមិត្តសញ្ញាបញ្ចូលសម្រាប់អក្សរឡាតាំង"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"ភ្លើង"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"ពិលអគ្គិសនី"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"ម៉ាឡេត"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"ញញួរ"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"ឡោស៊ី"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"កាំបិត"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"កាំភ្លើងខ្លី"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"មីក្រូទស្សន៍"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"កែវយឹត"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"បាល់គ្រីស្តាល់"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"ផ្កាយ ៦ជ្រុងមានចំណុចកណ្ដាល"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"និមិត្តសញ្ញាជប៉ុនសម្រាប់អ្នកចាប់ផ្ដើម"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"លំពែងមុខបី"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"ប៊ូតុងការេពណ៌ខ្មៅ"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"ប៊ូតុងការ៉េពណ៌ស"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"រង្វង់ពណ៌ក្រហមធំ"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"រង្វង់ពណ៌ខៀវធំ"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"ពេជ្រពណ៌ទឹកក្រូចធំ"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"ពេជ្រពណ៌ខៀវធំ"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"ពេជ្រពណ៌ទឹកក្រូចតូច"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"ពេជ្រពណ៌ខៀវតូច"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"ត្រីកោណពណ៌ក្រហមកំពូលឡើងលើ"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"ត្រីកោណពណ៌ក្រហមកំពូលចុះក្រោម"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"ត្រីកោណពណ៌ក្រហមតូចកំពូលឡើងលើ"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"ត្រីកោណពណ៌ក្រហមតូចកំពូលចុះក្រោម"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"មុខនាឡិកាម៉ោងមួយ"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"មុខនាឡិកាម៉ោងពីរ"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"មុខនាឡិកាម៉ោងបី"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"មុខនាឡិកាម៉ោងបួន"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"មុខនាឡិកាម៉ោងប្រាំ"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"មុខនាឡិកាម៉ោងប្រាំមួយ"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"មុខនាឡិកាម៉ោងប្រាំពីរ"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"មុខនាឡិកាម៉ោងប្រាំបី"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"មុខនាឡិកាម៉ោងប្រាំបួន"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"មុខនាឡិកាម៉ោងដប់"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"មុខនាឡិកាម៉ោងដប់មួយ"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"មុខនាឡិកាម៉ោងដប់ពីរ"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"មុខនាឡិកាម៉ោងមួយកន្លះ"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"មុខនាឡិកាម៉ោងពីរកន្លះ"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"មុខនាឡិកាម៉ោងបីកន្លះ"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"មុខនាឡិកាម៉ោងបួនកន្លះ"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"មុខនាឡិកាម៉ោងប្រាំកន្លះ"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"មុខនាឡិកាម៉ោងប្រាំមួយកន្លះ"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"មុខនាឡិកាម៉ោងប្រាំពីរកន្លះ"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"មុខនាឡិកាម៉ោងប្រាំបីកន្លះ"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"មុខនាឡិកាម៉ោងប្រាំបួនកន្លះ"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"មុខនាឡិកាម៉ោងដប់កន្លះ"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"មុខនាឡិកាម៉ោងដប់មួយកន្លះ"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"មុខនាឡិកាម៉ោងដប់ពីរកន្លះ"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"ភ្នំហ្វូជី"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"អគារទីក្រុងតូក្យូ"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"រូបសំណាកសេរីភាព"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"គំនូរស្រមោលជប៉ុន"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"ម៉ូយ៉ៃ"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"មុខញញឹមខាំធ្មេញ"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"មុខញញឹមខាំធ្មេញជាមួយភ្នែកបើក"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"មុខទទឹកមានភ្នែករីករាយ"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"មុខញញឹមបើកមាត់"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"មុខញញឹមបើកមាត់ និងបើកភ្នែក"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"ញញឹមមុខបើកមាត់ និងខ្វល់ខ្វាយ"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"ញញឹមមុខបើកមាត់ និងបិទភ្នែកបន្តិច"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"មុខញញឹមមានរង្វង់ខាងលើ"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"មុខញញឹមមានស្នែង"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"មុខមិចភ្នែក"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"មុខញញឹមបើកភ្នែក"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"មុខបង្ហាញរសជាតិអាហារឆ្ងាញ់"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"មុខធូរស្រាល"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"មុខញញឹមជាមួយភ្នែករូបបេះដូង"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"មុខញញឹមពាក់វ៉ែនតាខ្មៅ"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"មុខញញឹមចំអក"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"មុខអព្យាក្រឹត"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"មុខគ្មានអារម្មណ៍"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"មុខអត់សប្បាយចិត្ត"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"មុខមានញើស"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"មុខគិត"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"មុខយល់ច្រឡំ"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"មុខអាក្រក់"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"មុខថើប"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"មុខបង្ហាញការថើប"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"មុខថើបបើកភ្នែក"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"មុខថើបបិទភ្នែក"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"មុខលៀនអណ្ដាត"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"មុខលៀនអណ្ដាត ហើយមិចភ្នែក"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"មុខលៀនអណ្ដាត ហើយបិទភ្នែកបន្តិច"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"មុខខកចិត្ត"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"មុខព្រួយបារម្ភ"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"មុខខឹង"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"មុខក្និកក្នក់"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"មុខយំ"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"មុខលំបាក"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"មុខជ័យជំនះ"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"មុខខកចិត្តប៉ុន្តែធូរស្រាល"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"មុខក្រញូវបើកមាត់"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"មុខខូចចិត្ត"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"មុខភ័យខ្លាច"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"មុខនឿយហត់"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"មុខងងុយគេង"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"មុខអស់កម្លាំង"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"មុខក្រញេវក្រញូវ"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"មុខយំលឺៗ"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"មុខបើកមាត់"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"មុខស្ងៀមស្ងាត់"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"មុខបើកមាត់ ហើយមានញើស"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"មុខស្រែកដោយភ័យខ្លាច"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"មុខរភ្ញាក់ផ្អើល"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"មុខក្រហម"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"មុខកំពុងដេក"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"មុខកំពុងវិលមុខ"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"មុខអត់មានមាត់"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"មុខពាក់ម៉ាស់"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"មុខឆ្មាញញឹមខាំធ្មេញ ហើយបើកភ្នែក"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"មុខឆ្មាស្រកទឹកភ្នែក"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"មុខឆ្មាញញឹម ហើយបើកមាត់"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"មុខឆ្មាញញឹមជាមួយភ្នែករាងបេះដូង"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"មុខឆ្មាជាមួយការញញឹមញាក់មុខ"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"មុខឆ្មាថើបហើយបិទភ្នែក"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"មុខឆ្មាក្និកក្នក់"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"មុខឆ្មាយំ"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"មុខឆ្មានឿយហត់"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"មុខគ្មានកាយវិការល្អ"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"មុខមានកាយវិការយល់ព្រម"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"មនុស្សអោនខ្លួនខ្លាំង"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"ស្វាបិទភ្នែក"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"ស្វាបិទត្រចៀក"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"ស្វាបិទមាត់"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"មនុស្សសប្បាយលើកដៃមួយ"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"លើកដៃទាំងពីរអបអរ"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"មនុស្សចងចិញ្ចើម"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"មនុស្សមានមុខក្និកក្នក់"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"មនុស្សអោបដៃ"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"គ្រាប់រ៉ុកកែត"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"ឧទ្ធម្ភាគចក្រ"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"ក្បាលរថភ្លើង"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"ទូររថភ្លើង"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"រថភ្លើងល្បឿនលឿន"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"រថភ្លើងល្បឿនលឿនមានច្រមុះ"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"រថភ្លើង"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"មេត្រូ"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"រថភ្លើងប្រើពន្លឺ"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"ស្ថានីយ"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"រថយន្តរត់លើផ្លូវដែក"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"រថយន្តរត់លើផ្លូវដែក"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"រថយន្តក្រុង"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"រថយន្តក្រុងខាងមុខ"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"រថយន្តប្រើចរន្តអគ្គិសនី"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"ចំណតរថយន្តក្រុង"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"រថយន្តទេសចរណ៍តូច"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"រថយន្តសង្គ្រោះបន្ទាន់"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"ម៉ាស៊ីនភ្លើង"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"រថយន្តប៉ូលិស"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"រថយន្តប៉ូលិសខាងមុខ"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"តាក់ស៊ី"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"តាក់ស៊ីខាងមុខ"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"រថយន្ត"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"រថយន្តខាងមុខ"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"រថយន្តសម្រាប់កម្សាន្ត"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"រថយន្តចែកចាយ"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"ឡានកាមីយ៉ុង"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"ត្រាក់ទ័រ"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"ប្រព័ន្ធផ្លូវរថភ្លើង"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"ផ្លូវរថភ្លើងកាត់ភ្នំ"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"ផ្លូវរថភ្លើងផ្អាកដំណើរការ"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"ខ្សែកាបឆ្លងភ្នំុ"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"ផ្លូវរថភ្លើងលើអាកាស"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"កប៉ាល់"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"ទូកចែវ"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"ទូកល្បឿនលឿន"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"ភ្លើងចរាចរណ៍"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"ភ្លើងចរាចរណ៍បញ្ឈរ"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"សញ្ញាសំណង់"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"រថយន្តប៉ូលិសបើកសារ៉ែនវិល"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"បង្គោលទង់ជាតិរាងត្រីកោណ"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"ទ្វារ"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"សញ្ញាហាមចូល"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"សញ្ញាជក់បារី"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"ហាមជក់បារី"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"និមិត្តសញ្ញាដាក់សំរាមក្នុងធុង"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"និមិត្តសញ្ញាហាមចោលសំរាម"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"និមិត្តសញ្ញាទឹកចល័ត"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"និមិត្តសញ្ញាទឹកមិនចល័ត"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"កង់"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"ហាមជិះកង់"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"អ្នកជិះកង់"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"អ្នកជិះកង់ឡើងភ្នំ"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"ថ្មើរជើង"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"មិនសម្រាប់ថ្មើរជើង"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"កុមារឆ្លងកាត់"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"និមិត្តសញ្ញាបុរស"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"និមិត្តសញ្ញាស្ត្រី"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"បន្ទប់ទឹក"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"និមិត្តសញ្ញាទារក"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"បង្គន់"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"បន្ទប់ទឹក"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"ផ្កាឈូកងូតទឹក"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"ងូតទឹក"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"អាងងូតទឹក"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"ការត្រួតពិនិត្យលិខិតឆ្លងដែន"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"គយ"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"ប្រកាសឥវ៉ាន់"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"ឥវ៉ាន់នៅសល់"</string> +</resources> diff --git a/java/res/values-km-rKH/strings-talkback-descriptions.xml b/java/res/values-km-rKH/strings-talkback-descriptions.xml index 76bc35f84..0f0ac5600 100644 --- a/java/res/values-km-rKH/strings-talkback-descriptions.xml +++ b/java/res/values-km-rKH/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"គ្មានអត្ថបទបានបញ្ចូល"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> កែ <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ទៅជា <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> អនុវត្តការកែស្វ័យប្រវត្តិ"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"កូដគ្រាប់ចុច %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"បើក Shift (ប៉ះដើម្បីបិទ)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"បើក Caps lock (ប៉ះដើម្បីបិទ)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"និមិត្តសញ្ញាច្រើនទៀត"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"និមិត្តសញ្ញា"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"លុប"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"និមិត្តសញ្ញា"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"អក្សរ"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"មុន"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"បានបើក Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"បានបើក Caps lock"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"បានបិទ Shift"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"របៀបនិមិត្តសញ្ញា"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"របៀបនិមិត្តសញ្ញាច្រើនទៀត"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"របៀបអក្សរ"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"របៀបទូរស័ព្ទ"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"របៀបនិមិត្តសញ្ញាទូរស័ព្ទ"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"ទីកន្លែង"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"និមិត្តសញ្ញា"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"សញ្ញាអារម្មណ៍"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml index 519aa44d0..c7b6f2b4f 100644 --- a/java/res/values-km-rKH/strings.xml +++ b/java/res/values-km-rKH/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"ជម្រើសបញ្ចូល"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"ពាក្យបញ្ជាកំណត់ហេតុការស្រាវជ្រាវ"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"រកមើលឈ្មោះទំនាក់ទំនង"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"កម្មវិធីពិនិត្យអក្ខរាវិរុទ្ធប្រើធាតុពីក្នុងបញ្ជីទំនាក់ទំនងរបស់អ្នក"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"ញ័រនៅពេលចុចគ្រាប់ចុច"</string> @@ -29,7 +28,7 @@ <string name="popup_on_keypress" msgid="123894815723512944">"លេចឡើងនៅពេលចុចគ្រាប់ចុច"</string> <string name="general_category" msgid="1859088467017573195">"ទូទៅ"</string> <string name="correction_category" msgid="2236750915056607613">"ការកែអត្ថបទ"</string> - <string name="gesture_typing_category" msgid="497263612130532630">"បញ្ចូលដោយប្រើកាយវិការ"</string> + <string name="gesture_typing_category" msgid="497263612130532630">"បញ្ចូលដោយប្រើកាយវិការ"</string> <string name="misc_category" msgid="6894192814868233453">"ជម្រើសផ្សេងទៀត"</string> <string name="advanced_settings" msgid="362895144495591463">"ការកំណត់កម្រិតខ្ពស់"</string> <string name="advanced_settings_summary" msgid="4487980456152830271">"ជម្រើសសម្រាប់អ្នកជំនាញ"</string> @@ -39,7 +38,7 @@ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"បង្ហាញនៅពេលដែលបើកភាសាបញ្ចូលច្រើន"</string> <string name="sliding_key_input_preview" msgid="6604262359510068370">"បង្ហាញទ្រនិចបង្ហាញស្លាយ"</string> <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"បង្ហាញសញ្ញាមើលឃើញខណៈពេលដែលរុញពីឆ្វេង ឬគ្រាប់ចុចនិមិត្តសញ្ញា"</string> - <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"សោលេចឡើងបោះបង់ការពន្យារពេល"</string> + <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"សោលេចឡើងបោះបង់ការពន្យារពេល"</string> <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"គ្មានការពន្យារពេល"</string> <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"លំនាំដើម"</string> <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> មិល្លីវិនាទី"</string> @@ -47,9 +46,11 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"ស្នើឈ្មោះទំនាក់ទំនង"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ប្រើឈ្មោះពីទំនាក់ទំនងសម្រាប់ការស្នើ និងកែ"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"ការស្នើផ្ទាល់ខ្លួន"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"រយៈពេលចុចដកឃ្លាពីរដង"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"ប៉ះដកឃ្លាពីរដងបញ្ចូលរយៈពេលដែលអនុវត្តតាមដកឃ្លា"</string> - <string name="auto_cap" msgid="1719746674854628252">"ការសរសេរជាអក្សរធំស្វ័យប្រវត្តិ"</string> + <string name="auto_cap" msgid="1719746674854628252">"ការសរសេរជាអក្សរធំស្វ័យប្រវត្តិ"</string> <string name="auto_cap_summary" msgid="7934452761022946874">"សរសេរពាក្យដំបូងជាអក្សរធំនៃប្រយោគនីមួយៗ"</string> <string name="edit_personal_dictionary" msgid="3996910038952940420">"វចនានុក្រមផ្ទាល់ខ្លួន"</string> <string name="configure_dictionaries_title" msgid="4238652338556902049">"ផ្នែកបន្ថែមវចនានុក្រម"</string> @@ -60,7 +61,7 @@ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"បង្ហាញនៅក្នុងរបៀបបញ្ឈរ"</string> <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"លាក់ជានិច្ច"</string> <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ទប់ស្កាត់ពាក្យបំពាន"</string> - <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំស្នើឲ្យពាក្យបំពានមានសក្ដានុពល"</string> + <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំស្នើឲ្យពាក្យបំពានមានសក្ដានុពល"</string> <string name="auto_correction" msgid="7630720885194996950">"ការកែស្វ័យប្រវត្តិ"</string> <string name="auto_correction_summary" msgid="5625751551134658006">"ចន្លោះមិនឃើញ និងសញ្ញាវណ្ណយុត្តកែពាក្យដែលបានវាយខុសស្វ័យប្រវត្តិ"</string> <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"បិទ"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"បញ្ចូលភាសា"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"ប៉ះម្ដងទៀត ដើម្បីរក្សាទុក"</string> <string name="has_dictionary" msgid="6071847973466625007">"មានវចនានុក្រម"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"បើកមតិត្រឡប់"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"ជំនួយធ្វើឲ្យប្រសើរឡើងនៃកម្មវិធីកែវិធីសាស្ត្របញ្ចូលដោយស្វ័យប្រវត្តិ ដោយផ្ញើស្ថិតិប្រើប្រាស់ និងរបាយការណ៍គាំង"</string> <string name="keyboard_layout" msgid="8451164783510487501">"រូបរាងក្ដារចុច"</string> <string name="subtype_en_GB" msgid="88170601942311355">"អង់គ្លេស (អង់គ្លេស)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"អង់គ្លេស (សហរដ្ឋអាមេរិក)"</string> @@ -118,12 +117,11 @@ <string name="enable" msgid="5031294444630523247">"បើក"</string> <string name="not_now" msgid="6172462888202790482">"មិនមែនឥឡូវ"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"មានរចនាប័ទ្មបញ្ចូលដូចគ្នាដូចហើយ៖ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"របៀបការសិក្សាដែលអាចប្រើបាន"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ពន្យារពេលចុចគ្រាប់ចុចឲ្យយូរ"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ថិរវេលាញ័រពេលចុចគ្រាប់ចុច"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"កម្រិតសំឡេងពេលចុចគ្រាប់ចុច"</string> <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"អានឯកសារវចនានុក្រមខាងក្រៅ"</string> - <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"គ្មានឯកសារវចនានុក្រមនៅក្នុងថតទាញយក"</string> + <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"គ្មានឯកសារវចនានុក្រមនៅក្នុងថតទាញយក"</string> <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ជ្រើសឯកសារវចនានុក្រម ដើម្បីដំឡើង"</string> <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"ពិតជាដំឡើងឯកសារនេះសម្រាប់ <xliff:g id="LANGUAGE_NAME">%s</xliff:g>?"</string> <string name="error" msgid="8940763624668513648">"មានកំហុស"</string> @@ -154,7 +152,7 @@ <string name="dictionary_provider_name" msgid="3027315045397363079">"កម្មវិធីផ្ដល់វចនានុក្រម"</string> <string name="dictionary_service_name" msgid="6237472350693511448">"សេវាកម្មវចនានុក្រម"</string> <string name="download_description" msgid="6014835283119198591">"ព័ត៌មានបច្ចុប្បន្នភាពវចនានុក្រម"</string> - <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែកបន្ថែមវចនានុក្រម"</string> + <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែកបន្ថែមវចនានុក្រម"</string> <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"វចនានុក្រមអាចប្រើបាន"</string> <string name="dictionary_settings_summary" msgid="5305694987799824349">"ការកំណត់សម្រាប់វចនានុក្រម"</string> <string name="user_dictionaries" msgid="3582332055892252845">"វចនានុក្រមអ្នកប្រើ"</string> @@ -170,10 +168,10 @@ <string name="message_updating" msgid="4457761393932375219">"ពិនិត្យមើលបច្ចុប្បន្នភាព"</string> <string name="message_loading" msgid="5638680861387748936">"កំពុងផ្ទុក..."</string> <string name="main_dict_description" msgid="3072821352793492143">"វចនានុក្រមចម្បង"</string> - <string name="cancel" msgid="6830980399865683324">"បោះបង់"</string> + <string name="cancel" msgid="6830980399865683324">"បោះបង់"</string> <string name="go_to_settings" msgid="3876892339342569259">"ការកំណត់"</string> <string name="install_dict" msgid="180852772562189365">"ដំឡើង"</string> - <string name="cancel_download_dict" msgid="7843340278507019303">"បោះបង់"</string> + <string name="cancel_download_dict" msgid="7843340278507019303">"បោះបង់"</string> <string name="delete_dict" msgid="756853268088330054">"លុប"</string> <string name="should_download_over_metered_prompt" msgid="1583881200688185508">"ភាសាដែលបានជ្រើសនៅលើឧបករណ៍ចល័តមានវចនានុក្រមអាចប្រើបាន។<br/> យើងផ្ដល់អនុសាសន៍ឲ្យ <b>ទាញយក</b> វចនានុក្រមភាសា <xliff:g id="LANGUAGE_NAME">%1$s</xliff:g> ដើម្បីបង្កើនបទពិសោធន៍វាយបញ្ចូលរបស់អ្នក។<br/> <br/> ការទាញយកអាចចំណាយពេលប្រហែលពីរនាទីនៅតាម 3G។ ការគិតថ្លៃអាចអនុវត្តប្រសិនបើអ្នកមិនប្រើ <b>ផែនការទិន្នន័យគ្មានដែនកំណត់</b>.<br/> បើអ្នកមិនប្រាកដថាផែនការណាមួយដែលអ្នកមាន យើងផ្ដល់អនុសាសន៍ឲ្យភ្ជាប់វ៉ាយហ្វាយ ដើម្បីចាប់ផ្ដើមទាញយកដោយស្វ័យប្រវត្តិ។<br/> <br/> ជំនួយ៖ អ្នកអាចទាញយក និងលុបវចនានុក្រមដោយចូលទៅ <b>ភាសា & ការបញ្ចូល</b> នៅក្នុងម៉ឺនុយ <b>ការកំណត់</b> សម្រាប់ឧបករណ៍ចល័ត។"</string> <string name="download_over_metered" msgid="1643065851159409546">"ទាញយកឥឡូវនេះ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> មេកាបៃ)"</string> @@ -191,7 +189,7 @@ <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ពាក្យ៖"</string> <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ផ្លូវកាត់៖"</string> <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ភាសា៖"</string> - <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយបញ្ចូលពាក្យ"</string> + <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយបញ្ចូលពាក្យ"</string> <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ផ្លូវកាត់ជាជម្រើស"</string> <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"កែពាក្យ"</string> <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"កែ"</string> diff --git a/java/res/values-ko/strings-action-keys.xml b/java/res/values-ko/strings-action-keys.xml index 04febeeea..34cd8a21d 100644 --- a/java/res/values-ko/strings-action-keys.xml +++ b/java/res/values-ko/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"이전"</string> <string name="label_done_key" msgid="7564866296502630852">"완료"</string> <string name="label_send_key" msgid="482252074224462163">"전송"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"중지"</string> <string name="label_wait_key" msgid="5891247853595466039">"대기"</string> </resources> diff --git a/java/res/values-ko/strings-talkback-descriptions.xml b/java/res/values-ko/strings-talkback-descriptions.xml index 6142c6a3b..d5d71f935 100644 --- a/java/res/values-ko/strings-talkback-descriptions.xml +++ b/java/res/values-ko/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"입력한 텍스트 없음"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g>을(를) 누르면 <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>을(를) <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>(으)로 수정합니다."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g>을(를) 누르면 자동 수정됩니다."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"키 코드 %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift 키"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift 사용(사용하지 않으려면 탭하세요.)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock 사용(사용하지 않으려면 탭하세요.)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"기호 더보기"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift 키"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"기호"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift 키"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"삭제"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"기호"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"문자"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"이전"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift 사용"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock 사용"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift 사용 중지됨"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"기호 모드"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"기호 더보기 모드"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"문자 모드"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"다이얼 모드"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"전화 기호 모드"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"장소"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"기호"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"이모티콘"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index 4037abd02..3eff38c9d 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"맞춤법 검사기가 주소록의 항목을 사용합니다."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"주소록 이름 활용"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"맞춤 추천 검색어"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"더블스페이스 마침표"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"스페이스바를 두 번 탭하면 마침표와 공백 한 개가 삽입됩니다."</string> <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"입력 언어"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"저장하려면 다시 터치"</string> <string name="has_dictionary" msgid="6071847973466625007">"사전 사용 가능"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"사용자 의견 사용"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"사용 통계 및 오류 보고서를 자동으로 전송하여 입력 방법 편집기의 개선에 도움을 줍니다."</string> <string name="keyboard_layout" msgid="8451164783510487501">"키보드 테마"</string> <string name="subtype_en_GB" msgid="88170601942311355">"영어(영국)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"영어(미국)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"사용"</string> <string name="not_now" msgid="6172462888202790482">"나중에"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"같은 입력 스타일이 다음과 같이 이미 존재합니다. <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"가용성 연구 모드"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"키 길게 누르기 지연"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"키를 누를 때 진동 시간"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"키를 누를 때 소리 볼륨"</string> diff --git a/java/res/values-ky/strings.xml b/java/res/values-ky/strings.xml index 3758c2df1..8165a3c7d 100644 --- a/java/res/values-ky/strings.xml +++ b/java/res/values-ky/strings.xml @@ -126,9 +126,7 @@ <skip /> <!-- no translation found for has_dictionary (6071847973466625007) --> <skip /> - <!-- no translation found for prefs_enable_log (6620424505072963557) --> <skip /> - <!-- no translation found for prefs_description_log (5827825607258246003) --> <skip /> <!-- no translation found for keyboard_layout (8451164783510487501) --> <skip /> @@ -138,7 +136,6 @@ <skip /> <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) --> <skip /> - <!-- no translation found for prefs_usability_study_mode (1261130555134595254) --> <skip /> <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) --> <skip /> diff --git a/java/res/values-land/config.xml b/java/res/values-land/config.xml index 43ae068f2..37bf22fb0 100644 --- a/java/res/values-land/config.xml +++ b/java/res/values-land/config.xml @@ -58,6 +58,8 @@ <fraction name="config_key_shifted_letter_hint_ratio_5row">48%</fraction> <dimen name="config_suggestions_strip_height">36dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">54dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">54dp</dimen> <dimen name="config_more_suggestions_row_height">36dp</dimen> <integer name="config_max_more_suggestions_row">2</integer> <fraction name="config_min_more_suggestions_width">60%</fraction> diff --git a/java/res/values-lo-rLA/strings-action-keys.xml b/java/res/values-lo-rLA/strings-action-keys.xml index 08dc983e0..239930583 100644 --- a/java/res/values-lo-rLA/strings-action-keys.xml +++ b/java/res/values-lo-rLA/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"ກ່ອນໜ້າ"</string> <string name="label_done_key" msgid="7564866296502630852">"Done"</string> <string name="label_send_key" msgid="482252074224462163">"ສົ່ງ"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"ຢຸດຊົ່ວຄາວ"</string> <string name="label_wait_key" msgid="5891247853595466039">"ລໍຖ້າ"</string> </resources> diff --git a/java/res/values-lo-rLA/strings-emoji-descriptions.xml b/java/res/values-lo-rLA/strings-emoji-descriptions.xml new file mode 100644 index 000000000..0747fa629 --- /dev/null +++ b/java/res/values-lo-rLA/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"ເຄື່ອງໝາຍລິຂະສິດ"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"ເຄື່ອງໝາຍຈົດທະບຽນ"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Double exclamation mark"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Exclamation question mark"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Trade mark sign"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Information source"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Left right arrow"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Up down arrow"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"North west arrow"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"North east arrow"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"South east arrow"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"South west arrow"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Leftwards arrow with hook"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Rightwards arrow with hook"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"ເບິ່ງ"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Hourglass"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Black right-pointing double triangle"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Black left-pointing double triangle"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Black up-pointing double triangle"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Black down-pointing double triangle"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"ໂມງປຸກ"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Hourglass with flowing sand"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Circled latin capital letter m"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Black small square"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"White small square"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Black right-pointing triangle"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Black left-pointing triangle"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"White medium square"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Black medium square"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"White medium small square"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Black medium small square"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Black sun with rays"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Cloud"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Black telephone"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Ballot box with check"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Umbrella with rain drops"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Hot beverage"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"White up pointing index"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"White smiling face"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Aries"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Taurus"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Gemini"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"ມະເຮັງ"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Leo"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Virgo"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Libra"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Scorpius"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Sagittarius"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Capricorn"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Aquarius"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Pisces"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Black spade suit"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Black club suit"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Black heart suit"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Black diamond suit"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Hot springs"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Black universal recycling symbol"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Wheelchair symbol"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Anchor"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Warning sign"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"High voltage sign"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Medium white circle"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Medium black circle"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Soccer ball"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"ເບສບອລ"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Snowman without snow"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Sun behind cloud"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Ophiuchus"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"No entry"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Church"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Fountain"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Flag in hole"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Sailboat"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Tent"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Fuel pump"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Black scissors"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"White heavy check mark"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Airplane"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Envelope"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Raised fist"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Raised hand"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Victory hand"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Pencil"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Black nib"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Heavy check mark"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Heavy multiplication x"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Sparkles"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Eight spoked asterisk"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Eight pointed black star"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Snowflake"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Sparkle"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Cross mark"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Negative squared cross mark"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Black question mark ornament"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"White question mark ornament"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"White exclamation mark ornament"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Heavy exclamation mark symbol"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"ຫົວໃຈສີດຳໜັກ"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Heavy plus sign"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Heavy minus sign"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Heavy division sign"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Black rightwards arrow"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Curly loop"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Double curly loop"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Arrow pointing rightwards then curving upwards"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Arrow pointing rightwards then curving downwards"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Leftwards black arrow"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Upwards black arrow"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Downwards black arrow"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Black large square"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"White large square"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"White medium star"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Heavy large circle"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Wavy dash"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Part alternation mark"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Circled ideograph congratulation"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Circled ideograph secret"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"ໄພ້ນົກກະຈອກເທດມັງກອນແດງ"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"ກຳລັງຫຼິ້ນໄພ້ໂຈກເກີສີດຳ"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"ເລືອດກຣຸບ A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"ເລືອດກຣຸບ B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"ເລືອດກຣຸບ O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"ບ່ອນຈອດລົດ"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"ເລືອດກຣຸບ AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"CL ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Cool ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Free ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"ID ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"New ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"N G ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"OK ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"SOS ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"UP! ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"VS ສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"ບໍລິການ ແບບກະຕະກະນະສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"ບໍລິການ ແບບກະຕະກະນະສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Squared ideograph charge-free"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Squared ideograph reserved-seat"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Squared ideograph prohibitation"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Squared ideograph vacancy"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Squared ideograph acceptance"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Squared ideograph full occupancy"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Squared ideograph paid"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Squared ideograph monthly"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Squared ideograph application"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Squared ideograph discount"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Squared ideograph in business"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Circled ideograph advantage"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Circled ideograph accept"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"ພະຍຸໝຸນໄຊໂຄລນ"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"ໝອກຄວັນ"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"ຄັນຮົ່ມປິດ"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"ຄ່ຳຄືນກັບແສງດາວ"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"ຕາເວັນຂຶ້ນຢູ່ພູ"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"ຕາເວັນຂຶ້ນ"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"ເມືອງໃນຍາມໃກ້ຄ່ຳ"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"ຕາເວັນ"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"ຮຸ້ງ"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"ຂົວໃນຍາມຄ່ຳຄືນ"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"ຄື້ນນ້ຳ"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"ພູເຂົາໄຟ"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"ທາງຊ້າງເຜືອກ"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"ໜ່ວຍໂລກຢູໂຣບອາຟຣິກາ"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"ໜ່ວຍໂລກອາເມລິກາ"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"ໜ່ວຍໂລກເອເຊຍອອສເຕຣເລຍ"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"ໜ່ວຍໂລກມີເສັ້ນຕັດ"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"ສັນຍາລັກເດືອນມືດ"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"ສັນຍາລັກເດືອນບໍ່ເຕັມດວງ"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"ສັນຍາລັກເດືອນເຄິ່ງດວງທຳອິດ"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"ສັນຍາລັກເດືອນຂຶ້ນ"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"ສັນຍາລັກເດືອນເຕັມດວງ"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"ສັນຍາລັກເດືອນໃກ້ເຕັມດວງ"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"ສັນຍາລັກເດືອນເຄິ່ງດວງທ້າຍ"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"ສັນຍາລັກເດືອນສ້ຽວ"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"ເດືອນສ້ຽວ"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"ເດືອນມືດມີໜ້າ"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"ເດືອນເຄິ່ງດວງທຳອິດມີໜ້າ"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"ສັນຍາລັກເດືອນເຄິ່ງດວງທ້າຍມີໜ້າ"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"ເດືອນເຕັມດວງມີໜ້າ"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"ຕາເວັນມີໜ້າ"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"ດາວເຮືອງແສງ"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"ດາວຕົກ"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"ລູກເກົາລັດ"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"ກ້າໄມ້"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"ຕົ້ນໄມ້ບໍ່ຜັດໃບ"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"ຕົ້ນໄມ້ຜັດໃບ"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"ຕົ້ນປາມ"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"ກະບອງເພັດ"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"ທິວລິບ"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"ດອກເຊີຣີ"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"ດອກກຸຫລາບ"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"ສະກຸນຊະບາ"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"ດອກຕາເວັນ"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"ດອກໄມ້ບານ"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"ຕົ້ນສາລີ"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"ຕົ້ນເຂົ້າ"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"ສະໝຸນໄພ"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"ໂຄລເວີສີ່ໃບ"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"ໃບເມເປີ້ນ"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"ໃບໄມ້ຫຼົ່ນ"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"ໃບໄມ້ປິວຕາມລົມ"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"ເຫັດ"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"ໝາກເຫຼັ່ນ"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"ໝາກເຂືອ"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"ອະງຸ່ນ"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"ໝາກໂມ"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"ໝາກໂມ"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"ສົ້ມຂຽວຫວານ"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"ໝາກນາວ"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"ໝາກກ້ວຍ"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"ໝາກນັດ"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"ໝາກໂປ່ມແດງ"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"ໝາກໂປ່ມຂຽວ"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"ລູກສາລີ່"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"ລູກພີດ"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"ເຊີຣີ"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"ສະຕຣໍເບີຣີ"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"ແຮມເບີເກີ"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"ພິດຊ່າປ່ຽງນຶ່ງ"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"ຊີ້ນຕິດກະດູກ"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"ຂາສັດປີກ"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"ເຂົ້າໜົມເຂົ້າ"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"ເຂົ້າປັ້ນ"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"ເຂົ້າສຸກ"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"ເຂົ້າແກງກະຫລີ່"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"ຖ້ວຍຮ້ອນ"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"ສະປາເກັດຕີ"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"ເຂົ້າຈີ່"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"ມັນຝຣັ່ງທອດ"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"ມັນຝຣັ່ງຫວານອົບ"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"ດັງໂງະ"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"ໂອເດັ້ງ"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"ຊູຊິ"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"ກຸ້ງທອດ"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"ເຄັກປາແບບໝຸນໆ"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"ກະແລ່ມນຸ້ມ"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"ນ້ຳແຂງໃສ"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"ກະແລ່ມ"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"ໂດນັດ"</string> + <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_1F36E" msgid="4630541402785165902">"ຄັສຕາດ"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ໝໍ້ນ້ຳເຜິ້ງ"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"ຊັອດເຄັກ"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"ເບັນໂຕ"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"ໝໍ້ອາຫານ"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"ຄົວກິນ"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"ມີດແລະສ້ອມ"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"ຖ້ວຍຊາແບບບໍ່ມີດ້າມຈັບ"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"ຈອກແລະເຫຼົ້າສາເກ"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"ຈອກວາຍ"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"ຈອກຄັອກເທວ"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"ເຄື່ອງດື່ມເຂດຮ້ອນ"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"ແກ້ວເບຍ"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"ຕຳແກ້ວເບຍ"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"ຕຸກນົມແອນ້ອຍ"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"ຣິບບອນ"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"ຂອງຂວັນຫໍ່ໄວ້"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"ເຄັກວັນເກີດ"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"ແຈັກ-ໂອ-ແລນເທິນ"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"ຕົ້ນຄຣິສມາສ"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"ຊານຕາຄລອສ"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"ບັ້ງໄຟດອກ"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"ດອກໄມ້ໄຟ"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"ບານລູນ"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"ປາຕີ້ປັອບເປີ"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"ບານຄອນເຟັດຕີ"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"ຕົ້ນທານາບາຕະ"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"ທຸງໄຂວ່ກັນ"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"ການຕົກແຕ່ງຕົ້ນພາຍ"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"ຕຸກກະຕາຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"ທຸງປາຄາຣ໌ບ"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"ກະດິ່ງລົມ"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"ພິທີຊົມເດືອນ"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"ກະເປົາໂຮງຮຽນ"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"ໝວກສຳເລັດການສຶກສາ"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"ມ້າໝຸນ"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"ຊິງຊ້າສະຫວັນ"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"ລົດໄຟເຫາະ"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"ເບັດຕຶກປາກັບປາ"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"ໄມໂຄຣໂຟນ"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"ກ້ອງຖ່າຍໜັງ"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"ໂຮງຮູບເງົາ"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"ຫູຟັງ"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"ຈານສີ"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"ໝວກຊົງສູງ"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"ເຕັ້ນລະຄອນສັດ"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"ປີ້"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"ແຄລັບເປີບອດ"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"ສິນລະປະການສະແດງ"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"ວິດີໂອເກມ"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"ຍິງເຂົ້າເປົ້າ"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"ສະລັອດແມັດຊີນ"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"ສະນຸກເກີ"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"ລູກເຕົ໋າ"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"ໂບລິງ"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"ໄພ່ດອກໄມ້"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"ໂນດດົນຕີ"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"ໂນດດົນຕີຫຼາຍອັນ"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"ແຊັກໂຊໂຟນ"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"ກີຕ້າ"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"ແປ້ນພິມດົນຕີ"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"ແຕ"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"ໄວໂອລິນ"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"ສະກໍດົນຕີ"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"ເສື້ອແລ່ນກັບສາຍພາຍ"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"ໄມ້ແລະລູກເທນນິສ"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"ສະກີແລະເກີບສະກີ"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"ບານບ້ວງແລະບ່ວງ"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"ທຸງເສັ້ນໄຊ"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"ສະໂນບອດເດີ"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"ນັກແລ່ນ"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"ນັກເຊີບ"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"ຮາງວັນ"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"ການແຂ່ງມ້າ"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"ອາເມລິກັນຟຸດບອນ"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"ຣັກບີ"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"ນັກລອຍນ້ຳ"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"ປຸກເຮືອນ"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"ເຮືອນພ້ອມສວນ"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"ອາຄານສຳນັກງານ"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"ຫ້ອງການໄປສະນີຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"ຫ້ອງການໄປສະນີຢູໂຣບ"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"ໂຮງໝໍ"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"ທະນາຄານ"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"ເອທີເອັມ"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"ໂຮງແຮມ"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"ມ່ານຮູດ"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"ຮ້ານສະດວກຊື້"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"ໂຮງຮຽນ"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"ຫ້າງສັບພະສິນຄ້າ"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"ໂຮງງານ"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"ໂຄມໄຟອິຊາກາຢະ"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"ຜາສາດຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"ຜາສາດຢູໂຣບ"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"ໜູ"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"ໜູ"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Ox"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"ຄວາຍ"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"ງົວ"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"ເສືອດາວ"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"ກະຕ່າຍ"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"ແມວ"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"ມັງກອນ"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"ແຂ້"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"ວານ"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"ຫອຍ"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"ງູ"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"ມ້າ"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"ແກະ"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"ແບ້"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"ແກະ"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"ລິງ"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"ໄກ່ໂຕຜູ້"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"ໄກ່"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"ໝາ"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"ໝູ"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"ໝູປ່າ"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"ຊ້າງ"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"ປາເໝິກ"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"ເປືອກຫອຍກຽວ"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"ແມງໄມ້"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"ມົດ"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"ເຜິ້ງ"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"ແມງເຕົ່າທອງ"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"ປາ"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"ປາເຂດຮ້ອນ"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"ປາປັກເປົ້າ"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"ເຕົ່າ"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"ໄຂ່ຟັກ"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"ໄກ່ນ້ອຍ"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"ໜ້າໄກ່ນ້ອຍ"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"ນົກ"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"ເພນກວິນ"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"ໂຄອາລາ"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"ພຸນເດິນ"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"ອູດອາຣາບຽນ"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"ອູດແບັກທຣຽນ"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"ໂລມາ"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"ໜ້າໜູ"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"ໜ້າງົວ"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"ໜ້າເສືອ"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"ໜ້າກະຕ່າຍ"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"ໜ້າແມວ"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"ໜ້າມັງກອນ"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"ວານພົ່ນນ້ຳ"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"ໜ້າມ້າ"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"ໜ້າລິງ"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"ໜ້າໝາ"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"ໜ້າໝູ"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"ໜ້າກົບ"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"ໜ້າໜູແຮມສະເຕີ"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"ໜ້າໝາປ່າ"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"ໜ້າໝີ"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"ໜ້າແພນດ້າ"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"ດັງໝູ"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"ຮອຍຕີນ"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"ຕາ"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"ຫູ"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"ດັງ"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"ປາກ"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"ລິ້ນ"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"ຫຼັງມືຊີ້ຂຶ້ນເທິງ"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"ຫຼັງມືຊີ້ລົງລຸ່ມ"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"ຫຼັງມືຊີ້ໄປຊ້າຍ"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"ຫຼັງມືຊີ້ໄປຂວາ"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"ກຳປັ້ນ"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"ມືໂບກໄປມາ"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"ມືເຮັດທ່າໂອເຄ"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"ຍົກໂປ້ໃຫ້"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"ຊິ້ວນີ້ໂປ້ລົງ"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"ຕົບມື"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"ແບມືອອກ"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"ມຸງກຸດ"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"ໝວກຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"ແວ່ນຕາ"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"ເນັກໄທ"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"ເສືອຢືນ"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"ໂສ້ງຢີນ"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"ຊຸດເດຣສ"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"ກິໂມໂນ"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"ບິກີນີ"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"ເສື້ອຜ້າຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"ກະເປົາ"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"ກະເປົາ"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"ກະເປົາ"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"ເກີບຜູ່ຊາຍ"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"ເກີບກິລາ"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"ເກີບສົ້ນສູງ"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"ເກີບຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"ເກີດບູດຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"ຮອຍຕີນ"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"ຄົນເປັນເງົາ"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"ຄົນເປັນເງົາ"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"ຜູ່ຊາຍ"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"ຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"ຜູ່ຊາຍ"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"ຜູ່ຍິງ"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"ຄອບຄົວ"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"ຊາຍຍິງຈູງມືກັນ"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"ຊາຍສອງຄົນຈູງມືກັນ"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"ຍິງສອງຄົນຈູງມືກັນ"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"ເຈົ້າໜ້າທີ່ຕຳຫຼວດ"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"ຜູ່ຍິງໃສ່ຫູກະຕ່າຍ"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"ເຈົ້າສາວມີຜ້າຄຸມໜ້າ"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"ຄົນຜົມສີບລອນ"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"ຄົນໃສ່ໝວກກົວປີເໝົາ"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"ຄົນໃສ່ຜ້າໂພກຫົວ"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"ຄົນອາຍຸຫຼາຍກວ່າ"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"ຜູ່ຍິງອາຍຸຫຼາຍກວ່າ"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"ແອນ້ອຍ"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"ຄົນເຮັດວຽກກໍ່ສ້າງ"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"ເຈົ້າຍິງ"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"ຍັກຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"ກລອບບິນຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"ຜີ"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"ທູດແອນ້ອຍສະຫວັນ"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"ມະນຸດຕ່າງດາວ"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"ສັດປະຫລາດຕ່າງດາວ"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"ອິມປ໌"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"ຫົວກະໂຫຼກ"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"ພະນັກງານບໍລິການຂໍ້ມູນ"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"ຍາມ"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"ນັກເຕັ້ນ"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"ລິບສະຕິກ"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"ຢາທາເລັບ"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"ນວດໜ້າ"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"ຕັດຜົມ"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"ເສົາຮ້ານຕັດຜົມ"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"ເຂັມສີດຢາ"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"ຢາເມັດ"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"ຮອຍຈູບ"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"ຈົດໝາຍຮັກ"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"ແຫວນ"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"ຫິນອັນຍະມະນີ"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"ຈູບ"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"ຊໍ່ດອກໄມ້"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"ຄູ່ຮັກກັບຫົວໃຈ"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"ການແຕ່ງດອງ"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"ຫົວໃຈເຕັ້ນ"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"ອົກຫັກ"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"ສອງໃຈ"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"ຫົວໃຈເປັນປະກາຍ"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"ຫົວໃຈຂະຫຍາຍ"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"ລູກສອນປັກຫົວໃຈ"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"ໃຈສີຟ້າ"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"ໃຈສີຂຽວ"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"ໃຈສີເຫຼືອງ"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"ໃຈສີມ່ວງ"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"ຫົວໃຈກັບຣິບບອນ"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"ຫົວໃຈວົງມົນ"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"ຕົກແຕ່ງຫົວໃຈ"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"ຮູບເພັດມີຈຸດທາງກາງ"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"ຫຼອດໄຟ"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"ສັນຍາລັກໃຈຮ້າຍ"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"ລະເບີດ"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"ສັນຍາລັກນອນຫຼັບ"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"ສັນຍາລັກປະທະກັນ"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"ສັນຍາລັກເຫື່ອກະຈາຍ"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"ຢົດນ້ຳ"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"ສັນຍາລັກແລ່ນ"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"ກອງອຸດຈາລະ"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"ເບັ່ງກ້າມ"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"ດາວໝຸນ"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"ບອນລູນຄຳເວົ້າ"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"ບອນລູນຄວາມຄິດ"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"ດອກໄມ້ຂາວ"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"ສັນຍາລັກຮ້ອຍ"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"ຖົງເງິນ"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"ອັດຕາແລກປ່ຽນ"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"ສັນຍາລັກໂດລ່າແບບໜາ"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"ບັດເຄຣດິດ"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"ທະນະບັດເງິນເຢນ"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"ທັນນະບັດຮູບເງິນໂດລ່າ"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"ທະນະບັດເງິນຢູໂຣ"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"ທະນະບັດເງິນພາວດ໌"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"ເງິນຕິດປີກ"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"ແຜນວາດຊີ້ຂຶ້ນມີຮູບເງິນເຢນ"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"ບ່ອນນັ່ງ"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"ຄອມພິວເຕີສ່ວນບຸກຄົນ"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"ກະເປົາເດີນທາງ"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"ມິນິດິສ"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"ຟລັອບປີ້ດິສ"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"ອັອບຕິຄອນ ດິສ"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"ດີວີດີ"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"ໄຟລ໌ໂຟນເດີ"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"ເປີດໂຟນເດີໄຟລ໌"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"ເຈ້ຍມ້ວນທາງລຸ່ມ"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"ເຈ້ຍຫງາຍໜ້າຂຶ້ນ"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"ປະຕິທິນ"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"ປະຕິທິນຖືກຈີກ"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"ດັດຊະນີບັດ"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"ແຜນວາດຊີ້ຂຶ້ນ"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"ແຜນວາດຊີ້ລົງ"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"ແຜນວາດແບບບາ"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"ຄລິບບອດ"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"ເຂັມມຸດ"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"ເຂັມມຸດຫົວມົນ"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"ອັນໜີບເຈ້ຍ"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"ໄມ້ບັນທັດ"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"ໄມ້ບັດທັດສາມຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"ແຖບບຸກມາກ"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"ປຶ້ມບັນຊີ"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"ສະມຸດບັນທຶກ"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"ສະມຸດບັນທຶກແບບມີປົກຕົບແຕ່ງ"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"ປຶ້ມທີ່ປິດໄວ້"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"ປຶ້ມທີ່ເປີດໄວ້"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"ປຶ້ມຂຽວ"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"ປຶ້ມຟ້າ"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"ປຶ້ມສົ້ມ"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"ປຶ້ມ"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"ປ້າຍຊື່"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"ເລື່ອນ"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"ບັນທຶກ"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"ຫູໂທລະສັບ"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"ເພກເຈີ"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"ເຄື່ອງແຟັກ"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"ຈານດາວທຽມ"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"ໂທລະໂຄ່ງ"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"ໂທລະໂຂ່ງ"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"ຖາດຂາອອກ"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"ຖາດຂາເຂົ້າ"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"ພັດສະດຸ"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"ສັນຍາລັກອີເມວ"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"ຈົດໝາຍເຂົ້າ"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"ຈົດໝາຍມີລູກສອນຢູ່ທາງເທິງ"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"ກ່ອງຈົດໝາຍປິດໄວ້ມີທຸງທາງລຸ່ມ"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"ກ່ອງຈົດໝາຍປິດໄວ້ມີທຸງສະບັດ"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"ກ່ອງຈົດໝາຍເປິດໄວ້ມີທຸງສະບັດ"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"ກ່ອງຈົດໝາຍເປິດໄວ້ມີທຸງທາງລຸ່ມ"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"ຕູ້ໄປສະນີ"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"ແຕໄປສະນີ"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"ໜັງສືພິມ"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"ໂທລະສັບມືຖື"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"ໂທລະສັບມືຖືມີລູກສອນຊີ້ໄປທາງຂວາຢູ່ທາງຊ້າຍ"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"ໂໝດສັ່ນ"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"ໂທລະສັບມືຖືປິດ"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"ບໍ່ມີໂທລະສັບມືຖື"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"ຂີດສັນຍານໂທລະສັບ"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"ກ້ອງຖ່າຍຮູບ"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"ກ້ອງວິດີໂອ"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"ໂທລະພາບ"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"ວິທະຍຸ"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"ກະແຊັດວິດີໂອ"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"ລູກສອນໄຂວ່ກັນ"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"ລູກສອນໝຸນວົນຕາມເຂັມໂມງ"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"ລູກສອນໝຸນຕາມເຂັມໂມງມີເລກສູນໃນວົງມົນຢູ່ຊ້າຍລຸ່ມ"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"ລູກສອນໝຸນຕາມເຂັມໂມງລວງຕັ້ງ"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"ລູກສອນໝຸນທວນເຂັມໂມງໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"ສັນຍາລັກຄວາມແຈ້ງຕ່ຳ"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"ສັນຍາລັກຄວາມແຈ້ງສູງ"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"ລຳໂພງມີຂີດຂ້າ"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"ລຳໂພງ"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"ລຳໂພງມີຄື້ນສຽງອັນນຶ່ງ"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"ລຳໂພງມີຄື້ນສຽງສາມອັນ"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"ແບັດເຕີຣີ"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"ປລັກໄຟຟ້າ"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"ແວ່ນຂະຫຍາຍສ່ອງໄປທາງຊ້າຍ"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"ແວ່ນຂະຫຍາຍສ່ອງໄປທາງຂວາ"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"ລັອກມີປາກກາ"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"ລັອກທີ່ປິດໄວ້ພ້ອມກະແຈ"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"ກະແຈ"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"ລັອກ"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"ລັອກທີ່ເປີດໄວ້"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"ກະດິ່ງ"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"ກະດິ່ງມີຂີດຂ້າ"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"ບຸກມາກ"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"ສັນຍາລັກລິ້ງ"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"ປຸ່ມເຄື່ອງໝາຍເລືອກ"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"ລູກສອນຊີ້ໄປທາງຊ້າຍມີຄຳວ່າ Back"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"ລູກສອນຊີ້ໄປທາງຊ້າຍມີຄຳວ່າ End"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"ຄຳວ່າ On ມີເຄື່ອງໝາຍທ້ວງກັບລູກສອນຊີ້ໄປທາງຊ້າຍແລະຂວາຢູ່ເທິງ"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"ຄຳວ່າ Soon ມີລູກສອນຊີ້ໄປທາງຂວາຢູ່ເທິງ"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"ຄຳວ່າ Top ມີລູກສອນຊີ້ໄປທາງເທິງຢູ່ເທິງ"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"ສັນຍາລັກຫ້າມຕ່ຳກວ່າສິບແປດ"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"ເລກສິບໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"ABCD ໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"abcd ໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"ໂຕເລກໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"ໃສ່ສັນຍາລັກເພື່ອສັນຍາລັກ"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"ໂຕອັກສອນລາຕິນໃນຂອບສີ່ຫຼ່ຽມ"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"ໄຟ"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"ໄຟສາຍ"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"ປະແຈ"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"ຄ້ອນ"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"ນັອດ"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"ມີດ"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"ປືນສັ້ນ"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"ກ້ອງຈຸລະທັດ"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"ກ້ອງສ່ອງ"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"ບານຄຣິສຕັນ"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"ດາວຫົກຫຼ່ຽມມີຈ້ຳທາງກາງ"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"ສັນຍາລັກພາສາຍີ່ປຸ່ນສຳລັບຜູ່ເລີ່ມຕົ້ນ"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"ສັນຍາລັກສາມງ່າມ"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"ປຸ່ມສີ່ຫຼ່ຽມສີດຳ"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"ປຸ່ມສີ່ຫຼ່ຽມສີຂາວ"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"ວົງມົນໃຫຍ່ສີແດງ"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"ວົງມົນໃຫຍ່ສີຟ້າ"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"ເພັດເມັດໃຫຍ່ສີສົ້ມ"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"ເພັດເມັດໃຫຍ່ສີຟ້າ"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"ເພັດເມັດນ້ອຍສີສົ້ມ"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"ເພັດເມັດນ້ອຍສີຟ້າ"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"ສາມຫຼ່ຽມສີແດງຊີຂຶ້ນ"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"ສາມຫຼ່ຽມສີແດງຊີລົງ"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"ສາມຫຼ່ຽມນ້ອຍສີແດງຊີຂຶ້ນ"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"ສາມຫຼ່ຽມນ້ອຍສີແດງຊີລົງ"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລານຶ່ງໂມງ"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສອງໂມງ"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສາມໂມງ"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສີ່ໂມງ"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາຫ້າໂມງ"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາຫົກໂມງ"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາເຈັດໂມງ"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາແປດໂມງ"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາເກົ້າໂມງ"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບໂມງ"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບເອັດໂມງ"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບສອງໂມງ"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລານຶ່ງໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສອງໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສາມໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສີ່ໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາຫ້າໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາຫົກໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາເຈັດໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາແປດໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາເກົ້າໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບເອັດໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"ໂມງພ້ອມເຂັມຊີ້ໄປເວລາສິບສອງໂມງເຄິ່ງ"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"ພູເຂົາໄຟຟູຈິ"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"ຫໍຄອຍໂຕກຽວ"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"ເທພີເສລີພາບ"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"ເງົາປະເທດຍີ່ປຸ່ນ"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"ໂມອາຍ"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"ໜ້າຍິ້ມເຫັນແຂ້ວ"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"ໜ້າຕາຍິ້ມເຫັນແຂ້ວ"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"ໜ້າພ້ອມດ້ວຍນ້ຳຕາແຫ່ງຄວາມສຸກ"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"ໜ້າຍິ້ມພ້ອມກັບອ້າປາກ"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"ໜ້າຕາຍິ້ມພ້ອມກັບອ້າປາກ"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"ໜ້າຕາຍິ້ມພ້ອມກັບເຫື່ອຕົກ"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"ໜ້າຕາຍິ້ມພ້ອມກັບຫຼັບຕາ"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"ໜ້າຍິ້ມພ້ອມມີວົງແຫວນເທິງຫົວ"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"ໜ້າຍິ້ມມີເຂົາ"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"ໜ້າຍິ້ມກະພິບຕາຂ້າງນຶ່ງ"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"ໜ້າຕາຍິ້ມແຍ້ມ"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"ໜ້າຍິ້ມລິ້ນເລຍສົບ"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"ໜ້າຜ່ອນຄາຍ"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"ໜ້າຍິ້ມພ້ອມຕາເປັນຫົວໃຈ"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"ໜ້າຍິ້ມໃສ່ແວ່ນຕາ"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"ໜ້າຍິ້ມມີເລດສະໄນ"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"ໜ້າຊື່ໆ"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"ໜ້າບໍ່ມີຄວາມຮູ້ສຶກ"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"ໜ້າບໍ່ຕະຫລົກ"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"ໜ້າມີເຫື່ອໄຫຼ"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"ໜ້າໝົ່ນໝອງ"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"ໜ້າສັບສົນ"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"ໜ້າຜູ່ຮ້າຍ"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"ໜ້າຈູບ"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"ໜ້າເປົ່າຈູບ"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"ໜ້າຈູບພ້ອມກັບຍິ້ມ"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"ໜ້າຍິ້ມພ້ອມກັບຫຼັບຕາ"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"ໜ້າແລບລິ້ນ"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"ໜ້າແລບລິ້ນຫຼັບຕາຂ້າງນຶ່ງ"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"ໜ້າແລບລິ້ນຫຼັບຕາສອງຂ້າງ"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"ໜ້າຜິດຫວັງ"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"ໜ້າເປັນກັງວົນ"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"ໜ້າໃຈຮ້າຍ"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"ໜ້າມຸ້ຍ"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"ໜ້າຮ້ອງໄຫ້"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"ໜ້າຂະໝວດ"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"ໜ້າໃຈຮ້າຍລົມອອກດັງ"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"ໜ້າຜິດຫວັງແຕ່ໂລ່ງໃຈ"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"ໜ້າມຸ້ຍອ້າປາກ"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"ໜ້າເຈັບປວດ"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"ໜ້າຢ້ານ"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"ໜ້າເມື່ອຍສຸດຂີດ"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"ໜ້າເຫງົານອນ"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"ໜ້າເມື່ອຍ"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"ໜ້າຍິ້ມແຫ້ງໆ"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"ໜ້າຮ້ອງໄຫ້ສຽງດັງ"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"ໜ້າອ້າປາກ"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"ໜ້າຕະລຶງງຽບ"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"ໜ້າອ້າປາກມີເຫື່ອຕົກ"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"ໜ້າຮ້ອງດ້ວຍຄວາມຢ້ານ"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"ໜ້າປະຫຼາດໃຈ"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"ໜ້າແດງ"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"ໜ້າເຫງົານອນ"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"ໜ້າວິງວຽນ"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"ໜ້າບໍ່ມີປາກ"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"ໜ້າໃສ່ຜ້າປິດປາກ"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"ໜ້າແມວຍິ້ມ"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"ໜ້າແມວຍິ້ມພ້ອມນ້ຳຕາແຫ່ງຄວາມສຸກ"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"ໜ້າແມວຍິ້ມພ້ອມກັບອ້າປາກ"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Smiling cat face with heart-shaped eyes"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Cat face with wry smile"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Kissing cat face with closed eyes"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Pouting cat face"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Crying cat face"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Weary cat face"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Face with no good gesture"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Face with ok gesture"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Person bowing deeply"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"See-no-evil monkey"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Hear-no-evil monkey"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Speak-no-evil monkey"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Happy person raising one hand"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"ຄົນຍົກມືຂຶ້ນສະຫຼອງ"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Person frowning"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Person with pouting face"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Person with folded hands"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Rocket"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Helicopter"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Steam locomotive"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Railway car"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"High-speed train"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"High-speed train with bullet nose"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Train"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metro"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Light rail"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Station"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Tram"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Tram car"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Bus"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Oncoming bus"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Trolleybus"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Bus stop"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Minibus"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulance"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Fire engine"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Police car"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Oncoming police car"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Taxi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Oncoming taxi"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Automobile"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Oncoming automobile"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Recreational vehicle"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Delivery truck"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"ລົດບັນທຸກພ່ວງ"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Tractor"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Monorail"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Mountain railway"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Suspension railway"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Mountain cableway"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Aerial tramway"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Ship"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Rowboat"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Speedboat"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"ໄຟສັນຍານຈະລາຈອນລວງນອນ"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Vertical traffic light"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Construction sign"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Police cars revolving light"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Triangular flag on post"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Door"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"No entry sign"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Smoking symbol"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"ສັນຍາລັກຫ້າມສູບຢາ"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Put litter in its place symbol"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Do not litter symbol"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Potable water symbol"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Non-potable water symbol"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Bicycle"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"No bicycles"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Bicyclist"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Mountain bicyclist"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Pedestrian"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"No pedestrians"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Children crossing"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Mens symbol"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Womens symbol"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Restroom"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Baby symbol"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Toilet"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"Water closet"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Shower"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Bath"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Bathtub"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Passport control"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Customs"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Baggage claim"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Left luggage"</string> +</resources> diff --git a/java/res/values-lo-rLA/strings-talkback-descriptions.xml b/java/res/values-lo-rLA/strings-talkback-descriptions.xml index 681a21370..c6140fda9 100644 --- a/java/res/values-lo-rLA/strings-talkback-descriptions.xml +++ b/java/res/values-lo-rLA/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"ບໍ່ມີການໃສ່ຂໍ້ຄວາມ"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ແກ້ໄຂ <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ເປັນ <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ດຳເນີນການແກ້ໄຂອັດຕະໂນມັດ"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"ລະຫັດກະແຈ %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift ເປີດນຳໃຊ້ຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock ເປີດຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"ສັນຍາລັກເພີ່ມເຕີມ"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"ສັນຍາລັກ"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"ລຶບ"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"ສັນຍາລັກ"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"ໂຕອັກສອນ"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"ກ່ອນໜ້ານີ້"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift ເປີດນຳໃຊ້ຢູ່"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock ເປີດນຳໃຊ້ຢູ່"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift ປິດນຳໃຊ້ຢູ່"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"ໂຫມດສັນຍາລັກ"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"ໂໝດສັນຍາລັກເພີ່ມເຕີມ"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"ໂຫມດໂຕອັກສອນ"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"ໂຫມດໂທລະສັບ"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"ໂຫມດສັນຍາລັກໂທລະສັບ"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"ສະຖານທີ່"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"ສັນຍາລັກ"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"ອີໂມຕິຄອນ"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index 76fe5e042..bca49e573 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"ຕົວເລືອກການປ້ອນຂໍ້ມູນ"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ເບິ່ງທີ່ຊື່ຂອງລາຍຊື່ຜູ່ຕິດຕໍ່"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ໂຕຊ່ວຍສະກົດໃຊ້ຂໍ້ມູນຈາກລາຍການຂອງລາຍຊື່ຜູ່ຕິດຕໍ່ຂອງທ່ານ"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"ສັ່ນເຕືອນເມື່ອພິມ"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"ແນະນຳລາຍຊື່ຜູ່ຕິດຕໍ່"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ໃຊ້ຊື່ຈາກລາຍຊື່ຜູ່ຕິດຕໍ່ສຳລັບການແນະນຳ ແລະ ການຊ່ວຍແກ້ຄຳ"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"ຄຳແນະນຳຕາມການນຳໃຊ້ຂອງທ່ານ"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"ຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດ"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"ກົດທີ່ປຸ່ມຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດແລ້ວຕາມດ້ວຍການຍະຫວ່າງ"</string> <string name="auto_cap" msgid="1719746674854628252">"ເຮັດໂຕພິມໃຫຍ່ອັດຕະໂນມັດ"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"ພາສາການປ້ອນຂໍ້ມູນ"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"ກົດອີກຄັ້ງເພື່ອບັນທຶກ"</string> <string name="has_dictionary" msgid="6071847973466625007">"ມີວັດຈະນານຸກົມ"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"ເປີດນຳໃຊ້ຄຳຕິຊົມຈາກຜູ່ໃຊ້"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"ຊ່ວຍເພີ່ມປະສິດທິພາບໂຕແກ້ໄຂການປ້ອນຂໍ້ມູນ ໂດຍການສົ່ງສະຖິຕິການນຳໃຊ້ ແລະການລາຍການຂໍ້ຜິດພາດໂດຍອັດຕະໂນມັດ"</string> <string name="keyboard_layout" msgid="8451164783510487501">"ສີສັນແປ້ນພິມ"</string> <string name="subtype_en_GB" msgid="88170601942311355">"ອັງກິດ (ສະຫະລາດຊະອານາຈັກ)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"ອັງກິດ (ສະຫະລັດຯ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"ເປີດນຳໃຊ້"</string> <string name="not_now" msgid="6172462888202790482">"ບໍ່ແມ່ນຕອນນີ້"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ຮູບແບບການປ້ອນຂໍ້ມູນທີ່ຄືກັນມີຢູ່ແລ້ວ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"ໂໝດການສຶກສາ Usability"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ໄລຍະເວລາຂອງການກົດປຸ່ມ"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ໄລຍະເວລາຂອງການສັ່ນໃນການກົດປຸ່ມ"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ລະດັບສຽງຂອງການກົດປຸ່ມ"</string> diff --git a/java/res/values-lt/strings-action-keys.xml b/java/res/values-lt/strings-action-keys.xml index 39b3894be..20d5ca04d 100644 --- a/java/res/values-lt/strings-action-keys.xml +++ b/java/res/values-lt/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Anks."</string> <string name="label_done_key" msgid="7564866296502630852">"Atl."</string> <string name="label_send_key" msgid="482252074224462163">"Siųs."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pris."</string> <string name="label_wait_key" msgid="5891247853595466039">"Lauk."</string> </resources> diff --git a/java/res/values-lt/strings-talkback-descriptions.xml b/java/res/values-lt/strings-talkback-descriptions.xml index 07119cb65..4e7f6afef 100644 --- a/java/res/values-lt/strings-talkback-descriptions.xml +++ b/java/res/values-lt/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nėra įvesto teksto"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> pataiso <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> į <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> atlieka automatinį taisymą"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Klavišo kodas %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Klavišas „Shift“"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Klavišas „Shift“ įjungtas (palieskite, kad išjungtumėte)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Įjungtos didžiosios raidės (palieskite, kad išjungtumėte)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Daugiau simbolių"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Klavišas „Shift“"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboliai"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Klavišas „Shift“"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Ištrinti"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simbolių klavišas"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Raidžių klavišas"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Ankstesnis"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Klavišas „Shift“ įgalintas"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Didžiųjų raidžių klavišas įgalintas"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Klavišas „Shift“ išjungtas"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Simbolių režimas"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Daugiau simbolių režimas"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Raidžių režimas"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefonų numerių režimas"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefonų numerių simbolių režimas"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Vietos"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboliai"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Jaustukai"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index c049a165e..01ec1bea7 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rašybos tikrinimo progr. naudoja įrašus, esančius kontaktų sąraše"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Siūlyti kontaktų vardus"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Suasmeninti pasiūlymai"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Tšk. ir tarp. pal. dukart"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dukart palietus tarpo klavišą įterpiamas taškas ir tarpas."</string> <string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Įvesties kalbos"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Jei norite išsaugoti, palieskite dar kartą"</string> <string name="has_dictionary" msgid="6071847973466625007">"Žodynas galimas"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Įgalinti naudotojų atsiliepimus"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Padėkite patobulinti šią įvesties metodo redagavimo priemonę automatiškai siųsdami naudojimo statistiką ir strigčių ataskaitas"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatūros tema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Anglų k. (JK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Anglų k. (JAV)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Įgalinti"</string> <string name="not_now" msgid="6172462888202790482">"Ne dabar"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Toks pat įvesties stilius jau yra: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tinkamumo tyrimo režimas"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavišo ilgo paspaudimo delsa"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrav. paspaudus mygt. trukmė"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Garso paspaudus mygt. garsumas"</string> diff --git a/java/res/values-lv/strings-action-keys.xml b/java/res/values-lv/strings-action-keys.xml index c2fbda26b..a3ada38a6 100644 --- a/java/res/values-lv/strings-action-keys.xml +++ b/java/res/values-lv/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Iepr."</string> <string name="label_done_key" msgid="7564866296502630852">"Gatavs"</string> <string name="label_send_key" msgid="482252074224462163">"Sūtīt"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauze"</string> <string name="label_wait_key" msgid="5891247853595466039">"Gaidīt"</string> </resources> diff --git a/java/res/values-lv/strings-talkback-descriptions.xml b/java/res/values-lv/strings-talkback-descriptions.xml index eb32e9dcc..e68d83afb 100644 --- a/java/res/values-lv/strings-talkback-descriptions.xml +++ b/java/res/values-lv/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Teksts nav ievadīts"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Nospiežot taustiņu <xliff:g id="KEY_NAME">%1$s</xliff:g>, “<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>” tiek labots uz “<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>”."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Taustiņam <xliff:g id="KEY_NAME">%1$s</xliff:g> ir automātiskas labošanas funkcija."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Taustiņu kods %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Pārslēgšanas taustiņš"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Pārslēgšanas režīms ir iespējots (pieskarieties, lai atspējotu)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Burtslēgs ir iespējots (pieskarieties, lai atspējotu)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Citi simboli"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Pārslēgšanas taustiņš"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboli"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Pārslēgšanas taustiņš"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Dzēšanas taustiņš"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simboli"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Burti"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Atpakaļ"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Pārslēgšanas režīms ir iespējots"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Burtslēgs ir iespējots"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Pārslēgšanas režīms ir atspējots"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Simbolu režīms"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Citu simbolu režīms"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Burtu režīms"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Tālruņa režīms"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Tālruņa simbolu režīms"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Vietas"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboli"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emocijzīmes"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 901c3486f..bc4e3e1ea 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pareizrakst. pārbaudītājs lieto ierakstus no kontaktp. saraksta."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Ieteikt kontaktp. vārdus"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalizēti ieteikumi"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dubultpiesk. = punkts"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Divreiz pieskaroties atst. taustiņam, ievada punktu un atstarpi."</string> <string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Ievades valodas"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Pieskarieties vēlreiz, lai saglabātu."</string> <string name="has_dictionary" msgid="6071847973466625007">"Ir pieejama vārdnīca."</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Iespējot lietotāju atsauksmes"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Palīdziet uzlabot šo ievades metodes redaktoru, automātiski nosūtot lietojuma statistiku un avāriju pārskatus."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastatūras motīvs"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Angļu valoda (Lielbritānija)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Angļu valoda (ASV)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Iespējot"</string> <string name="not_now" msgid="6172462888202790482">"Vēlāk"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Šāds ievades stils jau pastāv: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Lietojamības izpētes režīms"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Taustiņa ilgās nosp. noildze"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Taust. nosp. vibrācijas ilgums"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Taustiņu nosp. skaņas skaļums"</string> diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml index 1588534c9..ab2f2625a 100644 --- a/java/res/values-mk/strings.xml +++ b/java/res/values-mk/strings.xml @@ -22,7 +22,6 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- no translation found for english_ime_input_options (3909945612939668554) --> <skip /> - <!-- no translation found for english_ime_research_log (8492602295696577851) --> <skip /> <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) --> <skip /> @@ -140,9 +139,7 @@ <skip /> <!-- no translation found for has_dictionary (6071847973466625007) --> <skip /> - <!-- no translation found for prefs_enable_log (6620424505072963557) --> <skip /> - <!-- no translation found for prefs_description_log (7525225584555429211) --> <skip /> <!-- no translation found for keyboard_layout (8451164783510487501) --> <skip /> @@ -198,7 +195,6 @@ <skip /> <!-- no translation found for custom_input_style_already_exists (8008728952215449707) --> <skip /> - <!-- no translation found for prefs_usability_study_mode (1261130555134595254) --> <skip /> <!-- no translation found for prefs_key_longpress_timeout_settings (6102240298932897873) --> <skip /> diff --git a/java/res/values-mn-rMN/strings-action-keys.xml b/java/res/values-mn-rMN/strings-action-keys.xml index 77b8f2c05..a855386d0 100644 --- a/java/res/values-mn-rMN/strings-action-keys.xml +++ b/java/res/values-mn-rMN/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Өмнөх"</string> <string name="label_done_key" msgid="7564866296502630852">"Дууссан"</string> <string name="label_send_key" msgid="482252074224462163">"Илгээх"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Түр зогсоох"</string> <string name="label_wait_key" msgid="5891247853595466039">"Хүлээх"</string> </resources> diff --git a/java/res/values-mn-rMN/strings-emoji-descriptions.xml b/java/res/values-mn-rMN/strings-emoji-descriptions.xml new file mode 100644 index 000000000..f09a51dd9 --- /dev/null +++ b/java/res/values-mn-rMN/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Зохиогчийн эрхийн тэмдэг"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Бүртгэгдсэн тэмдэг"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Давхар анхаарлын тэмдэг"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Анхаарал, асуултын тэмдэг"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Худалдааны тэмдэг"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Мэдээллийн эх сурвалж"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Зүүн баруун сум"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Дээш доош сум"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Баруун хойд сум"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Зүүн хойд сум"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Зүүн өмнөд сум"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Баруун өмнөд сум"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Зүүн дэгээтэй сум"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Баруун дэгээтэй сум"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Үзэх"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Элсэн цаг"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Баруун заасан хар давхар гурвалжин"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Зүүн заасан хар давхар гурвалжин"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Дээш заасан хар давхар гурвалжин"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Доош заасан хар давхар гурвалжин"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Сэрүүлэгтэй цаг"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Урссан элсэн цаг"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Дугуйлсан латин том m үсэг"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Хар жижиг дөрвөлжин"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Цагаан жижиг дөрвөлжин"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Баруун заасан хар гурвалжин"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Зүүн заасан хар гурвалжин"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Цагаан дунд дөрвөлжин"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Хар дунд дөрвөлжин"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Цагаан дунд жижиг дөрвөлжин"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Хар дунд жижиг дөрвөлжин"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Цацрагтай хар нар"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Үүл"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Хар утас"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Чектэй саналын хайрцаг"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Борооны дусалтай шүхэр"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Халуун ундаа"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Цагаан дээш заасан долоовор"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Цагаан инээмсэглэсэн царай"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Хонины орд"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Үхрийн орд"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Ихрийн орд"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Хавчийн орд"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Арслангийн орд"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Охины орд"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Жинлүүрийн орд"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Хилэнцийн орд"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Нумын орд"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Матрын орд"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Бумбын орд"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Загасны орд"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Хар гил хөзөр"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Хар цэцэг хөзөр"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Хар бундан хөзөр"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Хар дөрвөлжин хөзөр"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Халуун рашаан"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Хар дахин боловсруулах тэмдэг"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Тэргэнцэрийн тэмдэг"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Зангуу"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Сануулга тэмдэг"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Өндөр хүчдэлийн тэмдэг"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Дунд зэргийн цагаан тойрог"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Дунд зэргийн хар тойрог"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Хөлбөмбөгийн бөмбөг"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Бейсбол"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Цасгүй цасан хүн"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Үүлний цаадах нар"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Тэнгэрийн мөрөн орд"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Орохыг хориглоно"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Сүм"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Усан оргилуур"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Нүхэндэх туг"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Далбаат завь"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Майхан"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Түлшний насос"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Хар хайч"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Цагаан хүнд чек тэмдэг"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Онгоц"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Дугтуй"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Өргөсөн нударга"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Өргөсөн гар"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Ялалтын гар"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Харандаа"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Хар хошуу"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Хүнд чек тэмдэг"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Хүнд үржүүлэх х"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Цацраг"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Найман үзүүрт од"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Найман үзүүртэй хар од"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Цасан ширхэг"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Цацраг"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Загалмайн тэмдэг"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Сөрөг квадрат дарах тэмдэг"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Хар асуултын тэмдэгэн чимэглэл"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Цагаан асуултын тэмдэгэн чимэглэл"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Цагаан анхаарлын тэмдэгэн чимэглэл"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Хүнд анхаарлын тэмдэгэн симбол"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Хүнд хар зүрх"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Хүнд нэмэх тэмдэг"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Хүнд хасах тэмдэг"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Хүнд хуваах тэмдэг"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Хар баруун сум"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Нуман гогцоо"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Давхар нуман гогцоо"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Баруун зааж дээш эргэсэн сум"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Баруун зааж доош эргэсэн сум"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Зүүн заасан хар сум"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Дээш заасан хар сум"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Доош заасан хар сум"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Хар том дөрвөлжин"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Цагаан том дөрвөлжин"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Цагаан дунд од"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Хүнд том тойрог"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Долгионт зураас"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Хэсэг шилжих тэмдэг"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Дугуй идеограф баяр хүргэлт"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Дугуй идеограф нууц"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Mahjong -н улаан луу"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Хөзөрийн хар хүн"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Цусны А бүлэг"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Цусны В бүлэг"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Цусны O бүлэг"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Машины зогсоол"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Цусны AB бүлэг"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"Дөрвөлжин CL"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Дөрвөлжин гоё"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Дөрвөлжин чөлөөтэй"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"Дөрвөлжин ID"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"Дөрвөлжин шинэ"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"Дөрвөлжин N G"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"Дөрвөлжин OK"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"Дөрвөлжин SOS"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"Анхаарлын тэмдэгтэй дөрвөлжин"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"Дөрвөлжин vs"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Энд дөрвөлжин катакана"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Дөрвөлжин катакана үйлчилгээ"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Дөрвөлжин идеограф төлбөргүй"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Дөрвөлжин идеограф захиалсан суудал"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Дөрвөлжин идеограф хориг"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Дөрвөлжин идеограф орон тоо"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Дөрвөлжин идеограф зөвшөөрөл"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Дөрвөлжин идеограф бүрэн эзэлсэн"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Дөрвөлжин идеограф төлсөн"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Дөрвөлжин идеограф сараар"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Дөрвөлжин идеограф аппликешн"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Дөрвөлжин идеограф хөнгөлөлт"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Дөрвөлжин идеограф бизнесийн"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Дугуй идеограф давуу тал"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Дугуй идеограф зөвшөөрөх"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Циклон"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Будантай"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Хаасан шүхэр"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Одтой шөнө"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Уулын дээр нар мандах"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Нар мандах"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Үдшийн хот"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Барилга дээр нар жаргах"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Солонго"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Шөнийн гүүр"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Усны давалгаа"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Галт уул"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Сүүн зам"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Дэлхий бөмбөрцөг Европ Африк"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Дэлхий бөмбөрцөг Америк"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Дэлхий бөмбөрцөг Ази-Австрали"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Меридантай бөмбөрцөг"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Шинэ сарны симбол"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Мандах хавирган сарны тэмдэг"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Эхний улирлын сарны тэмдэг"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Мандах бөгтөр сарны тэмдэг"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Бүтэн сарны тэмдэг"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Жаргах бөгтөр сарны тэмдэг"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Сүүлийн улирлын сарны тэмдэг"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Жаргах хавирган сарны тэмдэг"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Хавирган сар"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Нүүртэй шинэ сар"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Нүүртэй эхний улирлын сар"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Нүүртэй сүүлийн улирлын сар"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Нүүртэй бүтэн сар"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Нүүртэй нар"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Гялалзах од"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Сүүлт од"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Туулайн бөөр"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Суулгац"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Мөнх ногоон мод"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Навчит мод"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Далдуу мод"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Кактус"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Алтанзул"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Интоорын дэлбээ"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Сарнай"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Хибискус"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Наранцэцэг"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Дэлбээ"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Эрдэнэ шишийн түрүү"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Цагаан будааны түрүү"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Ургамал"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Дөрвөн навчит хошоонгор"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Агч модны навч"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Унасан навч"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Сэрчигнэх навч"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Мөөг"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Улаан лооль"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Чэс"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Усан үзэм"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Амтат гуа"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Тарвас"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Мандарин"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Лемон"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Банана"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Хан боргоцой"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Улаан алим"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Ногоон алим"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Лийр"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Тоор"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Интоор"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Гүзээлзгэнэ"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Гамбургер"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Пиццаны зүсэм"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Ястай мах"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Тахианы хөл"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Будааны жигнэмэг"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Будааны бөмбөлөг"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Болгосон будаа"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Кари болон будаа"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Ууран аяга"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Шпагетти"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Талх"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Шарсан төмс"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Шарсан чихэрлэг төмс"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Данго"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oден"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Суши"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Шарсан сам хорхой"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Мушгай загасан бялуу"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Зөөлөн зайрмаг"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Хуссан мөс"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Зайрмаг"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Донат"</string> + <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_1F36E" msgid="4630541402785165902">"Шар тос"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Зөгийн бал"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Үелсэн бялуу"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Хоолны сав"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Хоолны тогоо"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Хоол хийх"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Хутга сэрээ"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Бариулгүй цайны аяг"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Сакены лонх болон хундага"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Виноны хундага"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Коктэйлийн хундага"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Халуун орны ундаа"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Пивоны аяга"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Жингэнэсэн пивоны аяга"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Хүүхдийн лонх"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Тууз"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Боодолтой бэлэг"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Төрсөн өдрийн бялуу"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Жак-O-дэнлүү"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Зул сарын гацуур"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Эцэг Христийн баяр"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Галын наадам"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Бенгалийн гал"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Шаар"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Үдэшлэгийн салют"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Чихрэн бөмбөлөг"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Taнабата мод"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Солисон тугнууд"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Нарс модон чимэглэл"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Япон хүүхэлдэй"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Мөрөг загас стример"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Салхины ая"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Сар харах ёслол"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Сургуулийн үүргэвч"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Төгсөгчийн малгай"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Тойруулгын модон морь"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Чөтгөрийн дугуй"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Галзуу хулгана"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Загасны уураг болон загас"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Микрофон"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Кино зургийн аппарат"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Кино"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Чихэвч"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Зураачийн палет"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Бортого малгай"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Циркийн майхан"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Тасалбар"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Кадрын самбар"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Урлагийн тоглолт"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Видео тоглоом"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Шууд хит"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Слот машин"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Билльярд"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Тоглоомын үхэл"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Боулинг"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Хөзрийн цэцэг"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Хөгжмийн нот"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Олон хөгжмийн нот"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Саксофон"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Гитар"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Хөгжмийн даруул"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Бүрээ"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Хийл"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Хөгжмийн оноо"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Гүйдэг цамц, хүрээ"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Теннисны ракет болон бөмбөг"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Цана болон цанын гутал"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Сагсан бөмбөг болон цагираг"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Шоотой туг"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Сноубордчин"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Гүйгч"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Сөрфер"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Цом"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Морин уралдаан"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Америк хөл бөмбөг"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Регби хөл бөмбөг"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Сэлэгч"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Байшин"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Цэцэрлэгтэй байшин"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Оффисын барилга"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Японы шуудан"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Европын шуудан"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Эмнэлэг"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Банк"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Автомат теллер машин"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Зочид буудал"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Секс буудал"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Ая тухтай дэлгүүр"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Сургууль"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Их дэлгүүр"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Үйлдвэр"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Izakaya дэнлүү"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Японы цайз"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Европын цайз"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Харх"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Хулгана"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Шар"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Усны одос"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Үнээ"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Ирвэс"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Молтогчин"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Муур"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Луу"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Матар"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Халим"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Эмгэн хумс"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Могой"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Морь"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Хуц"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Ямаа"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Хонь"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Сармагчин"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Азарган тахиа"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Тахиа"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Нохой"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Гахай"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Зэрлэг гахай"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Заан"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Наймаалж"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Мушгиа дун"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Цох"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Шоргоолж"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Зөгий"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Алтан тэмээ"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Загас"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Халуун орны загас"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Нохой загас"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Яст мэлхий"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Ангаахай"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Дэгдээхий"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Урдаас харсан дэгдээхэй"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Шувуу"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Пенгвин"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Коала"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Пүүдл"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Нэг бөхт тэмээ"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Хоёр бөхт тэмээ"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Делфин"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Хулганы нүүр"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Үнээний нүүр"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Барын нүүр"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Молтогчны нүүр"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Муурны нүүр"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Лууны нүүр"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Ус оргилуулах халим"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Морины нүүр"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Сармагчны нүүр"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Нохойны нүүр"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Гахайн нүүр"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Мэлхийн нүүр"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Хамстерийн нүүр"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Чонын нүүр"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Баавгайн нүүр"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Пандагийн нүүр"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Гахайн хамар"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Саврын мөр"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Нүд"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Чих"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Хамар"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Ам"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Хэл"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Цагаан дээр дээш заасан долоовор"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Цагаан дээр доош заасан долоовор"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Цагаан дээр зүүн заасан долоовор"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Цагаан дээр баруун заасан долоовор"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Зангидсан гарын тэмдэг"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Даллах гарын тэмдэг"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Ok гарын тэмдэг"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Эрхий дээш тэмдэг"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Эрхий доош тэмдэг"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Алга ташсан тэмдэг"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Нээлттэй гарын тэмдэг"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Титэм"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Эмэгтэй малгай"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Нүдний шил"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Зангиа"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Футболк"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Жинс"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Даашинз"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Кимоно"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Бикини"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Эмэгтэй хувцас"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Цүнх"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Гар цүнх"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Даалин"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Эрэгтэй гутал"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Биеийн тамирын гутал"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Өндөр өсгийт"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Эмэгтэй сандаал"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Эмэгтэй түрийтэй гутал"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Хөлийн мөр"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Сүүдрэн хүн"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Сүүдрэн хүмүүс"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Хөвгүүн"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Охин"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Эр хүн"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Эм хүн"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Гэр бүл"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Гар хөтлөлцсөн эр, эм"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Гар хөтлөлцсөн хоёр эр"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Гар хөтлөлцсөн хоёр эм"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Цагдаа"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Бөжин ээмэгтэй эмэгтэй"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Хуримын нөмрөгтэй бүсгүй"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Шаргал үст"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Тоорцогтой эр"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Турбантай эр"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Настай эр"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Настай эм"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Хүүхэд"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Барилгын ажилчин"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Гүнж"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Японы мангас"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Японы чөтгөр"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Сүнс"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Хүүхдийн элч тэнгэр"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Харь гаригийн хүн"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Харь гаригийн мангас"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Бяцхан чөтгөр"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Гавал"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Мэдээллийн ажилтан"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Харуул"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Бүжигчин"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Уруулын будаг"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Хумсны будаг"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Нүүрний массаж"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Үс засалт"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Үсчний реклам"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Тариур"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Эм"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Үнсэлтийн мөр"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Хайрын захиа"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Бөгж"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Эрдэнийн чулуу"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Үнсэлт"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Цэцгийн баглаа"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Зүрхтэй хосууд"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Хурим"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Цохилох зүрх"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Урагдсан зүрх"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Хоёр зүрх"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Оргилуун зүрх"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Томрох зүрх"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Сумтай зүрх"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Цэнхэр зүрх"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Ногоон зүрх"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Шар зүрх"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Нил ягаан өнгийн зүрх"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Туузтай зүрх"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Эргэлдэх зүрх"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Зүрхний чимэглэл"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Цэгтэй даймонд"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Цахилгаан чийдэнгийн шил"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Уурлах тэмдэг"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Бөмбөг"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Унтах тэмдэг"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Мөргөлдөх тэмдэг"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Цацрах хөлсний тэмдэг"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Дусал"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Налуу тэмдэг"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Овоолсон баас"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Булчинтай гар"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Нойрмог тэмдэг"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Үг хэлэх бөмбөлөг"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Бодлын бөмбөг"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Цагаан цэцэг"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Зуун оноо тэмдэг"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Мөнгөний уут"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Валютын арилжаа"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Хүнд долларын тэмдэг"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Кредит карт"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Иен тэмдэгтэй дэвсгэрт"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Долларын тэмдэгт"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Евро тэмдэгтэй дэвсгэрт"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Фунт тэмдэгтэй дэвсгэрт"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Далавчтай мөнгө"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Иен тэмдэгтэй дээш чиглэсэн граф"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Суудал"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Хувийн компьютер"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Чемодан"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Минидиск"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Уян диск"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Оптик диск"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"Dvd"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Файлын хавтас"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Нээлттэй файлын хавтас"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Хуйларсан хуудас"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Дээшээ харсан хуудас"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Календарь"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Урагдсан календарь"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Картын индекс"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Дээш чиглэсэн граф"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Доош чиглэсэн граф"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Хөндөл граф"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Түр санах ой"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Цаас хатгагч"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Цаас хатгагч"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Цаасны клип"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Шулуун шугам"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Гурвалжин шугам"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Хавчуургын таб"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Булшны чулуу"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Тэмдэглэлийн дэвтэр"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Гоёлтой тэмдэглэлийн дэвтэр"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Хаалттай ном"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Нээлттэй ном"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Ногоон ном"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Цэнхэр ном"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Улбар шар өнгийн хавтастай ном"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Ном"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Нэрний тэмдэг"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Гүйлгэх"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Мемо"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Утас хүлээн авагч"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Пэйжер"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Факс машин"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Хиймэл дагуулын антенн"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Нийтэд зарлах чанга яригч"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Хөгжөөн дэмжлэгийн мегафон"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Явсан бичгийн тавиур"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Ирсэн бичгийн тавиур"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Багц"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"И-мэйл тэмдэг"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Ирж буй дугтуй"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Доош сумтай дугтуй"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Доошлуулсан тугтай шуудангийн хайрцаг"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Босгосон тугтай хаагдсан шуудангийн хайрцаг"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Босгосон тугтай нээлттэй шуудангийн хайрцаг"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Доошлуулсан тугтай нээлттэй шуудангийн хайрцаг"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Шуудангийн хайрцаг"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Шуудангийн бүрээ"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Сонин"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Гар утас"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Зүүн талдаа баруун заасан сумтай гар утас"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Чичирхийллийн горим"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Гар утас унтраах"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Гар утас болохгүй"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Хөндөлтэй антенн"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Камер"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Видео камер"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Телевиз"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Радио"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Видео кассет"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Мушгирсан баруун сум"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Зөв баруун, зүүн эргэсэн дугуй сум"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"зөв баруун, зүүн эргэсэн дугуй сум"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Зөв доош, дээш эргэсэн дугуй сум"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Буруу доош, дээш эргэсэн дугуй сум"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Бага гэрэлтүүлэгтэй тэмдэг"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Их гэрэлтүүлэгтэй тэмдэг"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Дарсан зураастай чанга яригч"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Чанга яригч"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Нэг дууны долгиотой чанга яригч"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Гурван дууны долгиотой чанга яригч"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Батерей"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Цахилгаан залгуур"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Зүүн-заасан томруулагч шил"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Баруун-заасан томруулагч шил"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Бэхэн үзэгтэй түгжээ"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Түлхүүртэй цоожлогдсон цоож"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Түлхүүр"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Түгжих"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Нээлттэй цоож"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Хонх"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Дарсан зураастай хонх"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Хавчуурга"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Холбоосын тэмдэг"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Радио товч"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Дээрээ сумтай буцах тэмдэг"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"Дээрээ сумтай төгсгөл"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"Дээрээ зүүн сумтай анхаарлын тэмдэг"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Дээрээ баруун сумтай удахгүй"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Дээрээ дээш сумтай дээр"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Арван наймаас бага хүн байхгүй тэмдэг"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Аравтын товчлуур"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Латин том үсэгтэй оруулах симбол"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Латин жижиг үсэгтэй оруулах симбол"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Тоо оруулах тэмдэг"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Символ оруулах тэмдэг"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Латин үсэгтэй оруулах тэмдэг"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Гал"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Цахилгаан бамбар"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Түлхүүр"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Алх"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Эрэг, боолт"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Хутга"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Гар буу"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Микроскоф"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Телескоф"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Кристал бөмбөг"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Цэгтэй зургаан хошуут"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Эхлэн сурагчийн Япон тэмдэг"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Сэрээний эмблем"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Хар дөрвөлжин товч"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Цагаан дөрвөлжин товч"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Том улаан дугуй"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Том цэнхэр тойрог"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Том улбар шар даймонд"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Том цэнхэр даймонд"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Жижиг улбар шар даймонд"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Жижиг цэнхэр даймонд"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Дээш заасан улаан гурвалжин"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Доош заасан улаан гурвалжин"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Дээш заасан жижиг улаан гурвалжин"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Доош заасан жижиг улаан гурвалжин"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Цаг нэг цагийг заасан"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Цаг хоёр цагийг заасан"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Цаг гурван цагийг заасан"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Цаг дөрвөн цагийг заасан"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Цаг таван цагийг заасан"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Цаг зургаан цагийг заасан"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Цаг долоон цагийг заасан"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Цаг найман цагийг заасан"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Цаг есөн цагийг заасан"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Цаг арван цагийг заасан"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Цаг арван нэгийг заасан"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Цаг арван хоёрыг заасан"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Цаг нэг гучийг заасан"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Цаг хоёр гучийг заасан"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Цаг гурав гучийг заасан"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Цаг дөрөв гучийг заасан"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Цаг тав гучийг заасан"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Цаг зургаа гучийг заасан"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Цаг долоо гучийг заасан"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Цаг найм гучийг заасан"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Цаг ес гучийг заасан"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Цаг арав гучийг заасан"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Цаг арван нэг гучийг заасан"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Цаг арван хоёр гучийг заасан"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Фүжи уул"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Токио цамхаг"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Эрх чөлөөний хөшөө"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Японы сүүдэр"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Мояай"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Жуумалзсан царай"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Инээсэн нүдтэй жуумалзсан царай"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Баярын нулимстай царай"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Амаа ангайж инээмсэглэсэн царай"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Амаа ангайж инээсэн нүдтэй царай"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Амаа ангайж хүйтэн хөлстэй инээсэн царай"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Амаа ангайж онийсон нүдтэй инээсэн царай"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Хүрээтэй инээсэн царай"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Эвэртэй инээсэн царай"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Ирмэсэн царай"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Инээсэн нүдтэй инээсэн царай"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Амттай хоол тамшаалсан царай"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Тайвширсан царай"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Зүрхэн нүдтэй инээсэн царай"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Нарны шилтэй инээсэн царай"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Мишээсэн царай"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Төв царай"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Хувиралгүй царай"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Гайхашраагүй царай"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Хүйтэн хөлстэй царай"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Бодлогоширсон царай"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Гайхсан царай"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Сандарсан царай"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Үнсэлттэй царай"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Үнсэлт илгээх царай"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Инээсэн нүдтэй үнсэлт илгээх царай"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Аньсан нүдтэй үнсэлт илгээх царай"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Хэлээ цухуйлгасан царай"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Нүдээ ирмэж хэлээ цухуйлгасан царай"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Онийсон нүдтэй хэлээ цухуйлгасан царай"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Сэтгэл дундуур царай"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Санаа нь зовсон царай"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Ууртай царай"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Дорвогор царай"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Уйлсан царай"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Тэвчсэн царай"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Ялгуусан царай"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Сэтгэл дундуур ч тайван царай"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Амаа ангайж барайсан царай"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Шаналсан царай"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Айсан царай"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Ядарсан царай"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Нойрмог царай"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Ядарсан царай"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Ярвайсан царай"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Чанга уйлсан царай"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Амаа ангайсан царай"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Чимээгүй гэсэн царай"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Амаа ангайсан хүйтэн хөлстэй царай"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Айж хашгирсан царай"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Гайхширсан царай"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Уурссан царай"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Унтсан царай"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Дуниартсан царай"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Амгүй царай"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Эмнэлгийн масктай царай"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Инээсэн нүдтэй жуумалзсан муурын царай"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Баярын нулимстай муурын царай"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Амаа ангайж инээмсэглэсэн муурын царай"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Зүрхэн нүдтэй инээсэн муурын царай"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Муруй инээсэн муурын царай"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Аньсан нүдтэй үнсэлт илгээх муурын царай"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Дорвогор муурын царай"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Уйлсан муурын царай"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Ядарсан муурын царай"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Муу гэсэн зангаатай царай"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Сайн гэсэн зангаатай царай"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Мэхийн ёсолсон хүн"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Мууг харахгүй сармагчин"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Мууг сонсохгүй сармагчин"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Мууг ярихгүй сармагчин"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Нэг гараа өргөсөн жаргалтай хүн"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Баярлаж гараа өргөсөн хүн"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Барайсан хүн"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Дорвогор царайтай хүн"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Цээжээ тэвэрсэн хүн"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Пуужин"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Нисдэг тэрэг"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Уур зүтгүүр"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Төмөр замын вагон"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Хурдан галт тэрэг"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Суман хурдан галт тэрэг"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Галт тэрэг"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Метро"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Хөнгөн төмөр зам"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Буудал"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Трамвай"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Трамвай вагон"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Автобус"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Ирж буй автобус"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Тролейбус"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Автобусны зогсоол"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Минибус"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Түргэн тусламж"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Галын машин"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Цагдаагийн машин"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Ирж буй цагдаагийн машин"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Такси"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Ирж буй такси"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Автомашин"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Ирж автомашин"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Амралтын машин"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Хүргэлтийн ачааны машин"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Цуваа тэргэнцэр"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Трактор"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Moнорейл"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Уулын төмөр зам"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Дүүжин төмөр зам"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Уулын кабль зам"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Агаарын трамвай"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Усан онгоц"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Роу завь"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Хурдны завь"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Хэвтээ замын дохио"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Босоо замын дохио"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Барилгын тэмдэг"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Цагдаагийн машины эргэлдэх дохио"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Шуудан гурвалжин туг"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Хаалга"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Орохыг хориглосон тэмдэг"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Тамхи татах тэмдэг"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Тамхи хориглосон тэмдэг"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Хогийг саванд нь хаях тэмдэг"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Хог хаяхгүй тэмдэг"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Ундны усны тэмдэг"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Ундны бус усны тэмдэг"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Дугуй"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Унадаг дугуй болохгүй"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Унадаг дугуйч"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Уулын унадаг дугуйч"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Явган зорчигч"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Явган зорчигч болохгүй"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Хүүхдийн гарц"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Эрэгтэйчүүдийн тэмдэг"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Эмэгтэйчүүдийн тэмдэг"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Ариун цэврийн өрөө"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Хүүхдийн тэмдэг"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Бие засах газар"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"Усны сав"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Шүршүүр"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Ванн"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Ванн"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Паспорт хяналт"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Гааль"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Ачаа авах"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Орхисон ачаа"</string> +</resources> diff --git a/java/res/values-mn-rMN/strings-talkback-descriptions.xml b/java/res/values-mn-rMN/strings-talkback-descriptions.xml index 7eb316775..1c1e6e7f3 100644 --- a/java/res/values-mn-rMN/strings-talkback-descriptions.xml +++ b/java/res/values-mn-rMN/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Текст оруулаагүй"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> нь <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>-г <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> руу залруулна"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> авто-залруулалт хийдэг"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Товчийн код %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Шифт"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Сэлгэхийг идэвхжүүлсэн (товшиж идэвхгүйжүүлнэ үү)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Томоор бичихийг асаасан (товшиж идэвхгүйжүүлнэ үү)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Өөр тэмдэгтүүд"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Шифт"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Симбол"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Шифт"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Устгах"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Симбол"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Үсэгнүүд"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Өмнөх"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Сэлгэхийг идэвхжүүлсэн"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Томоор бичихийг идэвхжүүлсэн"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Сэлгэхийг идэвхжүүлээгүй"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Симбол төлөв"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Өөр тэмдэгтийн горим"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Үсэгнүүд төлөв"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Утасны төлөв"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Утасны символ төлөв"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Газар"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Симбол"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Эмотикон"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index 4c64b3354..67886e3bd 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Оруулах сонголтууд"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Судалгааны протоколын командууд"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Харилцагчийн нэр хайх"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Алдаа шалгагч нь таны харилцагчдын жагсаалтаас ашиглана"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Товч дарахад чичрэх"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Харилцагчдын нэрс санал болгох"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Санал болгох, залруулахда Харилцагчдын нэрсээс ашиглах"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Хувийн тохиргоотой зөвлөмжүүд"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Давхар зайтай цэг"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Ардаа зайтай цэг оруулахын тулд Зай авах дээр давхар товшино уу"</string> <string name="auto_cap" msgid="1719746674854628252">"Автоматаар томруулах"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Оруулах хэл"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Хадгалахын тулд дахин хүрнэ үү"</string> <string name="has_dictionary" msgid="6071847973466625007">"Толь бичиг байна"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Хэрэглэгчийн санал хүсэлтийг идэвхжүүлэх"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ашиглалтын статистик болон гацалтын репортуудыг автоматаар илгээснээр энэ оруулах арга засагчийг сайжруулахад туслаарай"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Гарын загвар"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Англи (ИБ)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Англи (АНУ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Идэвхжүүлэх"</string> <string name="not_now" msgid="6172462888202790482">"Одоо биш"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ижилхэн оруулах загвар байна: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Ашиглалтын судалгааны горим"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Товч удаан дарах хугацааны тохиргоо"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Товч дарах чичиргээний хугацаа"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Товчны дууны хэмжээ"</string> diff --git a/java/res/values-ms-rMY/strings-action-keys.xml b/java/res/values-ms-rMY/strings-action-keys.xml index f1a75d2ef..1910c0dc3 100644 --- a/java/res/values-ms-rMY/strings-action-keys.xml +++ b/java/res/values-ms-rMY/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Sblm"</string> <string name="label_done_key" msgid="7564866296502630852">"Siap"</string> <string name="label_send_key" msgid="482252074224462163">"Hntr"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Jeda"</string> <string name="label_wait_key" msgid="5891247853595466039">"Tggu"</string> </resources> diff --git a/java/res/values-ms-rMY/strings-emoji-descriptions.xml b/java/res/values-ms-rMY/strings-emoji-descriptions.xml new file mode 100644 index 000000000..1595a99a7 --- /dev/null +++ b/java/res/values-ms-rMY/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Tanda hak cipta"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Tanda berdaftar"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Tanda seruan berkembar"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Tanda tanya seruan"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Tanda cap dagangan"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Sumber maklumat"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Anak panah kiri kanan"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Anak panah atas bawah"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Anak panah barat laut"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Anak panah timur laut"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Anak panah tenggara"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Anak panah barat daya"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Anak panah hala kiri dengan cangkuk"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Anak panah hala kanan dengan cangkuk"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Jam"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Jam pasir"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Segi tiga kembar arah kanan hitam"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Segi tiga kembar arah kiri hitam"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Segi tiga kembar arah atas hitam"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Segi tiga kembar arah bawah hitam"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Jam penggera"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Jam pasir dengan pasir mengalir"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Huruf m besar latin dalam bulatan"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Empat segi kecil hitam"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Empat segi kecil putih"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Segi tiga arah kanan hitam"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Segi tiga arah kiri hitam"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Empat segi sederhana putih"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Empat segi sederhana hitam"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Empat segi sederhana kecil putih"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Empat segi sederhana kecil hitam"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Matahari dengan sinar hitam"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Awan"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Telefon hitam"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Peti undi dengan tanda semak"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Payung dengan titisan hujan"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Minuman panas"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Indeks putih menunjuk ke atas"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Muka tersenyum putih"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Aries"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Taurus"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Gemini"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Cancer"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Leo"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Virgo"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Libra"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Scorpius"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Sagittarius"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Capricorn"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Aquarius"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Pisces"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Daun sped hitam"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Daun kelawar hitam"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Daun hati hitam"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Daun berlian hitam"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Air panas"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Simbol kitar semula universal hitam"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Simbol kerusi roda"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Sauh"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Tanda amaran"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Tanda voltan tinggi"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Bulatan putih sederhana"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Bulatan hitam sederhana"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Bola sepak"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Besbol"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Orang salji tanpa salji"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Matahari di sebalik awan"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Ophiuchus"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Dilarang masuk"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Gereja"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Air pancut"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Bendera dalam lubang"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Kapal layar"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Khemah"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Pam minyak"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Gunting hitam"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Tanda semak tebal putih"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Kapal terbang"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Sampul surat"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Penumbuk"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Tangan diangkat"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Tanda menang"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Pensel"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Mata pen hitam"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Tanda semak tebal"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"X darab tebal"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Kilauan"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Asterisk berbucu lapan"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Bintang hitam berbucu lapan"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Emping salji"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Kilauan"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Tanda silang"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Tanda silang empat segi negatif"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Perhiasan tanda tanya hitam"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Perhiasan tanda tanya putih"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Perhiasan tanda seruan putih"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Simbol tanda seruan tebal"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Hati hitam tebal"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Tanda tambah tebal"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Tanda tolak tebal"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Tanda bahagi tebal"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Anak panah hala kanan hitam"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Gelung bergulung"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Gelung bergulung kembar"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Anak panah menghala ke kanan kemudian melengkung ke atas"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Anak panah menghala ke kanan kemudian melengkung ke bawah"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Anak panah hitam hala kiri"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Anak panah hitam hala atas"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Anak panah hitam hala bawah"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Empat segi besar hitam"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Empat segi besar putih"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Bintang sederhana putih"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Bulatan besar tebal"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Sengkang beralun"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Tanda perselangan silih"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Ideograf tahniah bulat"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Ideograf rahsia bulat"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Naga merah jubin mahjong"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Joker hitam daun terup"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Jenis darah A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Jenis darah B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Jenis darah O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Tempat letak kereta"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Jenis darah AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"CL empat segi"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"Tenang empat segi"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"Percuma empat segi"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"ID empat segi"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"Baharu empat segi"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"NG empat segi"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"OK empat segi"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"SOS empat segi"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"Naik dengan tanda seruan empat segi"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"Lawan empat segi"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Katakana di sini empat segi"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Perkhidmatan katakana empat segi"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Ideograf percuma empat segi"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Ideograf tempat duduk yang ditempah empat segi"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Ideograf larangan empat segi"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Ideograf jawatan kosong empat segi"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Ideograf penerimaan empat segi"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Ideograf penuh empat segi"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Ideograf dibayar empat segi"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Ideograf bulanan empat segi"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Ideograf permohonan empat segi"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Ideograf diskaun empat segi"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Ideograf perniagaan dibuka empat segi"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Ideograf kelebihan bulat"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Ideograf terima bulat"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Puting beliung"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Berkabut"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Payung tertutup"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Malam dengan bintang"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Matahari terbit di atas gunung"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Sunrise"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Skap bandar raya waktu senja"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Matahari terbenam di atas bangunan"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Pelangi"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Jambatan pada waktu malam"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Gelombang air"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Gunung Berapi"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Bima sakti"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Glob bumi eropah-afrika"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Glob bumi amerika"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Glob bumi asia-australia"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Glob dengan meridian"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Simbol bulan baharu"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Simbol bulan sabit mengambang"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Simbol bulan suku pertama"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Simbol bulan hampir purnama mengambang"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Simbol bulan purnama"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Simbol bulan hampir purnama surut"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Simbol bulan suku akhir"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Simbol bulan sabit surut"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Bulan sabit"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Bulan baharu dengan muka"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Bulan suku pertama dengan muka"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Bulan suku akhir dengan muka"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Bulan purnama dengan muka"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Matahari dengan muka"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Bintang bersinar"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Tahi bintang"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Buah berangan"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Anak benih"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Pokok malar hijau"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Pokok daun luruh"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Pokok palma"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Kaktus"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Tulip"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Bunga sakura"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Ros"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Bunga raya"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Bunga matahari"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Bunga"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Tongkol jagung"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Pokok padi"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Herba"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Empat daun semanggi"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Daun mapel"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Daun gugur"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Daun ditiup angin"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Cendawan"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Tomato"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Terung"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Anggur"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Tembikai"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Tembikai"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Oren Tangerin"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Lemon"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Kuning Pisang"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Nanas"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Epal merah"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Epal hijau"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Pear"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Pic"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Ceri"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Strawberi"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Hamburger"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Sepotong piza"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Daging pada tulang"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Kaki ayam"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Keropok beras"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Bebola beras"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Nasi"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Kari dan nasi"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Mangkuk mengukus"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Spageti"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Roti"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Kentang goreng"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Ubi keledek panggang"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Dango"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oden"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Sushi"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Udang goreng"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Kek ikan dengan corak pusaran"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Aiskrim lembut"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Ais kisar"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Aiskrim"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Donat"</string> + <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biskut"</string> + <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Bar coklat"</string> + <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Kandi"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lolipop"</string> + <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Kastad"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Bekas madu"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kek rapuh"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Kotak Bento"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Seperiuk makanan"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Masakan"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Garpu dan pisau"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Cawan tanpa pemegang"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Botol Sake dan cawan"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Gelas wain"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Gelas koktel"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Minuman tropika"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Kole bir"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Kole bir dihantukkan"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Botol bayi"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Reben"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Hadiah berbalut"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Kek hari lahir"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Jack-o-lantern"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Pokok Krismas"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Santa Klaus"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Bunga api"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Bunga api berlian"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Belon"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Peletus konfeti"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Bola Konfeti"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Pokok Tanabata"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Bendera berpalang"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Hiasan pain"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Anak patung Jepun"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Ular-ular kap"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Loceng angin"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Majlis melihat bulan"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Beg galas sekolah"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Topi graduasi"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Kuda karusel"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Roda Ferris"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Roller-coaster"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Joran dan ikan"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Mikrofon"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Kamera filem"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Pawagam"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Fon kepala"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Palet Artis"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Topi"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Khemas sarkas"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Tiket"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Papan ketap"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Seni persembahan"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Permainan video"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Tepat pada sasaran"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Mesin slot"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Biliard"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Dadu"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Boling"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Daun terup bunga"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Nota muzik"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Pelbagai nota muzik"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Saksofon"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Gitar"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Papan nada muzik"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Trompet"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Biola"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Skor muzik"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Baju berlari dengan selempang"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Raket dan bola tenis"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Ski dan but ski"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Bola keranjang dan gelung"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Bendera kotak-kotak"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Peluncur salji"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Pelari"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Peluncur"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Trofi"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Lumba kuda"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Bola sepak Amerika"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Bola sepak ragbi"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Perenang"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Bangunan rumah"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Rumah dengan taman"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Bangunan pejabat"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Pejabat pos Jepun"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Pejabat pos Eropah"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Hospital"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Bank"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Mesin teler automatik"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Hotel"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Hotel cinta"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Kedai serbaneka"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Sekolah"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Gedung beli-belah"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Kilang"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Tanglung izakaya"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Istana Jepun"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Istana Eropah"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Tikus"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Tikus"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Lembu jantan"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Kerbau"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Lembu"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Harimau Bintang"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Arnab"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Kucing"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Naga"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Buaya"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Ikan paus"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Siput"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Ular"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Kuda"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Biri-biri jantan"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Kambing"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Biri-biri"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Monyet"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Ayam jantan"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Ayam"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Anjing"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Babi"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Babi hutan"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Gajah"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Kurita"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Cengkerang berpusar"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Pepijat"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Semut"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Lebah madu"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Kumbang kura-kura"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Ikan"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Ikan tropika"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Ikan buntal"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Penyu"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Anak ayam menetas"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Anak ayam"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Anak ayam menghadap depan"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Burung"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Penguin"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Koala"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Poodle"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Unta dromedaris"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Unta Bactrian"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Dolfin"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Muka tikus"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Muka lembu"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Muka harimau"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Muka arnab"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Muka kucing"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Muka naga"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Ikan paut memancutkan air"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Muka kuda"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Muka monyet"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Muka anjing"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Muka babi"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Muka katak"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Muka hamster"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Muka serigala"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Muka beruang"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Muka panda"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Hidung babi"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Kesan tapak"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Mata"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Telinga"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Hidung"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Mulut"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Lidah"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Tangan putih menunjuk ke atas"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Tangan putih menunjuk ke bawah"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Tangan putih menunjuk ke kiri"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Tangan putih menunjuk ke kanan"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Isyarat enumbuk"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Isyarat tangan melambai"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Isyarat ok"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Isyarat bagus"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Isyarat tidak bagus"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Isyarat bertepuk tangan"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Isyarat tangan terbuka"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Mahkota"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Topi wanita"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Cermin mata"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Tali leher"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Kemeja T"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Jean"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Gaun"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Kimono"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Bikini"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Pakaian wanita"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Beg duit"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Beg tangan"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Dompet"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Kasut lelaki"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Kasut sukan"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Kasut tumit tinggi"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Sandal wanita"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"But wanita"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Tapak kaki"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Bayang patung"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Bayang patung"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Budak lelaki"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Budak perempuan"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Lelaki"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Wanita"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Keluarga"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Lelaki dan wanita berpegangan tangan"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Dua lelaki berpegangan tangan"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Dua wanita berpegangan tangan"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Pegawai polis"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Wanita dengan telinga arnab"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Pengantin dengan vel"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Orang berambut perang"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Lelaki dengan gua pi mao"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Lelaki berserban"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Lelaki lebih tua"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Wanita lebih tua"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Bayi"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Buruh binaan"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Puteri"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Gergasi Jepun"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Jembalang Jepun"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Bebayang"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Malaikat kecil"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Makhluk asing"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Raksasa asing"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Imp"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Tengkorak"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Orang meja maklumat"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Pengawal"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Penari"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Gincu"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Pengilat kuku"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Urutan muka"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Gunting rambut"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Jalur Tukang Gunting Rambut"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Picagari"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Pil"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Tanda ciuman"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Surat cinta"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Cincin"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Batu permata"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Ciuman"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Jambak bunga"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Pasangan dengan hati"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Perkahwinan"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Jantung berdegup"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Patah hati"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Dua hati"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Hati berkilauan"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Hati berkembang"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Hati dengan anak panah"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Hati biru"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Hati hijau"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Hati kuning"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Hati ungu"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Hati dengan riben"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Hati berputar"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Hiasan hati"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Bentuk berlian dengan titik di dalam"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Mentol elektrik"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Simbol kemarahan"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Bom"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Simbol tidur"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Simbol pelanggaran"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Simbol peluh memercik"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Titik kecil"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Simbol sengkang"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Timbunan najis"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Biseps dilenturtegang"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Simbol pening"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Belon pertuturan"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Belon pemikiran"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Bunga putih"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Simbol seratus mata"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Beg wang"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Pertukaran mata wang"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Isyarat dolar tebal"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Kad kredit"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Wang kertas dengan tanda yen"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Wang kertas dengan tanda dolar"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Wang kertas dengan tanda euro"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Wang kertas dengan tanda pound"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Wang dengan sayap"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Carta dengan aliran ke atas dan tanda yen"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Tempat duduk"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Komputer peribadi"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Beg bimbit"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Minicakera"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Cakera liut"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Cakera optik"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"Dvd"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Folder fail"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Buka folder fail"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Halaman bergulung"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Halaman menghadap ke atas"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Kalendar"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Kalendar koyak"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Kad indeks"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Carta dengan aliran ke atas"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Carta dengan aliran ke bawah"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Carta bar"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Papan Keratan"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Pin tekan"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Pin tekan bulat"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Klip kertas"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Pembaris lurus"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Pembaris segi tiga"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Tab penanda halaman"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Lejar"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Buku Nota"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Buku nota dengan kulit bercorak"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Buku tertutup"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Buku terbuka"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Buku hijau"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Buku biru"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Buku jingga"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Buku"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Lencana nama"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Skrol"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Memo"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Gagang telefon"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Alat kelui"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Mesin faks"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Antena satelit"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Pembesar suara pengumuman awam"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Megafon sorakan"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Dulang peti keluar"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Dulang peti masuk"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Pakej"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"Simbol e-mel"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Sampul surat masuk"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Sampul surat dengan anak panah ke bawah di atas"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Peti mel tertutup dengan bendera diturunkan"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Peti mel tertutup dengan bendera dinaikkan"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Peti mel terbuka dengan bendera dinaikkan"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Peti mel terbuka dengan bendera diturunkan"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Peti surat"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Hon pos"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Surat khabar"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Telefon mudah alih"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Telefon mudah alih dengan anak panah hala kanan di sebelah kiri"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Mod getaran"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Telefon mudah alih dimatikan"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Tiada telefon mudah alih"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Antena dengan bar"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Kamera"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Kamera video"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Televisyen"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Radio"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Kaset Video"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Anak panah hala kanan berpintal"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Anak panah bulatan terbuka hala kanan dan hala kiri mengikut arah jam"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Anak panah bulatan terbuka hala kanan dan hala kiri mengikut arah jam dengan tindihan satu dalam bulatan"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Anak panah bulatan terbuka menurun dan menaik mengikut arah jam"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Anak panah bulatan terbuka menurun dan menaik lawan arah jam"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Simbol cahaya rendah"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Simbol cahaya tinggi"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Pembesar suara dengan garisan pembatalan"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Pembesar suara"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Pembesar suara dengan satu gelombang bunyi"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Pembesar suara dengan tiga gelombang bunyi"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Bateri"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Palam elektrik"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Kanta pembesar menunjuk ke kiri"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Kata pembesar menunjuk ke kanan"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Mangga dengan pen dakwat"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Mangga tertutup dengan kunci"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Anak kunci"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Mangga"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Mangga terbuka"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Loceng"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Loceng dengan garisan pembatalan"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Penanda halaman"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Simbol pautan"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Butang radio"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"Kembali dengan anak panah hala kiri di atas"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"Akhir dengan anak panah hala kiri di atas"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"Hidup dengan tanda seruan dengan anak panah kiri kanan di atas"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"Tidak lama lagi dengan anak panah hala kiri di atas"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"Atas dengan anak panah hala atas di atas"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Simbol tiada sesiapa di bawah umur 18 tahun"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Butang kekunci sepuluh"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Simbol input huruf besar latin"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Simbol input huruf kecil latin"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Simbol input nombor"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Simbol input untuk simbol"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Simbol input huruf latin"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Api"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Lampu suluh"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Sepana"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Tukul"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Nat dan bolt"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Hocho"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Pistol"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Mikroskop"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Teleskop"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Bola kristal"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Bintang berbucu enam dengan titik tengah"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Simbol Jepun untuk perantis"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Lambang trisula"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Butang empat segi hitam"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Butang empat segi putih"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Bulatan merah besar"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Bulatan biru besar"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Berlian oren besar"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Berlian biru besar"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Berlian oren kecil"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Berlian biru kecil"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Segi tiga merah menunjuk ke atas"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Segi tiga merah menunjuk ke bawah"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Segi tiga merah kecil menunjuk ke atas"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Segi tiga merah kecil menunjuk ke bawah"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Muka jam pukul satu"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Muka jam pukul dua"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Muka jam pukul tiga"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Muka jam pukul empat"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Muka jam pukul lima"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Muka jam pukul enam"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Muka jam pukul tujuh"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Muka jam pukul lapan"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Muka jam pukul sembilan"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Muka jam pukul sepuluh"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Muka jam pukul sebelas"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Muka jam pukul dua belas"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Muka jam pukul satu setengah"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Muka jam pukul dua setengah"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Muka jam pukul tiga setengah"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Muka jam pukul empat setengah"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Muka jam pukul lima setengah"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Muka jam pukul enam setengah"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Muka jam pukul tujuh setengah"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Muka jam pukul lapan setengah"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Muka jam pukul sembilan setengah"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Muka jam pukul sepuluh setengah"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Muka jam pukul sebelas setengah"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Muka jam pukul dua belas setengah"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Gunung fuji"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Menara Tokyo"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Patung Liberty"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Bebayang jepun"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Moyai"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Muka tersengih"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Muka tersengih dengan mata tersenyum"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Muka dengan air mata kegembiraan"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Muka tersenyum dengan mulut terbuka"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Muka tersenyum dengan mulut terbuka dan mata tersenyum"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Muka tersenyum dengan mulut terbuka dan peluh dingin"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Muka tersenyum dengan mulut terbuka dan mata terpejam rapat"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Muka tersenyum dengan halo"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Muka tersenyum dengan tanduk"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Muka mengenyit mata"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Muka tersenyum dengan mata tersenyum"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Muka menikmati makanan lazat"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Muka lega"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Muka tersenyum dengan mata berbentuk hati"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Muka tersenyum dengan cermin mata hitam"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Muka mencebik"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Muka neutral"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Muka tanpa perasaan"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Muka tidak hairan"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Muka dengan peluh dingin"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Muka termenung"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Muka keliru"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Muka bingung"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Muka bercium"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Muka melayangkan ciuman"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Muka bercium dengan mata tersenyum"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Muka bercium dengan mata tertutup"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Muka dengan lidah terjelir"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Muka dengan lidah terjelir dan mengenyitkan mata"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Muka dengan lidah terjelir dan mata tertutup rapat"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Muka kecewa"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Muka bimbang"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Muka marah"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Muka muncung"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Muka menangis"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Muka bersungguh-sungguh"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Muka dengan rupa kemenangan"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Muka kecewa tetapi lega"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Muka muram dengan mulut terbuka"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Muka sedih"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Muka takut"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Muka letih"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Muka mengantuk"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Muka penat"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Muka berkerut"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Muka menangis kuat"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Muka dengan mulut terbuka"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Muka diam"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Muka dengan mulut terbuka dan peluh dingin"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Muka menjerit ketakutan"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Muka terperanjat"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Muka merah"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Muka tidur"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Muka pening"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Muka tanpa mulut"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Muka dengan topeng perubatan"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Muka kucing tersengih dengan mata tersenyum"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Muka kucing dengan air mata kegembiraan"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Muka kucing tersenyum dengan mulut terbuka"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Muka kucing tersenyum dengan mata berbentuk hati"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Muka kucing dengan senyuman sinis"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Muka kucing bercium dengan mata tertutup"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Muka kucing muncung"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Muka kucing menangis"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Muka kucing letih"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Muka tanpa gerak isyarat baik"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Muka dengan gerak isyarat ok"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Orang menunduk lama"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Monyet menutup mata"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Monyet menutup telinga"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Monyet menutup mulut"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Orang gembira mengangkat satu tangan"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Orang mengangkat kedua-dua tangan untuk meraikan"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Orang muram"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Orang dengan muka muncung"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Orang dengan tangan bersilang"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Roket"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Helikopter"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Kepala kereta api wap"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Kereta api"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Kereta api kelajuan tinggi"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Kereta api kelajuan tinggi dengan muncung peluru"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Kereta api"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metro"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Aliran ringan"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Stesen"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Trem"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Kereta trem"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Bas"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Bas dari arah depan"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Bas elektrik"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Perhentian bas"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Bas mini"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulans"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Kereta bomba"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Kereta polis"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Kereta polis dari arah depan"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Teksi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Teksi dari arah depan"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Kereta"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Kereta dari arah depan"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Kenderaan rekreasi"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Trak penghantaran"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Lori sambung sendi"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Traktor"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Monorel"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Kereta api gunung"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Kereta api tergantung"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Kereta kabel gunung"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Trem udara"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Kapal"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Perahu dayung"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Bot laju"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Lampu isyarat mendatar"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Lampu isyarat menegak"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Tanda pembinaan"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Lampu berputar kereta polis"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Bendera segi tiga pada tiang"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Pintu"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Isyarat dilarang masuk"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Simbol merokok"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Simbol dilarang merokok"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Simbol buang sampah di tempatnya"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Simbol jangan buang sampah"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Simbol air boleh diminum"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Simbol air tidak boleh diminum"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Basikal"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Tiada basikal"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Penunggang basikal"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Penunggang basikal gunung"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Pejalan kaki"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Tiada pejalan kaki"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Kanak-kanak melintas"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Simbol lelaki"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Simbol wanita"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Tandas"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Simbol bayi"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Tandas"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"Tandas"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Pancuran"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Bilik mandi"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Tab mandi"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Kawalan pasport"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Kastam"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Tuntutan bagasi"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Bagasi kiri"</string> +</resources> diff --git a/java/res/values-ms-rMY/strings-talkback-descriptions.xml b/java/res/values-ms-rMY/strings-talkback-descriptions.xml index 29c5fd86a..d6a45033e 100644 --- a/java/res/values-ms-rMY/strings-talkback-descriptions.xml +++ b/java/res/values-ms-rMY/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Tiada teks dimasukkan"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> membetulkan <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> melakukan auto pembetulan"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kod kunci %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Kunci anjak dihidupkan (ketik untuk melumpuhkan)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Kunci huruf besar dihidupkan (ketik untuk melumpuhkan)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Lagi simbol"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simbol"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Padam"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simbol"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Huruf"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Sebelumnya"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Kunci anjak didayakan"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Kunci huruf besar didayakan"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Kunci anjak dilumpuhkan"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Mod simbol"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Lagi mod simbol"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Mod huruf"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Mod telefon"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Mod simbol telefon"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Tempat"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simbol"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikon"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml index cea20217e..52165c55a 100644 --- a/java/res/values-ms-rMY/strings.xml +++ b/java/res/values-ms-rMY/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Penyemak ejaan menggunakan entri dari senarai kenalan anda"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Cadangan diperibadikan"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Titik ruang berganda"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Mengetik 2X pada bar ruang memasukkan titik diikuti dengan ruang"</string> <string name="auto_cap" msgid="1719746674854628252">"Autopenghurufbesaran"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Bahasa input"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Sentuh lagi untuk menyimpan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamus tersedia"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Dayakan maklum balas pengguna"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Bantu memperbaik editor kaedah input ini dengan menghantar statistik penggunaan dan laporan ranap secara automatik"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema papan kekunci"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Bahasa Inggeris (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Bahasa Inggeris (Australia)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Dayakan"</string> <string name="not_now" msgid="6172462888202790482">"Bukan sekarang"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mod kajian kebolehgunaan"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Kelewatan tekan lama kekunci"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tempoh getaran tekan kekunci"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Kelantangan bunyi tekan kekunci"</string> diff --git a/java/res/values-nb/strings-action-keys.xml b/java/res/values-nb/strings-action-keys.xml index d4acd36c9..af67b0073 100644 --- a/java/res/values-nb/strings-action-keys.xml +++ b/java/res/values-nb/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Forrige"</string> <string name="label_done_key" msgid="7564866296502630852">"Ferdig"</string> <string name="label_send_key" msgid="482252074224462163">"Send"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Vent"</string> </resources> diff --git a/java/res/values-nb/strings-talkback-descriptions.xml b/java/res/values-nb/strings-talkback-descriptions.xml index 96edf38c8..4870366d1 100644 --- a/java/res/values-nb/strings-talkback-descriptions.xml +++ b/java/res/values-nb/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Ingen tekst er skrevet inn"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> retter <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> til <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> utfører automatisk retting"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Tastaturkode %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift er på (trykk for å deaktivere)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock er på (trykk for å deaktivere)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Flere symboler"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboler"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Slett"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboler"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Bokstaver"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Forrige"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift er aktivert"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock er aktivert"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift er deaktivert"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbolmodus"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modus for flere symboler"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Bokstavmodus"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Ringemodus"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Ringemodus med symboler"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Steder"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboler"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Smilefjes"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index e4f16032c..339361cc4 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruker oppføringer fra kontaktlisten din"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå kontaktnavn"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Spesialtilpassede forslag"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Punktum ved doble mellomrom"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dobbeltrykk på mellomromstasten for punktum etterfulgt av mellomrom"</string> <string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Inndataspråk"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Trykk på nytt for å lagre"</string> <string name="has_dictionary" msgid="6071847973466625007">"Ordbok tilgjengelig"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktiver brukertilbakemelding"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ved å sende bruksstatistikk og programstopprapporter til Google automatisk, hjelper du oss med å gjøre redigeringsfunksjonen for denne inndatametoden enda bedre."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tastaturtema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engelsk (Storbritannia)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engelsk (USA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktiver"</string> <string name="not_now" msgid="6172462888202790482">"Ikke nå"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Inndatastilen finnes allerede: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Bruksstudiemodus"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Forsinkelse lange tastetrykk"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasjonstid ved tastetrykk"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Lydstyrke ved tastetrykk"</string> diff --git a/java/res/values-ne-rNP/strings-action-keys.xml b/java/res/values-ne-rNP/strings-action-keys.xml index 34b0a14a7..5c0a46225 100644 --- a/java/res/values-ne-rNP/strings-action-keys.xml +++ b/java/res/values-ne-rNP/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"पहिलो"</string> <string name="label_done_key" msgid="7564866296502630852">"भयो"</string> <string name="label_send_key" msgid="482252074224462163">"पठाउनुहोस्"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"रोक्नुहोस्"</string> <string name="label_wait_key" msgid="5891247853595466039">"पर्खनुहोस्"</string> </resources> diff --git a/java/res/values-ne-rNP/strings-emoji-descriptions.xml b/java/res/values-ne-rNP/strings-emoji-descriptions.xml new file mode 100644 index 000000000..a3419b86d --- /dev/null +++ b/java/res/values-ne-rNP/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"प्रतिलिपि अधिकार चिन्ह"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"दर्ता चिन्ह"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"दोहोरो उद्गार मार्क"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"विस्मयादिबोधक प्रश्न चिन्ह"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"व्यापार प्रतिक चिन्ह"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"सूचना स्रोत"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"दायाँ तीर बाँयामा"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"तल तीर माथिमा"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"उत्तर पश्चिम तीर"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"उत्तर पूर्वी तीर"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"दक्षिण पूर्व तीर"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"दक्षिण पश्चिम तीर"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"बायाँ हुक साथ तीर"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"दाँया हुक साथ तीर"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"हेर्नु"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"बलौटे घडी"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"कालो दाँया देखाउने डबल त्रिकोण"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"कालो बायाँ-इशारा डबल त्रिकोण"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"कालो माथि देखाउने डबल त्रिकोण"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"कालो तल-इशारा डबल त्रिकोण"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"सचेतक घडी"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"बग्ने बालुवा साथ बालुवा घडी"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"बृताकार भित्र ठुलो ल्याटिन अक्षर m"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"कालो सानो वर्ग"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"श्वेत सानो वर्ग"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"कालो दाँया देखाउने त्रिकोण"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"कालो बायाँ-इशारा त्रिकोण"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"श्वेत मध्यम वर्ग"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"कालो मध्यम वर्ग"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"श्वेत मध्यम सानो वर्ग"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"कालो मध्यम सानो वर्ग"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"किरण साथ कालो सूर्य"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"बादल"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"कालो टेलिफोन"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"जाँच साथ मतदान बक्स"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"वर्षा थोपा साथ छाता"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"तातो पेय"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"श्वेत अप सूचकांक इशारा"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"श्वेत हँसिलो अनुहार"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"मेष"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"वृष"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"मिथुन"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"कर्कट"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"सिंह"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"कन्या"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"तुला"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"वृष"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"धनु"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"मकर"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"कुंभ"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"मीन"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"कालो कुदाल सुट"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"कालो क्लब सुट"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"कालो हृदय सुट"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"कालो हीरा सुट"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"तातो स्प्रिंग्स"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"कालो विश्वव्यापी रिसाइकिलिंग चिन्ह"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"ह्वीलचेयर चिन्ह"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"अंकुश"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"चेतावनी चिन्ह"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"उच्च भोल्टेज चिन्ह"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"मध्यम सफेद वृत्त"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"मध्यम कालो वृत्त"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"फुटबल"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"बेसबल"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"बिना हिउँ हिममानब"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"बादलको पछाडी सूर्य"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"तारामंडल Ophiuchus"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"प्रवेश निषेध"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"चर्च"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"फोहरा"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"प्वालमा झण्डा"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"जहाजयात्रा"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"पाल"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"इन्धन पम्प"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"कालो कैंची"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"श्वेत भारी चेक मार्क"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"हवाइजहाज"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"खाम"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"उठेको मुट्ठी"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"उठाएको हात"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"विजयी हात"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"शिसाकलम"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"कालो निब"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"भारी चेक मार्क"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"भारी गुणन x"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"झिल्काहरु"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"आठधर्के तारांकन"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"आठ चुच्चे कालो तारा"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"हिमपात"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"चमक"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"क्रस चिन्ह"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"उल्टो चारपाते काटिएको चिन्ह"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"कालो प्रश्न चिन्ह आभूषण"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"श्वेत प्रश्न चिन्ह आभूषण"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"श्वेत उद्गार चिन्ह आभूषण"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"भारी उद्गार चिन्ह प्रतिक"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"दह्रो कालो हृदय"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"भारी प्लस चिन्ह"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"भारी ऋण चिन्ह"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"भारी विभाजन चिन्ह"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"कालो दाँयातीर तीर"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"घुम्रिएको फेरो"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"दोहोरो घुम्रिएको फेरो"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"दाँयातीर इशारा तीर त्यसपछि माथिको घुमाँइ"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"दाँयातीर इशारा तीर त्यसपछि तलको घुमाँइ"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"कालोतीर बायाँतर्फ"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"कालो तीर माथि तर्फ"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"कालो तीर तल तर्फ"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"कालो ठूलो वर्ग"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"सेतो ठूलो वर्ग"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"सेतो मध्यम तारा"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"भारी ठूलो वृत्त"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"तरंग ड्यास"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"खण्ड बैकल्पिक मार्क"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"बृताकार भाबाङ्कन बधाई"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"बृताकार भाबाङ्कन रहस्य"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"माजोङ टाइल रातो ड्रागन"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"खेल्ने तास कालो जोकर"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"रक्त प्रकार A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"रक्त प्रकार B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"रक्त प्रकार O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"पार्क्ङिग स्थल"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"रक्त प्रकार AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"चारपाटे CL"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"चारपाते सुन्दर"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"चारपाते मुक्त"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"चारपाते ID"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"चारपाते नयाँ"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"चारपाते N G"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"चारपाते ठीक"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"चारपाते SOS"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"उद्गार चिन्ह साथ चारपाते"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"चारपाते बिरुद्ध"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"यहाँ चारपाते काताकाना"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"चारपाते काताकाना सेवा"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"चारपाते भाबाङ्कन -निःशुल्क"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"चारपाते भाबाङ्कन सुरक्षित-सीट"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"चारपाते भाबाङ्कन निषेध"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"चारपाते भाबाङ्कन रिक्तता"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"चारपाते भाबाङ्कन स्वीकृति"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"चारपाते भाबाङ्कन पूर्ण अधिभोग"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"चारपाते भाबाङ्कन भुक्तानी"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"चारपाते भाबाङ्कन मासिक"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"चारपाते भाबाङ्कन अनुप्रयोग"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"चारपाते भाबाङ्कन छूट"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"व्यापारमा चारपाते भाबाङ्कन"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"बृताकार भाबाङ्कन लाभ"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"बृताकार भाबाङ्कन स्वीकार"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"चक्रवात"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"धुवाँदार"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"बन्द छाता"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"ताराहरूका साथ रात"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"पहाडमा सूर्योदय"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"सूर्योदय"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"धुलो रहित शहर"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"भवनहरुमा सूर्यास्त"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"ईन्द्रेणी"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"रातमा पुल"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"जल लहर"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"ज्वालामुखी"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"दुधेली बाटो"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"पृथ्बी विश्व युरोप-अफ्रिका"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"पृथ्बी विश्व अमेरीकास"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"पृथ्बी विश्व एशिया-अस्ट्रेलिया"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"मेरीडियन साथ विश्व"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"नयाँ चन्द्रमाको चिन्ह"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"पहेँलो अर्धचन्द्राकार चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"पहिलो चौथाई चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"पहेँलो बाँदर चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"पूर्ण चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"थकित बाँदर चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"अन्तिम चौथाई चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"चेतावनी अर्धचन्द्राकार चन्द्र प्रतीक"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"अर्धचन्द्राकार चन्द्र"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"नयाँ चन्द्रामुहार"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"पहिलो चौथाइ चन्द्रामुहार"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"अन्तिम चौथाइ चन्द्रामुहार"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"पूर्ण चन्द्रामुहार"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"सूर्यामुहार"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"चमकिलो तारा"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"सुटिङ तारा"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"ओखर"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"सिडिङ"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"सदाबहार रूख"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"पतझड रूख"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"ताड रूख"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"सिउँडी"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"तुलिप"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"चेरी फूल"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"गुलाब"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"जाभाकुसुम"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"सूर्यमुखी"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"फूल"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"काने मकै"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"काने धान"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"जडिबुटी"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"चार पाते क्लोभर"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"मेपल पातको"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"झरेको पात"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"पात हावामा उड्दै"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"च्याँउ"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"गोलभेंडा"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"डल्लो भन्टा"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"अंगूर"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"तरबूजा"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"खरबूजा"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"सुन्तले रङ"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"लेमन"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"केरा"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"भुईंकतहर"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"रातो स्याउ"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"हरियो स्याउ"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"नाशपाती"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"बयर"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"चेरी"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"स्ट्रबेरी"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"ह्यामबर्गर"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"पिज्जा टुक्रा"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"हड्डीमा मासु"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"कुखुराको खुट्टा"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"चामलको पातलो बिस्कुट"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"भातको बल"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"पकाएको भात"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"करी र भात"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"उमालेको कचौरा"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"स्पैगेट्टी"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"रोटी"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"फ्रान्सेली फ्राइज"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"रोस्ट गरेको मिठो आलु"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"जापानी मिठाई"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"ओदेन (जापानी खाना)"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"भात र काँचो माछा"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"तारेको सानो माछा"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"घुमाउरो डिजाइनमा माछा केक"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"नरम आइसक्रिम"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"चुर्ण बरफ"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"आइसक्रीम"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"डोनट"</string> + <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_1F36E" msgid="4630541402785165902">"कस्तार्ड"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"महदानी"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"सर्टकेक"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"टिफिन बट्टा"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"खानाको भाँडो"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"पकाउँदै"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"काँटा र चक्कु"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"ह्यान्डल बिनाको चियाकप"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"शाके (जापानी रक्सी) बोतल र प्याला"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"रक्सी गिलास"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"ककटेल गिलास"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"ट्रपिकल पेयपदार्थ"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"बियर मग"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"बियर मग छुआएको"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"बच्चाको बोतल"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"रिबन"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"बेरिएको उपहार"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"जन्मदिनको केक"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"ज्याक-o-लालटिन"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"क्रिसमस रूख"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"सान्ताक्लस"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"आतसबाजी"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"आतसबाजी झिल्का"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"बेलुन"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"पार्टी भंडुवा"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"रङगिन बल"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"तानाबाता रूख"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"क्रस झण्डा"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"पाइन सजावट"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"जापानी गुडिया"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"कार्प तोरण"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"हावा झंकार"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"चन्द्र अबलोकन समारोह"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"स्कूल झोला"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"दिक्षान्त समारोह टोपी"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"खेलाची घोडा"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"रोटेपिङ्"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"रोलर कोस्टर"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"माछामार्ने पोल र माछा"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"माइक्रोफोन"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"चलचित्र क्यामेरा"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"चलचित्र"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"हेडफोन"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"कलाकार रङदानी"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"शीर्ष टोपी"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"सर्कस पाल"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"टिकट"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"क्लापर बोर्ड"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"कला प्रदर्शन"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"भिडियो खेल"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"प्रत्यक्ष निशाना"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"स्लट मिसिन"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"बिलियर्ड्स"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"पोट"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"बलिङ"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"फूलछापे तास"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"साङ्गीतिक अक्षर"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"बहु साङ्गीतिक अक्षर"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"सेक्सोफोन"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"गितार"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"साङ्गीतिक कुञ्जीपाटी"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"बिगुल"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"भ्वायलिन"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"साङ्गीतिक स्कोर"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"तेर्सो धर्के जर्सी"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"टेनिस रैकेट र बल"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"स्की र स्की बुट"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"बास्केटबल र रिंग"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"चेकढाँचा झण्डा"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"स्नोबोर्दर"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"धावक"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"सर्फ़र"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"शिल"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"घोडा दौड"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"अमेरिकी फुटबल"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"रग्बी फुटबल"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"पौडीबाज"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"घर निर्माण"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"बगैचा सहित घर"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"कार्यालय भवन"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"जापानी हुलाक"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"युरोपेली हुलाक"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"अस्पताल"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"बैंक"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"स्वचालित पारंगत मिसिन"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"होटल"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"मायालु होटल"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"सुविधाजनक स्टोर"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"स्कूल"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"डिपार्टमेन्ट स्टोर"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"कारखाना"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"इजाकाया लालटेन"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"जापानी किल्ला"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"युरोपेली किल्ला"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"मुसा"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"घरेलु मुसा"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"साँढे"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"जल भैंसी"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"गाई"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"चितुवा"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"खरायो"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"बिरालो"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"ड्रागन"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"गोही"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"ह्वेल"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"घोंघा"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"साँप"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"घोडा"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"मेष"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"बाख्रा"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"भेडा"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"बाँदर"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"रोस्टर कुखुरा"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"कुखुरा"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"कुकुर"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"सुँगुर"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"बँदेल"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"हात्ती"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"अक्टोपस"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"शंख"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"कनसुत्लो"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"कमिला"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"माहुरी"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"खपटे कीरा"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"माछा"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"ट्रपिकल माछा"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"फुगु"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"कछुवा"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"चल्ला कोरल्नु"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"सानोचल्ला"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"अधोमुख सानोचल्ला"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"चरा"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"पेंगुइन"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"कोआल"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"पूडल"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"अरेबि उँट"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"दुईजुरे उँट"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"डल्फिन"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"मुसा अनुहार"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"गाई अनुहार"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"बाघ अनुहार"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"खरायो अनुहार"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"बिरालो अनुहार"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"ड्रागन अनुहार"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"ह्वेल पानीफाल्दै"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"घोडा अनुहार"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"बाँदर अनुहार"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"कुकुर अनुहार"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"सुँगुर अनुहार"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"भ्यागुतो अनुहार"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"लोखर्के अनुहार"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"ब्वाँसो अनुहार"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"भालु अनुहार"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"पाण्डा अनुहार"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"सुँगुर नाक"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"पंजा छाप"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"आँखाहरु"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"कान"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"नाक"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"मुख"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"जिब्रो"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"ठडाएको औंला पछाडिबाट सूचकांक इशारा"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"घोप्ताएको औंला पछाडिबाट सूचकांक इशारा"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"औंला इशारा पछाडिबाट सूचकांक बायाँ"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"औंला इशारा पछाडिबाट सूचकांक दायाँ"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"मुठी चिन्ह"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"हल्लाएको हात चिन्ह"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"ठीक हात चिन्ह"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"औंठा माथी चिन्ह"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"औंठा तल चिन्ह"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"ताली चिन्ह"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"खुला हात चिन्ह"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"श्रीपेच"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"महिला टोपी"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"चश्मा"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"टाई"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"टी-शर्ट"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"जीन्स"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"पोशाक"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"किमोनो"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"बिकिनी"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"महिला लुगा"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"बटुआ"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"हैंडबैग"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"थैली"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"केटा जुत्ता"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"खेलाडी जुत्ता"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"अग्लो हिलको जुत्ता"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"महिला जुत्ता"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"महिला बूट"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"पैताला छाप"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"छाँया चित्र"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"छाँया चित्रहरू"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"केटा"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"केटी"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"मान्छे"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"महिला"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"परिवार"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"स्त्री र पुरुष हातसमात्दै"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"दुई पुरुष हातसमात्दै"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"दुई महिला हातसमात्दै"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"प्रहरी अधिकारी"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"खरायो काने महिला"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"घुम्टोमा दुलही"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"खैरो कपाल व्यक्ति"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"गुआपी माओ पहिरनमा मानिस"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"फेटाधारी मानिस"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"बृढमान्छे"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"बृढा महिला"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"बच्चा"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"निर्माण कामदार"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"राजकुमारी"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"जापानी राक्षस"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"जापानी लुटेरा"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"भूत"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"सानोपरी"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"अन्य ग्रहबासी"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"अन्य ग्रहबासी राक्षस"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"सानो शैतान"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"खोपडी"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"सोधपुछको व्यक्ति"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"पहरेदार"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"नर्तक"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"लिपस्टिक"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"नेल पलिश"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"अनुहार मालिश"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"सैलुन"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"नाई पोल"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"सुई"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"गोली"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"चुम्बन चिन्ह"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"प्रेमपत्र"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"औंठी"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"रत्न पत्थर"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"चुम्बन"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"फूलगुच्छा"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"युगलजोडी"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"विवाह"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"हृदय धड्कन"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"टुटेको हृदय"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"दुई हृदय"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"चम्केको हृदय"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"बढ्दो हृदय"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"तीर सहित हृदय"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"नीलो हृदय"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"हरियो हृदय"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"पहेंलो हृदय"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"बैजनी हृदय"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"रिबन साथ हृदय"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"परिक्रामी हृदय"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"हृदय सजावट"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"भित्र बिन्दु साथ हीरा आकार"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"इलेक्ट्रिक चिम"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"रिस प्रतीक"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"बम"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"सुताहा प्रतीक"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"टकराव प्रतीक"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"उछिट्टिएको पसिना प्रतीक"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"सानो थोपा"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"ड्यास प्रतीक"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"थुप्रो गु"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"पाखुरा प्रदर्शन"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"तारा प्रतीक"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"भाषण बेलुन"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"बैचारिक बेलुन"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"सेतो फूल"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"सय अंक प्रतीक"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"पैसाको थैली"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"मुद्रा विनिमय"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"मोटो डलर चिन्ह"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"क्रेडिट कार्ड"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"येन चिन्ह साथ नोट"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"डलर चिन्ह सहित बैकनोट"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"यूरो चिन्ह साथ नोट"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"पाउन्ड चिन्ह साथ नोट"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"पखेटाली मुद्रा"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"माथिको प्रवृत्ति र येन चिन्ह साथ चित्रपाती"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"सिट"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"निजी कम्प्यूटर"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"ब्रिफकेस"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"सानो डिस्क"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"फ्लपी डिस्क"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"सी डि चक्का"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"डिभिडी"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"फाइल फोल्डर"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"फाइल फोल्डर खोल्नुहोस्"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"दोब्र्याइएको पृष्ठ"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"माथि पत्याएको पृष्ठ"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"पात्रो"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"च्यातेर पठाउने पात्रो"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"कार्ड सूचकांक"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"माथिको प्रवृत्ति साथ चित्रपाती"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"तल प्रवृत्ति साथ चित्रपाती"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"बार चार्ट"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"क्लिपबोर्ड"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"पुस पिन"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"गोल पुस पिन"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"क्लिप"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"फुट"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"त्रिकोणात्मक फुट"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"किताबी ट्याबहरू"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"लेजर"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"नोटबुक"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"सजावटी खोल सहित नोटबुक"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"बन्द पुस्तक"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"खुला पुस्तक"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"हरियो पुस्तक"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"नीलो पुस्तक"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"सुन्तला पुस्तक"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"पुस्तकहरू"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"बिल्ला नाम"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"स्क्रोल"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"मेमो"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"टेलिफोन रिसीभर"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"पेजर"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"फ्याक्स मेसिन"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"स्याटलाइट एंटीना"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"आकाशबाणी"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"ढ्वांग फुक्नु"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"आउटबक्स ट्रे"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"इनबक्समा ट्रे"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"प्याकेज"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"इमेल प्रतीक"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"आगमन खाम"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"माथि तीर तल देखाइएको साथ खाम"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"माथि झण्डा साथ बन्द मेलबक्स"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"उठाएको झन्डा साथ बन्द मेलबक्स"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"उठाएको झन्डा साथ खुला मेलबक्स"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"तल झण्डा साथ खुला मेलबक्स"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"पत्र पेटिका"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"पोस्टल सीङ"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"अखबार"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"मोबाइल फोन"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"दाँया तीर साथ मोबाइल फोन बायाँ"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"कम्पन ढाँचाः"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"मोबाइल फोन बन्द"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"बिना मोबाइल फोनहरु"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"बारहरू साथ एंटीना"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"क्यामेरा"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"भिडियो क्यामेरा"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"टेलिभिजन"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"रेडियो"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"भिडियोक्यासेट"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"दाँया बटारिएको तीर"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"दाँया र बायाँ खुला वृत्त तीर घडीकोदिशामा"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"वृत्त दोबारेको साथ दाँया र बायाँ खुला वृत्त तीर घडीकोदिशामा"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"घडीको दिशामा तल र माथिको तर्फ खुला वृत्त तीर"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"उल्टा घडीको दिशामा तल र माथिको तर्फ खुला वृत्त तीर"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"कम चमक प्रतीक"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"उच्च चमक प्रतीक"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"ध्वोंनी निषेध स्ट्रोक"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"स्पिकर"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"एक तरङ्ग साथ स्पिकर"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"तीन ध्वनि लहरहरु साथ स्पिकर"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"ब्याट्री"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"बिद्धुतीय प्लग"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"बाँया-इशारा आबर्धक लेन्स"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"दायाँ इशारा आबर्धक लेन्स"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"मसी कलमले बन्द"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"साँचो साथ बन्द ताला"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"साँचो"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"लक गर्नुहोस्"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"खुला ताला"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"घन्टी"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"रद्द स्ट्रोक साथ घन्टी"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"बुकमार्क"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"संपर्क प्रतीक"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"रेडियो बटन"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"बायाँ माथि तीर साथ फर्काएको"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"बायाँ माथि तीर साथ अन्त्य"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"बायाँ साथ उद्गार चिन्हले दाँया माथि तीर"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"दाँया माथि तीर साथ चाँडै"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"माथिको माथि तीर साथ माथि"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"अठार मुनिका प्रतीक निषेधित"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"किक्याप दस"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"ठुलो ल्याटिन अक्षरका आगत प्रतीक"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"सानो ल्याटिन अक्षरका आगत प्रतीक"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"संख्याका लागि आगत प्रतीक"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"प्रतीकको लागि प्रतीक प्रविष्ट गर्नुहोस्"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"ल्याटिन अक्षरका आगत प्रतीक"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"आगो"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"बिजुली बत्ती"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"रेंच"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"हथौडा"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"नट बोल्ट"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"जापानी चक्कु होचो"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"पिस्तौल"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"माइक्रोस्कोप"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"टेलिस्कोप"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"क्रिस्टल बल"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"बीचमा थोप्ला निहित छ चुच्चे तारा"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"शुरुवातको लागि जापानी प्रतीक"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"त्रिशूल प्रतीक"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"कालो वर्ग बटन"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"सेतो वर्ग बटन"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"ठूलो लाल वृत्त"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"ठूलो नीलो वृत्त"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"ठूलो सुन्तला हीरा"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"ठूलो नीलो हीरा"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"लघु सुन्तला हीरा"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"लघु नीलो हीरा"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"माथी-इशारा रातो त्रिकोण"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"तल-इशारा रातो त्रिकोण"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"माथी इशारा सानो रातो त्रिकोण"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"तल-इशारा सानो रातो त्रिकोण"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"घडी अनुहार एक बजे"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"घडी अनुहार दुई बजे"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"घडी अनुहार तीन बजे"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"घडी अनुहार चार बजे"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"घडी अनुहार पाँच बजे"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"घडी अनुहार छ बजे"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"घडी अनुहार सात बजे"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"घडी अनुहार आठ बजे"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"घडी अनुहार नौ बजे"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"घडी अनुहार दश बजे"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"घडी अनुहार एघार बजे"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"घडी अनुहार बाह्र बजे"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"घडी अनुहार एक-तिस"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"घडी अनुहार दुई-तिस"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"घडी अनुहार तीन तीस"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"घडी अनुहार चार-तिस"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"घडी अनुहार पाँच-तिस"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"घडी अनुहार छ-तिस"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"घडी अनुहार सात-तिस"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"घडी अनुहार आठ-तिस"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"घडी अनुहार नौ-तिस"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"घडी अनुहार दश-तिस"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"घडी अनुहार एघार-तिस"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"घडी अनुहार बाह्र-तिस"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"माउन्ट फुजी"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"टोकियो टावर"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"स्वतन्त्रताको प्रतिमा"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"जापानको छान्य मुर्ति"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"मोयै"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"अनुहार ङिच्च हँसाइ"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"हँसिल आँखाको साथ ङिच्च हँसाइ"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"आनन्द आँसुको साथ अनुहार"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"खुला मुखको साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"खुला मुखको र हँसिलो आँखा साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"खुला मुख र चिसो पसिना साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"खुला मुख र बन्द आँखाको साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"हेलो साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"सिंगहरु साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"आँखा झिम्काएको अनुहार"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"हँसिलो आँखाले साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"स्वादिष्ट भोजन savoring अनुहार"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"ढुक्क अनुहार"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"हृदय आकारको आँखाले हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"घाम साथ हँसिलो अनुहार"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"स्वाँग पारेको अनुहार"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"सामान्य अनुहार"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"भाबबिहिन अनुहार"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"निराश अनुहार"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"चिसो पसिना साथ अनुहार"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"चिंताग्रस्त अनुहार"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"गोलमाल अनुहार"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"आघातको अवस्थामा अनुहार"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"चुम्बन अनुहार"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"चुम्बन फालिएको अनुहार"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"हँसिलो आँखा साथ चुम्बन अनुहार"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"बन्द आँखा साथ चुम्बन अनुहार"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"बाहिर जिब्रो साथ अनुहार"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"बाहिर जिब्रो र झिम्काएको आँखा साथ अनुहार"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"बाहिर जिब्रो र बेस्सरी बन्द आँखा साथ अनुहार"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"निराश अनुहार"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"चिन्तित अनुहार"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"आक्रोशित अनुहार"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"दुखी अनुहार"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"कराएको अनुहार"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"संरक्षित अनुहार"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"विजय नजर अनुहार"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"निराश तर राहत अनुहार"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"खुला मुखको साथ निन्याउरो अनुहार"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"विचलित अनुहार"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"डराएको अनुहार"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"थकित अनुहार"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"निदाएको अनुहार"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"थकित अनुहार"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"कित्किताएको अनुहार"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"चर्को सोरले कराएको अनुहार"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"खुला मुख अनुहार"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"चुप अनुहार"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"खुला मुख र चिसो पसिना अनुहार"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"डरमा चिल्लाएको अनुहार"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"छक्क अनुहार"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"निर्लज्ज अनुहार"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"निन्द्रे अनुहार"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"चक्करलागेको अनुहार"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"मुख नभएको अनुहार"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"चिकित्सक माक्स लगाएको अनुहार"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"हँसिलो आँखा साथ ङीच्च बिरालो अनुहार"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"आनन्द आँसु झार्दै बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"खुला मुखले हँसिलो बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"हृदय आकारको आँखाले हँसिलो बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"छट्टु मुस्कान साथ बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"बन्द आँखा साथ चुम्बन बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"दुखी बिरालो अनुहार"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"कराएको बिरालो अनुहार"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"वाक्क भइसकेको बिरालो अनुहार"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"कुनै राम्रो सङ्केत बिना साथ अनुहार"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"ठीक इशारा साथ अनुहार"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"गहिरो निहुरेको व्यक्ति"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"हेर्नुहोस्-कुनै-दुष्ट बाँदर"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"सुन-कुनै-दुष्ट बाँदर"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"बोल्नु-कुनै-दुष्ट बाँदर"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"खुसी व्यक्ति एक हात उठाउँदै"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"उत्सवमा दुवै हात उठाँउदैको व्यक्ति"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"निराश व्यक्ति"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"दुखी अनुहार व्यक्ति"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"नतमस्तक व्यक्ति"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"रकेट"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"हेलिकोप्टर"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"भापद्वारा चल्ने यातायात"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"रेलवे कार"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"उच्च गति रेल"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"गोली नाक साथ उच्च गतिमा रेल"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"रेल"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"मेट्रो"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"प्रकाश रेल"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"स्टेशन"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"ट्राम"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"ट्राम कार"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"बस"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"आउदैको बस"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"ट्रलीबस"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"बस बिसौना"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"सानो बस"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"एम्बुलेन्स"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"दमकल"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"प्रहरी कार"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"आऊदैको प्रहरी कार"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"ट्याक्सी"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"आऊदैको ट्याक्सी"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"अटोमोबाइल"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"आऊदैको गाडी"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"मनोरंजन वाहन"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"वितरण ट्रक"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"जोडिएको ठूलोगाडी"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"ट्रयाक्टर"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"एकतर्फी रेल"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"पहाडी रेलवे"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"सस्पेंशन रेलवे"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"पहाडी केवलवे"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"हवाई ट्रामगाडी"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"पानीजहाज"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"डुंगा"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"द्रुत डुंगा"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"तेर्सो यातायात बत्ती"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"ठाडो यातायात प्रकाश"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"निर्माण चिन्ह"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"प्रहरी कारको परिक्रामी प्रकाश"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"पोस्टमा त्रिकोणात्मक झण्डा"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"ढोका"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"प्रवेश निषेध चिन्ह"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"धूम्रपान चिन्ह"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"धुम्रपान निषेधित चिन्ह"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"फोहर राख्ने ठाउँ चिन्ह"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"फोहर फाल्न निषेधित चिन्ह"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"पेय जल चिन्ह"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"गैह्र पिउने पानी चिन्ह"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"साइकल"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"साइकल निषेध"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"साइकल यात्री"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"पहाडी साइकल् यात्री"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"पैदल यात्री"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"पैदल यात्री निषेध"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"बच्चाहरु बाटो काट्दै"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"पुरुष चिन्ह"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"महिला चिन्ह"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"शौचालय"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"बच्चा चिन्ह"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"शौचालय"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"कम्बोथ"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"फोहरा"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"स्नान"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"बाथटब"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"राहदानी नियन्त्रण"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"भन्सार"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"सामान दाबी"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"छुटेको सामान"</string> +</resources> diff --git a/java/res/values-ne-rNP/strings-talkback-descriptions.xml b/java/res/values-ne-rNP/strings-talkback-descriptions.xml index cffed33db..044a04154 100644 --- a/java/res/values-ne-rNP/strings-talkback-descriptions.xml +++ b/java/res/values-ne-rNP/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"कुनै पाठ प्रविष्टि गरिएको छैन"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ले <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> लाई <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> मा सच्याउँछ"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ले स्वतः सच्याउने गर्छ"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"कुञ्जी कोड %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"सिफ्ट"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"सिफ्ट सक्रिय छ (असक्षम पार्न ट्याप गर्नुहोस्)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"क्याप्स लक सकृय छ (असक्षम पार्न ट्याप गर्नुहोस्)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"थप प्रतीकहरु"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"सिफ्ट"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"प्रतीकहरू"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"सिफ्ट"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"मेटाउनुहोस्"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"प्रतिकहरू"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"अक्षरहरू"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"अघिल्लो"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"सिफ्ट सक्षम पारिएको छ"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"क्याप्स लक सक्षम पारिएको छ"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"सिफ्ट असक्षम पारिएको छ"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"प्रतिक ढाँचा"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"थप प्रतीक मोड"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"अक्षर ढाँचा"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"फोन ढाँचा"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"फोन प्रतिक मोड"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"स्थानहरू"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"प्रतिकहरू"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"ईमोटिकन्स"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml index f467443b3..22e5acb91 100644 --- a/java/res/values-ne-rNP/strings.xml +++ b/java/res/values-ne-rNP/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्पहरू"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"लग निर्देशनहरू शोध गर्नुहोस्"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"सम्पर्क नामहरू हेर्नुहोस्"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"तपाईँको सम्पर्क सूचीबाट हिज्जे परीक्षकले प्रविष्टिहरूको प्रयोग गर्छ"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"कुञ्जी थिच्दा भाइब्रेट"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"सम्पर्क नामहरू सुझाव गर्नुहोस्"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव र सुधारका लागि सम्पर्कबाट नामहरू प्रयोग गर्नुहोस्"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"निजीकृत सुझावहरू"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"डबल-स्पेस पूर्णविराम"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबारमा डबल ट्याप गर्नाले पूर्णविरामपछि स्पेस राख्दछ"</string> <string name="auto_cap" msgid="1719746674854628252">"स्वतः पूँजिकरण"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाहरू"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"बचत गर्न पुनः छुनुहोस्"</string> <string name="has_dictionary" msgid="6071847973466625007">"उपलब्ध शब्दकोश"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"प्रयोगकर्ता प्रतिक्रिया सक्षम पार्नुहोस्"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"स्वचालित रूपमा प्रयोग तथ्याङ्कहरू र क्यास रिपोर्टहरू पठाएर यस इनपुट विधि सम्पादकलाई सुधार्न सहयोग गर्नुहोस्।"</string> <string name="keyboard_layout" msgid="8451164783510487501">"किबोर्ड थिम"</string> <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेजी (युके)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेजी (युएस्)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"सक्षम पार्नुहोस्"</string> <string name="not_now" msgid="6172462888202790482">"अहिले होइन"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"यस्तो इनपुट शैली पहिले नै अवस्थित छ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"प्रयोग अध्ययन मोड"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुञ्जी लामो थिचाइ ढिलाइ"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुञ्जी थिचाइ भाइब्रेसन अवधि"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुञ्जी थिचाइ आवाज भोल्युम"</string> diff --git a/java/res/values-nl/strings-action-keys.xml b/java/res/values-nl/strings-action-keys.xml index c1ce25acf..c9c68bd25 100644 --- a/java/res/values-nl/strings-action-keys.xml +++ b/java/res/values-nl/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Vorig"</string> <string name="label_done_key" msgid="7564866296502630852">"Klaar"</string> <string name="label_send_key" msgid="482252074224462163">"Verz."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauze"</string> <string name="label_wait_key" msgid="5891247853595466039">"Wacht"</string> </resources> diff --git a/java/res/values-nl/strings-talkback-descriptions.xml b/java/res/values-nl/strings-talkback-descriptions.xml index 8dcd5748d..57bbf91e2 100644 --- a/java/res/values-nl/strings-talkback-descriptions.xml +++ b/java/res/values-nl/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Geen tekst ingevoerd"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Met <xliff:g id="KEY_NAME">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Met <xliff:g id="KEY_NAME">%1$s</xliff:g> voert u automatische correctie uit"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Toetscode %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift aan (tik om uit te schakelen)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock aan (tik om uit te schakelen)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Meer symbolen"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbolen"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Verwijderen"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbolen"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letters"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Vorige"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift ingeschakeld"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock ingeschakeld"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift uitgeschakeld"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbolen"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modus voor meer symbolen"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Alfanumeriek toetsenbord"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Toetsenbord telefoon"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefoonsymbolen"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Plaatsen"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbolen"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticons"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index 342464fc8..94ccd6b46 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"De spellingcontrole gebruikt items uit uw contactenlijst"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Contactnamen suggereren"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Gepersonaliseerde suggesties"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dubbeltik is punt, spatie"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbeltik op spatiebalk voor een punt gevolgd door een spatie"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Invoertalen"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Raak nogmaals aan om op te slaan"</string> <string name="has_dictionary" msgid="6071847973466625007">"Woordenboek beschikbaar"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Gebruikersfeedback inschakelen."</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Help deze invoermethode te verbeteren door automatisch gebruiksstatistieken en crashmeldingen te verzenden."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Toetsenbordthema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engels (GB)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engels (VS)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Inschakelen"</string> <string name="not_now" msgid="6172462888202790482">"Niet nu"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Dezelfde invoerstijl bestaat al: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus voor gebruiksvriendelijkheidsonderzoek"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Vertraging toets lang indrukkn"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trilingsduur bij toetsgebruik"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Geluidsvolume bij toetsgebruik"</string> diff --git a/java/res/values-pl/strings-action-keys.xml b/java/res/values-pl/strings-action-keys.xml index 2984b98b8..d641cbc9e 100644 --- a/java/res/values-pl/strings-action-keys.xml +++ b/java/res/values-pl/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Wróć"</string> <string name="label_done_key" msgid="7564866296502630852">"Gotowe"</string> <string name="label_send_key" msgid="482252074224462163">"Wyślij"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauza"</string> <string name="label_wait_key" msgid="5891247853595466039">"Czekaj"</string> </resources> diff --git a/java/res/values-pl/strings-talkback-descriptions.xml b/java/res/values-pl/strings-talkback-descriptions.xml index 567bb3b23..1708ae02a 100644 --- a/java/res/values-pl/strings-talkback-descriptions.xml +++ b/java/res/values-pl/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nie wpisano tekstu"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> wykonuje autokorektę"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kod klawisza: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift włączony (kliknij, by wyłączyć)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock włączony (kliknij, by wyłączyć)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Więcej symboli"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symbole"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Usuń"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symbole"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Litery"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Wstecz"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift włączony"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock włączony"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift wyłączony"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Tryb symboli"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Tryb dodatkowych symboli"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Tryb liter"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Tryb telefonu"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Tryb symboli telefonu"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Miejsca"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symbole"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikony"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index c4261c60e..218e6197e 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Sprawdzanie pisowni bierze pod uwagę wpisy z listy kontaktów."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Proponuj osoby z kontaktów"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Spersonalizowane sugestie"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Szybka kropka ze spacją"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dwukrotne kliknięcie spacji wstawia kropkę ze spacją"</string> <string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Języki wprowadzania"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dotknij ponownie, aby zapisać"</string> <string name="has_dictionary" msgid="6071847973466625007">"Słownik dostępny"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Włącz przesyłanie opinii użytkownika"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Pomóż ulepszyć edytor wprowadzania tekstu, automatycznie wysyłając statystyki użycia i raporty o awariach."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motyw klawiatury"</string> <string name="subtype_en_GB" msgid="88170601942311355">"angielski (Wielka Brytania)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"angielski (Stany Zjednoczone)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Włącz"</string> <string name="not_now" msgid="6172462888202790482">"Nie teraz"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Taki styl wprowadzania już istnieje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Tryb badania przydatności"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Opóźnienie przy długim naciśnięciu"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Wibracja przy naciśniętym klawiszu"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Głośność przy naciśniętym klawiszu"</string> diff --git a/java/res/values-pt-rPT/strings-action-keys.xml b/java/res/values-pt-rPT/strings-action-keys.xml index 7a7559fea..1b5921f3e 100644 --- a/java/res/values-pt-rPT/strings-action-keys.xml +++ b/java/res/values-pt-rPT/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Ant."</string> <string name="label_done_key" msgid="7564866296502630852">"Conc."</string> <string name="label_send_key" msgid="482252074224462163">"Env."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausa"</string> <string name="label_wait_key" msgid="5891247853595466039">"Esp."</string> </resources> diff --git a/java/res/values-pt-rPT/strings-talkback-descriptions.xml b/java/res/values-pt-rPT/strings-talkback-descriptions.xml index bf51caa78..91d187ec9 100644 --- a/java/res/values-pt-rPT/strings-talkback-descriptions.xml +++ b/java/res/values-pt-rPT/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nenhum texto digitado"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> para <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> executa a correção automática"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Código da tecla %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift ativado (tocar para desativar)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock ativado (tocar para desativar)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Mais símbolos"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Símbolos"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Eliminar"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Símbolos"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letras"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift ativado"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock ativado"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift desativado"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modo de símbolos"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modo Mais símbolos"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modo de letras"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modo de telemóvel"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modo de símbolos de telemóvel"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Locais"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Símbolos"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Ícones expressivos"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index 8b75dd0dd..86a619bc6 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico utiliza entradas da sua lista de contactos"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de Contactos"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Sugestões personalizadas"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Ponto de espaço duplo"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Tocar duas vezes na barra espaço insere ponto seguido de espaço"</string> <string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Idiomas de introdução"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toque novamente para guardar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Ativar comentários do utilizador"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Envie automaticamente estatísticas de utilização e relatórios de falhas e ajude-nos a melhorar este editor do método de introdução."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Inglês (RU)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Inglês (EUA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Ativar"</string> <string name="not_now" msgid="6172462888202790482">"Agora não"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Já existe o mesmo estilo de introdução: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo da capacidade de utilização"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao manter tecla premida"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração vibr. ao premir teclas"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume do som ao premir teclas"</string> diff --git a/java/res/values-pt/strings-action-keys.xml b/java/res/values-pt/strings-action-keys.xml index 1d8e760e7..470846b21 100644 --- a/java/res/values-pt/strings-action-keys.xml +++ b/java/res/values-pt/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Ant."</string> <string name="label_done_key" msgid="7564866296502630852">"Conc."</string> <string name="label_send_key" msgid="482252074224462163">"Env."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausa"</string> <string name="label_wait_key" msgid="5891247853595466039">"Esp."</string> </resources> diff --git a/java/res/values-pt/strings-emoji-descriptions.xml b/java/res/values-pt/strings-emoji-descriptions.xml new file mode 100644 index 000000000..84e2cc08c --- /dev/null +++ b/java/res/values-pt/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Símbolo de copyright"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Símbolo de marca registrada"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Ponto de exclamação duplo"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Ponto de exclamação e ponto de interrogação"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Símbolo de marca comercial"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Fonte de informação"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Seta para esquerda e para direita"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Seta para cima e para baixo"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Seta para o noroeste"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Seta para o nordeste"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Seta para o sudeste"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Seta para o sudoeste"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Seta para a esquerda com gancho"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Seta para a direita com gancho"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Relógio de pulso"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Ampulheta"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Triângulo duplo preto para a direita"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Triângulo duplo preto para a esquerda"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Triângulo duplo preto para cima"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Triângulo duplo preto para baixo"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Despertador"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Ampulheta com areia correndo"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Letra M maiúscula contida em círculo"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Quadrado pequeno preto"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Quadrado pequeno branco"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Triângulo preto para a direita"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Triângulo preto para a esquerda"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Quadrado médio branco"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Quadrado médio preto"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Quadrado médio branco"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Quadrado médio preto"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Sol preto com raios"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Nuvem"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Telefone preto"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Urna de votação com marca de verificação"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Guarda-chuva com gotas de chuva"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Bebida quente"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Dedo indicador branco apontando para cima de frente"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Rosto sorridente branco"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Áries"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Touro"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Gêmeos"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Câncer"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Leão"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Virgem"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Libra"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Escorpião"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Sagitário"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Capricórnio"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Aquário"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Peixes"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Naipe preto de espadas"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Naipe preto de paus"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Naipe preto de copas"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Naipe preto de ouros"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Águas termais"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Símbolo de reciclagem universal preto"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Símbolo de cadeira de rodas"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Âncora"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Sinal de aviso"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Sinal de alta tensão"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Círculo branco médio"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Círculo preto médio"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Bola de futebol"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Bola de beisebol"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Boneco de neve sem neve"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Sol atrás da nuvem"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Serpentário"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Entrada proíbida"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Igreja"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Fonte"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Bandeira no buraco"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Barco a vela"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Barraca"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Bomba de combustível"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Tesoura preta"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Marca de verificação grossa branca"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Avião"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Envelope"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Punho levantado"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Mão levantada"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Mão de vitória"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Lápis"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Ponta de caneta-tinteiro preta"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Marca de verificação grossa"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Sinal de multiplicação x grosso"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Brilhos"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Asterisco de oito pontas"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Estrela preta de oito pontas"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Floco de neve"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Brilho"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Sinal de cruzamento"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"egativo de sinal de cruzamento contido em quadrado"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Ornamento de ponto de interrogação preto"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Ornamento de ponto de interrogação branco"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Ornamento de ponto de exclamação branco"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Símbolo de ponto de exclamação grosso"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Coração preto grosso"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Sinal de adição grosso"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Sinal de subtração grosso"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Sinal de divisão grosso"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Seta para a direita preta"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Laçada"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Laçada dupla"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Seta para a direita com curva para cima"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Seta para a direita com curva para baixo"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Seta preta para a esquerda"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Seta preta para cima"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Seta preta para baixo"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Quadrado grande preto"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Quadrado grande branco"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Estrela média branca"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Círculo grande grosso"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Traço ondulado"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Marca de alternação de parte"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Ideograma \"parabéns\" contido em círculo"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Ideograma \"segredo\" contido em círculo"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Pedra de mahjong do dragão vermelho"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Curinga preto de baralho"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Tipo sanguíneo A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Tipo sanguíneo B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Tipo sanguíneo O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Estacionamento"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Tipo sanguíneo AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"\"CL\" em letras de fôrma"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"\"COOL\" em letras de fôrma"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"\"‘FREE\" em letras de fôrma"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"\"ID\" em letras de fôrma"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"\"NEW\" em letras de fôrma"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"\"NG\" em letras de fôrma"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"\"OK\" em letras de fôrma"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"\"SOS\" em letras de fôrma"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"\"UP!\" em letras de fôrma"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"\"VS\" em letras de fôrma"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"Caracteres koko em katakana contidos em quadrado"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"Caractere sa em katakana contido em quadrado"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Ideograma \"gratuito\" contido em quadrado"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Ideograma \"lugar reservado\" contido em quadrado"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Ideograma \"proibição\" contido em quadrado"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Ideograma \"vaga\" contido em quadrado"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Ideograma \"aceitação\" contido em quadrado"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Ideograma \"lotação esgotada\" contido em quadrado"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Ideograma \"pago\" contido em quadrado"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Ideograma \"mensal\" contido em quadrado"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Ideograma \"aplicação\" contido em quadrado"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Ideograma \"desconto\" contido em quadrado"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Ideograma \"em atividade\" contido em quadrado"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Ideograma \"vantagem\" contido em círculo"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Ideograma \"aceitar\" contido em círculo"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Ciclone"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Enevoado"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Guarda-chuva fechado"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Noite estrelada"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Nascer do sol nas montanhas"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Nascer do sol"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Cidade ao entardecer"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Pôr do sol nos prédios"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Arco-íris"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Ponte à noite"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Onda do mar"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Vulcão"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Via Láctea"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Globo terrestre Europa/África"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Globo terrestre Américas"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Globo terrestre Ásia/Austrália"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Globo com meridianos"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Símbolo da lua nova"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Símbolo da lua crescente"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Símbolo da lua em quarto crescente"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Símbolo da lua crescente gibosa"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Símbolo da lua cheia"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Símbolo da lua minguante gibosa"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Símbolo da lua em quarto minguante"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Símbolo da lua minguante"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Lua crescente"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Lua nova com rosto"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Lua em quarto crescente com rosto"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Lua em quarto minguante com rosto"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Lua cheia com rosto"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Sol com rosto"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Estrela brilhante"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Estrela cadente"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Castanha"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Muda"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Árvore perenifólia"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Árvore decídua"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Palmeira"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Cacto"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Tulipa"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Flor de cerejeira"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Rosa"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Hibisco"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Girassol"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Florescência"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Espiga de milho"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Espiga de arroz"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Erva"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Trevo de quatro folhas"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Folha de bordo"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Folha caída"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Folha ao sabor do vento"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Cogumelo"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Tomate"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Berinjela"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Uvas"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Melão"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Melancia"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Tangerina"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Limão"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Banana"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Abacaxi"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Maçã vermelha"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Maçã verde"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Pera"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Pêssego"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Cerejas"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Morango"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Hambúrguer"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Fatia de pizza"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Carne com osso"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Coxa de frango"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Biscoito de arroz japonês"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Bolinho de arroz japonês"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Arroz cozido"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Arroz com curry"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Tigela quente"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Espaguete"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Pão"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Batatas fritas"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Batata doce assada"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Dango"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oden"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Sushi"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Camarão frito"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Kamaboko com design de espiral"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Sorvete de massa"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Raspadinha"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Sorvete"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Rosquinha"</string> + <string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cookie"</string> + <string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Barra de chocolate"</string> + <string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Doce"</string> + <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Pirulito"</string> + <string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Flan"</string> + <string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Pote de mel"</string> + <string name="spoken_emoji_1F370" msgid="7243244547866114951">"Torta"</string> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Marmiteira bento"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Tigela de comida"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Cozinha"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Garfo e faca"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Xícara sem asa"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Garrafa e copo para saquê"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Copo para vinho"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Copo para coquetel"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Bebida tropical"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Caneca de cerveja"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Canecas de cerveja brindando"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Mamadeira"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Fita de presente"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Presente embrulhado"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Bolo de aniversário"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Jack Lanterna"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Árvore de Natal"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Papai Noel"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Fogos de artifício"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Estrelinha"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Balão"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Lança confetes"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Bola de confetes"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Árvore de Tanabata"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Bandeiras cruzadas"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Decoração Kadomatsu"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Bonecas japonesas"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Biruta de carpa Koinobori"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Sinos de vento"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Cerimônia de contemplação da lua Otsukimi"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Mochila escolar"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Capelo"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Carrossel de cavalinho"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Roda-gigante"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Montanha russa"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Vara de pescar e peixe"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Microfone"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Câmera cinematográfica"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Cinema"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Fone de ouvido"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Aquarela"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Cartola"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Tenda de circo"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Ingresso"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Claquete"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Artes cênicas"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Videogame"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Bela pontaria"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Caça-níqueis"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Bilhar"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Dados"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Boliche"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Baralho temático de flores"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Nota musical"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Várias notas musicais"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Saxofone"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Guitarra"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Teclado musical"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Trompete"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Violino"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Partitura"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Camiseta de corrida com faixa"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Raquete e bola de tênis"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Esqui e bota de esqui"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Bola de basquete e cesto"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Bandeira xadrez"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Snowboarder"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Corredor"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Surfista"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Troféu"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Corridas de cavalos"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Futebol americano"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Bola de rugby"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Nadador"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Construção de casas"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Casa com jardim"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Edifício comercial"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Correio japonês"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Correio europeu"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Hospital"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Banco"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Caixa eletrônico"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Hotel"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Motel"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Loja de conveniência"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Escola"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Loja de departamentos"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Fábrica"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Luminária izakaya"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Castelo japonês"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Castelo europeu"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Rato"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Camundongo"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Boi"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Búfalo asiático"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Vaca"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Leopardo"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Coelho"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Gato"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Dragão"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Crocodilo"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Baleia"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Caracol"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Serpente"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Cavalo"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Carneiro"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Cabra"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Ovelha"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Macaco"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Galo"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Galinha"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Cachorro"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Porco"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Javali"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Elefante"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Polvo"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Concha espiral"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Inseto"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Formiga"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Abelha"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Joaninha"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Peixe"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Peixe tropical"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Peixe-balão"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Tartaruga"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Pintinho nascendo"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Pintinho"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Pintinho de frente"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Pássaro"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Pinguim"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Coala"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Poodle"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Dromedário"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Camelo bactriano"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Golfinho"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Smiley de rato"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Smiley de vaca"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Smiley de tigre"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Smiley de coelho"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Smiley de gato"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Smiley de dragão"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Baleia jorrando água"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Smiley de cavalo"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Smiley de macaco"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Smiley de cachorro"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Smiley de porco"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Smiley de sapo"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Smiley de porquinho-da-índia"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Smiley de lobo"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Smiley de urso"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Smiley de panda"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Focinho de porco"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Pegadas de patas"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Olhos"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Orelha"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Nariz"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Boca"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Língua"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Dedo indicador branco apontando para cima"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Dedo indicador branco apontando para baixo"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Dedo indicador branco apontando para a esquerda"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Dedo indicador branco apontando para a direita"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Sinal de mão em punho"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Sinal de mão acenando"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Sinal de mão indicando OK"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Sinal com polegar para cima"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Sinal com polegar para baixo"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Sinal de bater palmas"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Sinal de mãos abertas"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Coroa"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Chapéu de mulher"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Óculos de grau"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Gravata"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Camiseta"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Calça jeans"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Vestido"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Quimono"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Biquíni"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Roupas de mulher"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Bolsinha"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Bolsa"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Bolsa saco"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Sapato masculino"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Tênis"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Sapato de salto alto"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Sandália feminina"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Botas femininas"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Pegadas"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Busto em silhueta"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Bustos em silhueta"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Menino"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Menina"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Homem"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Mulher"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Família"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Homem e mulher de mãos dadas"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Dois homens de mãos dadas"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Duas mulheres de mãos dadas"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Policial"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Mulher com orelhas de coelho"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Noiva com véu"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Pessoa loira"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Homem com gua pi mao"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Homem com turbante"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Idoso"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Idosa"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Bebê"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Operário de construção civil"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Princesa"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Ogro japonês"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Gnomo japonês"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Fantasma"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Anjo bebê"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Alienígena"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Monstro alienígena"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Duende"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Caveira"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Recepcionista de balcão de informações"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Guarda"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Dançarina"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Batom"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Esmalte"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Massagem facial"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Corte de cabelo"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Poste de barbearia"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Seringa"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Comprimido"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Marca de beijo"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Carta de amor"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Anel"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Gema"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Beijo"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Buquê"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Casal com coração"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Casamento"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Coração batendo"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Coração partido"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Dois corações"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Coração brilhando"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Coração crescendo"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Coração com flecha"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Coração azul"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Coração verde"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Coração amarelo"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Coração roxo"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Coração com fita"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Corações em círculo"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Decoração de coração"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Forma de losango com ponto interno"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Lâmpada elétrica"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Símbolo de raiva"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Bomba"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Símbolo de sono"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Símbolo de colisão"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Símbolo da gota de suor"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Gota"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Símbolo de traço"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Cocô"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Bíceps contraídos"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Símbolo de tontura"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Balão de fala"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Balão de pensamento"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Flor branca"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Símbolo de cem pontos"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Saco de dinheiro"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Câmbio de moeda"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Sinal de cifrão grosso"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Cartão de crédito"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Nota bancária com sinal de iene"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Nota bancária com sinal de dólar"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Nota bancária com sinal de euro"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Nota bancária com sinal de libra"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Dinheiro com asas"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Gráfico com tendência ascendente e símbolo de iene"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Assento"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Computador pessoal"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Valise"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Minidisco"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Disquete"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Disco óptico"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Pasta de arquivos"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Pasta de arquivos aberta"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Página com curva"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Página voltada para cima"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Calendário"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Calendário destacável"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Índice de cartões"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Gráfico com tendência ascendente"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Gráfico com tendência descendente"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Gráfico de barras"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Prancheta"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Alfinete"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Alfinete redondo"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Clipe de papel"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Régua reta"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Régua triangular"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"Guias de marca-página"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Livro-razão"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Bloco de anotações"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Bloco de anotações com capa decorativa"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Livro fechado"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Livro aberto"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Livro verde"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Livro azul"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Livro laranja"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Livros"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Crachá"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Pergaminho"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Memorando"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Telefone"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Pager"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Aparelho de fax"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Antena de satélite"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Alto-falante para discursos públicos"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Megafone em uso"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Bandeja da caixa de saída"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Bandeja da caixa de entrada"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Pacote"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"Símbolo de e-mail"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Envelope recebido"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Envelope com seta para baixo"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Caixa de correio fechada com bandeira abaixada"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Caixa de correio fechada com bandeira levantada"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Caixa de correio aberta com bandeira levantada"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Caixa de correio aberta com bandeira abaixada"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Caixa de correio"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Corneta de correio"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Jornal"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Celular"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Celular com seta para a direita"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Modo de vibração"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Celular desligado"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Proibido usar celular"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Antena com barras"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Câmera"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Filmadora"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Televisão"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Rádio"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Videocassete"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Setas entrelaçadas para a direita"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Setas para a esquerda e para a direita em sentido horário"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Setas para a esquerda e para a direita em sentido horário com \"1\""</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Setas para baixo e para cima em sentido horário"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Setas para baixo e para cima em sentido anti-horário"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Símbolo de baixo brilho"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Símbolo de alto brilho"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Alto-falante cortado"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Alto-falante"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Alto-falante com onda sonora"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Alto-falante com três ondas sonoras"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Bateria"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Tomada elétrica"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Lente de aumento virada para a esquerda"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Lente de aumento virada para a direita"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Cadeado com caneta-tinteiro"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Cadeado fechado com chave"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Chave"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Cadeado"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Cadeado aberto"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Sino"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Sino cortado"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Marca-página"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Símbolo de link"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Botão de opção"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"\"Back\" com seta para a esquerda"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"\"End\" com seta para a esquerda"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"\"On!\" com seta para a esquerda e para a direita"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"\"Soon\" com seta para a direita"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"\"Top\" com seta para cima"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Símbolo de proibido para menores de 18"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Tecla do dez"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Símbolo de entrada de letras latinas maiúsculas"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Símbolo de entrada de letras latinas minúsculas"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Símbolo de entrada de números"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Símbolo de entrada de símbolos"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Símbolo de entrada de letras latinas"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Fogo"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Tocha elétrica"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Chave inglesa"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Martelo"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Porca e parafuso"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Facão"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Pistola"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Microscópio"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Telescópio"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Bola de cristal"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Estrela de seis pontas com ponto central"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Símbolo japonês para iniciante"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Emblema de tridente"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Botão quadrado preto"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Botão quadrado branco"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Círculo grande vermelho"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Círculo grande azul"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Losango grande laranja"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Losango grande azul"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Losango pequeno laranja"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Losango pequeno azul"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Triângulo vermelho apontando para cima"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Triângulo vermelho apontando para baixo"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Triângulo pequeno vermelho apontando para cima"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Triângulo pequeno vermelho apontando para baixo"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Relógio mostrando 01h00"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Relógio mostrando 02h00"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Relógio mostrando 03h00"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Relógio mostrando 04h00"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Relógio mostrando 05h00"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Relógio mostrando 06h00"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Relógio mostrando 07h00"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Relógio mostrando 08h00"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Relógio mostrando 09h00"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Relógio mostrando 10h00"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Relógio mostrando 11h00"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Relógio mostrando 12h00"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Relógio mostrando 01h30"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Relógio mostrando 02h30"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Relógio mostrando 03h30"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Relógio mostrando 04h30"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Relógio mostrando 05h30"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Relógio mostrando 06h30"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Relógio mostrando 07h30"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Relógio mostrando 08h30"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Relógio mostrando 09h30"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Relógio mostrando 10h30"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Relógio mostrando 11h30"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Relógio mostrando 12h30"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Monte Fuji"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Torre de Tóquio"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Estátua da Liberdade"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Silhueta do Japão"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Moai"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Rosto mostrando os dentes"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Rosto mostrando os dentes com olhos sorridentes"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Rosto com lágrimas de alegria"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Rosto sorridente com boca aberta"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Rosto sorridente com boca aberta e olhos sorridentes"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Rosto sorridente com boca aberta e suando frio"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Rosto sorridente com boca aberta e olhos bem fechados"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Rosto sorridente com auréola"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Rosto sorridente com chifres"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Rosto com olho piscando"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Rosto sorridente com olhos sorridentes"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Rosto saboreando comida deliciosa"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Rosto aliviado"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Rosto sorridente com olhos de coração"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Rosto sorridente com óculos de sol"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Rosto com meio sorriso"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Rosto neutro"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Rosto sem expressão"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Rosto de desinteressado"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Rosto suando frio"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Rosto pensativo"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Rosto confuso"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Rosto desconcertado"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Rosto beijando"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Rosto jogando beijo"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Rosto beijando com olhos sorridentes"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Rosto beijando com olhos fechados"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Rosto com língua de fora"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Rosto com língua de fora e piscando"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Rosto com língua de fora e olhos bem fechados"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Rosto de frustrado"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Rosto preocupado"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Rosto nervoso"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Rosto de desaprovação"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Rosto chorando"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Rosto de perseverante"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Rosto com aparência de triunfo"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Rosto com misto de decepção e alívio"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Rosto franzido com boca aberta"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Rosto angustiado"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Rosto temeroso"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Rosto fatigado"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Rosto sonolento"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Rosto cansado"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Rosto mostrando os dentes"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Rosto chorando alto"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Rosto com boca aberta"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Rosto silenciado"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Rosto com boca aberta e suando frio"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Rosto gritando de medo"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Rosto surpreso"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Rosto enrubescido"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Rosto dormindo"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Rosto tonto"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Rosto sem boca"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Rosto com máscara hospitalar"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Smiley de gato feliz com olhos sorridentes"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Smiley de gato com lágrimas de alegria"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Smiley de gato sorridente com boca aberta"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Smiley de gato sorridente com olhos de coração"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Smiley de gato com sorriso maroto"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Smiley de gato beijando com olhos fechados"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Smiley de gato de desaprovação"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Smiley de gato chorando"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Smiley de gato fatigado"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Smiley com gesto de reprovação"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Smiley com gesto de ok"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Pessoa curvando-se bastante"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Macaco cego"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Macaco surdo"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Macaco mudo"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Pessoa feliz levantando uma mão"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Pessoa levantando as mãos em comemoração"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Pessoa franzindo a testa"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Pessoa com rosto de desaprovação"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Pessoa com mãos unidas em oração"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Foguete"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Helicóptero"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Locomotiva a vapor"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Vagão"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Trem de alta velocidade"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Trem-bala de alta velocidade"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Trem"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metrô"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Metrô leve"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Estação"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Bonde"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Elétrico"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Ônibus"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Ônibus de frente"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Trólebus"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Ponto de ônibus"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Micro-ônibus"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulância"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Caminhão de bombeiros"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Viatura policial"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Viatura policial de frente"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Táxi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Táxi de frente"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Automóvel"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Automóvel de frente"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Veículo recreativo"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Caminhão de entrega"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Caminhão articulado"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Trator"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Monotrilho"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Trem de montanha"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Trem suspenso"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Bondinho"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Teleférico"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Navio"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Barco a remo"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Lancha"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Semáforo horizontal"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Semáforo vertical"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Sinal de construção"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Giroflex de viaturas policiais"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Bandeira triangular em poste"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Porta"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Sinal de entrada proibida"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Símbolo de permitido fumar"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Símbolo de proibido fumar"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Símbolo de \"jogue o lixo no lixo\""</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Símbolo de proibido jogar lixo"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Símbolo de água potável"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Símbolo de água não potável"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Bicicleta"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Proibido bicicletas"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Ciclista"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Ciclista de montanha"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Pedestre"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Proibido pedestres"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Travessia de crianças"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Símbolo masculino"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Símbolo feminino"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Banheiro"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Símbolo de bebê"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Vaso sanitário"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"WC"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Chuveiro"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Banho"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Banheira"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Controle de passaportes"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Alfândega"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Esteira de bagagem"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Bagagem abandonada"</string> +</resources> diff --git a/java/res/values-pt/strings-talkback-descriptions.xml b/java/res/values-pt/strings-talkback-descriptions.xml index 01aac3931..065cdaec6 100644 --- a/java/res/values-pt/strings-talkback-descriptions.xml +++ b/java/res/values-pt/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nenhum texto digitado"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> para <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> realiza correção automática"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Código de tecla %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift ativado (toque para desativar)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock ativado (toque para desativar)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Mais símbolos"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Símbolos"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Excluir"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Símbolos"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Letras"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Anterior"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift ativado"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock ativado"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift desativado"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modo de símbolos"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modo mais símbolos"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modo de letras"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modo de telefone"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modo de símbolos do telefone"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Lugares"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Símbolos"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticons"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index afee26d25..4d65ec86c 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico usa entradas de sua lista de contatos"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de contato"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Sugestões personalizadas"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Duplo espaço para ponto"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Toque duplo na barra de espaço insere um ponto seguido de espaço"</string> <string name="auto_cap" msgid="1719746674854628252">"Capitalização automática"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Idiomas de entrada"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Toque novamente para salvar"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicionário disponível"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Ativar comentário do usuário"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ajude a melhorar este editor de método de entrada enviando automaticamente estatísticas de uso e relatórios de falhas."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema do teclado"</string> <string name="subtype_en_GB" msgid="88170601942311355">"inglês (Reino Unido)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"inglês (EUA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Ativar"</string> <string name="not_now" msgid="6172462888202790482">"Agora não"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"O estilo de entrada já existe: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo de estudo de utilização"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Atraso ao pressionar teclas"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Duração da vibração ao tocar"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ao tocar na tela"</string> diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 2ea98cf53..526ec73bd 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -22,7 +22,6 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- no translation found for english_ime_input_options (3909945612939668554) --> <skip /> - <!-- no translation found for english_ime_research_log (8492602295696577851) --> <skip /> <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) --> <skip /> @@ -134,8 +133,6 @@ <!-- no translation found for hint_add_to_dictionary (573678656946085380) --> <skip /> <string name="has_dictionary" msgid="6071847973466625007">"Dicziunari disponibel"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Activar il feedback da l\'utilisader"</string> - <!-- no translation found for prefs_description_log (7525225584555429211) --> <skip /> <!-- no translation found for keyboard_layout (8451164783510487501) --> <skip /> @@ -191,7 +188,6 @@ <skip /> <!-- no translation found for custom_input_style_already_exists (8008728952215449707) --> <skip /> - <!-- no translation found for prefs_usability_study_mode (1261130555134595254) --> <skip /> <!-- no translation found for prefs_key_longpress_timeout_settings (6102240298932897873) --> <skip /> diff --git a/java/res/values-ro/strings-action-keys.xml b/java/res/values-ro/strings-action-keys.xml index 51aa82f9c..bee4b12ea 100644 --- a/java/res/values-ro/strings-action-keys.xml +++ b/java/res/values-ro/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Înap."</string> <string name="label_done_key" msgid="7564866296502630852">"Gata"</string> <string name="label_send_key" msgid="482252074224462163">"Trim."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauză"</string> <string name="label_wait_key" msgid="5891247853595466039">"Așt."</string> </resources> diff --git a/java/res/values-ro/strings-emoji-descriptions.xml b/java/res/values-ro/strings-emoji-descriptions.xml new file mode 100644 index 000000000..f44a0b974 --- /dev/null +++ b/java/res/values-ro/strings-emoji-descriptions.xml @@ -0,0 +1,851 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + --> + +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="spoken_emoji_00A9" msgid="2859822817116803638">"Semnul Copyright"</string> + <string name="spoken_emoji_00AE" msgid="7708335454134589027">"Semnul Marcă înregistrată"</string> + <string name="spoken_emoji_203C" msgid="153340916701508663">"Semn de exclamare dublu"</string> + <string name="spoken_emoji_2049" msgid="4877256448299555371">"Semn de exclamare și întrebare"</string> + <string name="spoken_emoji_2122" msgid="9188440722954720429">"Semn marcă înregistrată"</string> + <string name="spoken_emoji_2139" msgid="9114342638917304327">"Sursă de informații"</string> + <string name="spoken_emoji_2194" msgid="8055202727034946680">"Săgeată stânga-dreapta"</string> + <string name="spoken_emoji_2195" msgid="8028122253301087407">"Săgeată sus-jos"</string> + <string name="spoken_emoji_2196" msgid="4019164898967854363">"Săgeată nord-vest"</string> + <string name="spoken_emoji_2197" msgid="4255723717709017801">"Săgeată nord-est"</string> + <string name="spoken_emoji_2198" msgid="1452063451313622090">"Săgeată sud-est"</string> + <string name="spoken_emoji_2199" msgid="6942722693368807849">"Săgeată sud-vest"</string> + <string name="spoken_emoji_21A9" msgid="5204750172335111188">"Săgeată încovoiată spre stânga"</string> + <string name="spoken_emoji_21AA" msgid="3950259884359247006">"Săgeată încovoiată spre dreapta"</string> + <string name="spoken_emoji_231A" msgid="6751448803233874993">"Ceas de mână"</string> + <string name="spoken_emoji_231B" msgid="5956428809948426182">"Clepsidră"</string> + <string name="spoken_emoji_23E9" msgid="4022497733535162237">"Triunghi dublu, negru, orientat spre dreapta"</string> + <string name="spoken_emoji_23EA" msgid="2251396938087774944">"Triunghi dublu, negru, orientat spre stânga"</string> + <string name="spoken_emoji_23EB" msgid="3746885195641491865">"Triunghi dublu, negru, orientat în sus"</string> + <string name="spoken_emoji_23EC" msgid="7852372752901163416">"Triunghi dublu, negru, orientat în jos"</string> + <string name="spoken_emoji_23F0" msgid="8474219588750627870">"Ceas deşteptător"</string> + <string name="spoken_emoji_23F3" msgid="166900119581024371">"Clepsidră cu nisip"</string> + <string name="spoken_emoji_24C2" msgid="3948348737566038470">"Majuscula „M”, din alfabetul latin, încercuită"</string> + <string name="spoken_emoji_25AA" msgid="7865181015100227349">"Pătrat mic negru"</string> + <string name="spoken_emoji_25AB" msgid="6446532820937381457">"Pătrat mic alb"</string> + <string name="spoken_emoji_25B6" msgid="2423897708496040947">"Triunghi negru, orientat spre dreapta"</string> + <string name="spoken_emoji_25C0" msgid="3595083440074484934">"Triunghi negru, orientat spre stânga"</string> + <string name="spoken_emoji_25FB" msgid="4838691986881215419">"Pătrat mediu alb"</string> + <string name="spoken_emoji_25FC" msgid="7008859564991191050">"Pătrat negru mediu"</string> + <string name="spoken_emoji_25FD" msgid="7673439755069217479">"Pătrat alb, mijlociu spre mic"</string> + <string name="spoken_emoji_25FE" msgid="6782214109919768923">"Pătrat negru, mijlociu spre mic"</string> + <string name="spoken_emoji_2600" msgid="2272722634618990413">"Soare negru cu raze"</string> + <string name="spoken_emoji_2601" msgid="6205136889311537150">"Nor"</string> + <string name="spoken_emoji_260E" msgid="8670395193046424238">"Telefon negru"</string> + <string name="spoken_emoji_2611" msgid="4530550203347054611">"Casetă cu bifă"</string> + <string name="spoken_emoji_2614" msgid="1612791247861229500">"Umbrelă cu picături de ploaie"</string> + <string name="spoken_emoji_2615" msgid="3320562382424018588">"Băuturi calde"</string> + <string name="spoken_emoji_261D" msgid="4690554173549768467">"Deget arătător alb, orientat în sus"</string> + <string name="spoken_emoji_263A" msgid="3170094381521989300">"Față zâmbitoare albă"</string> + <string name="spoken_emoji_2648" msgid="4621241062667020673">"Berbec"</string> + <string name="spoken_emoji_2649" msgid="7694461245947059086">"Taur"</string> + <string name="spoken_emoji_264A" msgid="1258074605878705030">"Gemeni"</string> + <string name="spoken_emoji_264B" msgid="4409219914377810956">"Rac"</string> + <string name="spoken_emoji_264C" msgid="6520255367817054163">"Leu"</string> + <string name="spoken_emoji_264D" msgid="1504758945499854018">"Fecioară"</string> + <string name="spoken_emoji_264E" msgid="2354847104530633519">"Balanţă"</string> + <string name="spoken_emoji_264F" msgid="5822933280406416112">"Scorpion"</string> + <string name="spoken_emoji_2650" msgid="4832481156714796163">"Săgetător"</string> + <string name="spoken_emoji_2651" msgid="840953134601595090">"Capricorn"</string> + <string name="spoken_emoji_2652" msgid="3586925968718775281">"Vărsător"</string> + <string name="spoken_emoji_2653" msgid="8420547731496254492">"Peşti"</string> + <string name="spoken_emoji_2660" msgid="4541170554542412536">"Pică neagră"</string> + <string name="spoken_emoji_2663" msgid="3669352721942285724">"Treflă neagră"</string> + <string name="spoken_emoji_2665" msgid="6347941599683765843">"Inimă neagra"</string> + <string name="spoken_emoji_2666" msgid="8296769213401115999">"Romb negru"</string> + <string name="spoken_emoji_2668" msgid="7063148281053820386">"Izvoare termale"</string> + <string name="spoken_emoji_267B" msgid="21716857176812762">"Simbol negru, universal, de reciclare"</string> + <string name="spoken_emoji_267F" msgid="8833496533226475443">"Simbol pentru scaun cu rotile"</string> + <string name="spoken_emoji_2693" msgid="7443148847598433088">"Ancoră"</string> + <string name="spoken_emoji_26A0" msgid="6272635532992727510">"Semn de avertizare"</string> + <string name="spoken_emoji_26A1" msgid="5604749644693339145">"Semn de înaltă tensiune"</string> + <string name="spoken_emoji_26AA" msgid="8005748091690377153">"Cerc alb mediu"</string> + <string name="spoken_emoji_26AB" msgid="1655910278422753244">"Cerc negru mediu"</string> + <string name="spoken_emoji_26BD" msgid="1545218197938889737">"Minge de fotbal"</string> + <string name="spoken_emoji_26BE" msgid="8959760533076498209">"Minge de baseball"</string> + <string name="spoken_emoji_26C4" msgid="3045791757044255626">"Om de zăpadă fără zăpadă"</string> + <string name="spoken_emoji_26C5" msgid="5580129409712578639">"Soare în spatele unui nor"</string> + <string name="spoken_emoji_26CE" msgid="8963656417276062998">"Ophiuchus"</string> + <string name="spoken_emoji_26D4" msgid="2231451988209604130">"Intrarea interzisă"</string> + <string name="spoken_emoji_26EA" msgid="7513319636103804907">"Biserică"</string> + <string name="spoken_emoji_26F2" msgid="7134115206158891037">"Fântână"</string> + <string name="spoken_emoji_26F3" msgid="4912302210162075465">"Steag în gaură"</string> + <string name="spoken_emoji_26F5" msgid="4766328116769075217">"Barcă cu pânze"</string> + <string name="spoken_emoji_26FA" msgid="5888017494809199037">"Cort"</string> + <string name="spoken_emoji_26FD" msgid="2417060622927453534">"Pompă de combustibil"</string> + <string name="spoken_emoji_2702" msgid="4005741160717451912">"Foarfece negru"</string> + <string name="spoken_emoji_2705" msgid="164605766946697759">"Bifă albă îngroșată"</string> + <string name="spoken_emoji_2708" msgid="7153840886849268988">"Avion"</string> + <string name="spoken_emoji_2709" msgid="2217319160724311369">"Plic"</string> + <string name="spoken_emoji_270A" msgid="508347232762319473">"Pumn ridicat"</string> + <string name="spoken_emoji_270B" msgid="6640562128327753423">"Mână ridicată"</string> + <string name="spoken_emoji_270C" msgid="1344288035704944581">"Mână cu semnul victoriei"</string> + <string name="spoken_emoji_270F" msgid="6108251586067318718">"Creion"</string> + <string name="spoken_emoji_2712" msgid="6320544535087710482">"Peniță neagră"</string> + <string name="spoken_emoji_2714" msgid="1968242800064001654">"Bifă îngroșată"</string> + <string name="spoken_emoji_2716" msgid="511941294762977228">"Înmulțire complexă x"</string> + <string name="spoken_emoji_2728" msgid="5650330815808691881">"Scântei"</string> + <string name="spoken_emoji_2733" msgid="8915809595141157327">"Asterisc cu opt colțuri"</string> + <string name="spoken_emoji_2734" msgid="4846583547980754332">"Stea neagră, cu opt colțuri"</string> + <string name="spoken_emoji_2744" msgid="4350636647760161042">"Fulg de nea"</string> + <string name="spoken_emoji_2747" msgid="3718282973916474455">"Scânteie"</string> + <string name="spoken_emoji_274C" msgid="2752145886733295314">"Semn x"</string> + <string name="spoken_emoji_274E" msgid="4262918689871098338">"Semn x alb pe fond negru în pătrat"</string> + <string name="spoken_emoji_2753" msgid="6935897159942119808">"Semn de întrebare ornamental negru"</string> + <string name="spoken_emoji_2754" msgid="7277504915105532954">"Semn de întrebare ornamental alb"</string> + <string name="spoken_emoji_2755" msgid="6853076969826960210">"Semn de exclamare ornamental alb"</string> + <string name="spoken_emoji_2757" msgid="3707907828776912174">"Semn de exclamare îngroșat"</string> + <string name="spoken_emoji_2764" msgid="4214257843609432167">"Inimă neagră îngroșată"</string> + <string name="spoken_emoji_2795" msgid="6563954833786162168">"Semn plus îngroșat"</string> + <string name="spoken_emoji_2796" msgid="5990926508250772777">"Semn minus îngroșat"</string> + <string name="spoken_emoji_2797" msgid="24694184172879174">"Semn de împărțire îngroșat"</string> + <string name="spoken_emoji_27A1" msgid="3513434778263100580">"Săgeată neagră, spre dreapta"</string> + <string name="spoken_emoji_27B0" msgid="203395646864662198">"Buclă ondulată"</string> + <string name="spoken_emoji_27BF" msgid="4940514642375640510">"Buclă ondulată dublă"</string> + <string name="spoken_emoji_2934" msgid="9062130477982973457">"Săgeată orientată spre dreapta, apoi curbată în sus"</string> + <string name="spoken_emoji_2935" msgid="6198710960720232074">"Săgeată orientată spre dreapta, apoi curbată în jos"</string> + <string name="spoken_emoji_2B05" msgid="4813405635410707690">"Săgeată neagră orientată spre stânga"</string> + <string name="spoken_emoji_2B06" msgid="1223172079106250748">"Săgeată neagră orientată în sus"</string> + <string name="spoken_emoji_2B07" msgid="1599124424746596150">"Săgeată neagră orientată în jos"</string> + <string name="spoken_emoji_2B1B" msgid="3461247311988501626">"Pătrat negru mare"</string> + <string name="spoken_emoji_2B1C" msgid="5793146430145248915">"Pătrat alb mare"</string> + <string name="spoken_emoji_2B50" msgid="3850845519526950524">"Stea albă medie"</string> + <string name="spoken_emoji_2B55" msgid="9137882158811541824">"Cerc îngroșat, mare"</string> + <string name="spoken_emoji_3030" msgid="4609172241893565639">"Liniuță ondulată"</string> + <string name="spoken_emoji_303D" msgid="2545833934975907505">"Semn de modificare"</string> + <string name="spoken_emoji_3297" msgid="928912923628973800">"Ideogramă încercuită - felicitări"</string> + <string name="spoken_emoji_3299" msgid="3930347573693668426">"Ideogramă încercuită - secret"</string> + <string name="spoken_emoji_1F004" msgid="1705216181345894600">"Piesă Mahjong dragon roșu"</string> + <string name="spoken_emoji_1F0CF" msgid="7601493592085987866">"Carte de joc joker negru"</string> + <string name="spoken_emoji_1F170" msgid="3817698686602826773">"Grupa de sânge A"</string> + <string name="spoken_emoji_1F171" msgid="3684218589626650242">"Grupa de sânge B"</string> + <string name="spoken_emoji_1F17E" msgid="2978809190364779029">"Grupa de sânge O"</string> + <string name="spoken_emoji_1F17F" msgid="463634348668462040">"Parcare"</string> + <string name="spoken_emoji_1F18E" msgid="1650705325221496768">"Grupa de sânge AB"</string> + <string name="spoken_emoji_1F191" msgid="5386969264431429221">"„CL” în pătrat"</string> + <string name="spoken_emoji_1F192" msgid="8324226436829162496">"„COOL” în pătrat"</string> + <string name="spoken_emoji_1F193" msgid="4731758603321515364">"„FREE” în pătrat"</string> + <string name="spoken_emoji_1F194" msgid="4903128609556175887">"„ID” în pătrat"</string> + <string name="spoken_emoji_1F195" msgid="1433142500411060924">"„NEW” în pătrat"</string> + <string name="spoken_emoji_1F196" msgid="8825160701159634202">"„N G” în pătrat"</string> + <string name="spoken_emoji_1F197" msgid="7841079241554176535">"„OK” în pătrat"</string> + <string name="spoken_emoji_1F198" msgid="7020298909426960622">"„SOS” în pătrat"</string> + <string name="spoken_emoji_1F199" msgid="5971252667136235630">"„UP” cu semn de exclamare în pătrat"</string> + <string name="spoken_emoji_1F19A" msgid="4557270135899843959">"„VS” în pătrat"</string> + <string name="spoken_emoji_1F201" msgid="7000490044681139002">"„Katakana” în pătrat"</string> + <string name="spoken_emoji_1F202" msgid="8560906958695043947">"„Servicii Katakana” în pătrat"</string> + <string name="spoken_emoji_1F21A" msgid="1496435317324514033">"Caracter ideografic „Gratuit” în pătrat"</string> + <string name="spoken_emoji_1F22F" msgid="609797148862445402">"Caracter ideografic „Loc rezervat” în pătrat"</string> + <string name="spoken_emoji_1F232" msgid="8125716331632035820">"Caracter ideografic „Interzis” în pătrat"</string> + <string name="spoken_emoji_1F233" msgid="8749401090457355028">"Caracter ideografic „Loc liber” în pătrat"</string> + <string name="spoken_emoji_1F234" msgid="3546951604285970768">"Caracter ideografic „Aprobare” în pătrat"</string> + <string name="spoken_emoji_1F235" msgid="5320186982841793711">"Caracter ideografic „Nu sunt locuri libere” în pătrat"</string> + <string name="spoken_emoji_1F236" msgid="879755752069393034">"Caracter ideografic „Cu plată” în pătrat"</string> + <string name="spoken_emoji_1F237" msgid="6741807001205851437">"Caracter ideografic „Lunar” în pătrat"</string> + <string name="spoken_emoji_1F238" msgid="5504414186438196912">"Caracter ideografic „Înscriere” în pătrat"</string> + <string name="spoken_emoji_1F239" msgid="1634067311597618959">"Caracter ideografic „Reducere” în pătrat"</string> + <string name="spoken_emoji_1F23A" msgid="3107862957630169536">"Caracter ideografic „În activitate” în pătrat"</string> + <string name="spoken_emoji_1F250" msgid="6586943922806727907">"Caracter ideografic „Avantaj” în pătrat"</string> + <string name="spoken_emoji_1F251" msgid="9099032855993346948">"Caracter ideografic „Acord” în pătrat"</string> + <string name="spoken_emoji_1F300" msgid="4720098285295840383">"Ciclon"</string> + <string name="spoken_emoji_1F301" msgid="3601962477653752974">"Ceaţă"</string> + <string name="spoken_emoji_1F302" msgid="3404357123421753593">"Umbrelă închisă"</string> + <string name="spoken_emoji_1F303" msgid="3899301321538188206">"Noapte cu stele"</string> + <string name="spoken_emoji_1F304" msgid="2767148930689050040">"Răsărit peste munți"</string> + <string name="spoken_emoji_1F305" msgid="9165812924292061196">"Răsărit"</string> + <string name="spoken_emoji_1F306" msgid="5889294736109193104">"Peisaj urban la asfințit"</string> + <string name="spoken_emoji_1F307" msgid="2714290867291163713">"Apus de soare deasupra clădirilor"</string> + <string name="spoken_emoji_1F308" msgid="688704703985173377">"Curcubeu"</string> + <string name="spoken_emoji_1F309" msgid="6217981957992313528">"Pod noaptea"</string> + <string name="spoken_emoji_1F30A" msgid="4329309263152110893">"Val"</string> + <string name="spoken_emoji_1F30B" msgid="5729430693700923112">"Vulcan"</string> + <string name="spoken_emoji_1F30C" msgid="2961230863217543082">"Calea Lactee"</string> + <string name="spoken_emoji_1F30D" msgid="1113905673331547953">"Globul pământesc: Europa-Africa"</string> + <string name="spoken_emoji_1F30E" msgid="5278512600749223671">"Globul pământesc: continentul american"</string> + <string name="spoken_emoji_1F30F" msgid="5718144880978707493">"Globul pământesc: Asia-Australia"</string> + <string name="spoken_emoji_1F310" msgid="2959618582975247601">"Glob pământesc cu meridiane"</string> + <string name="spoken_emoji_1F311" msgid="623906380914895542">"Simbolul Lună nouă"</string> + <string name="spoken_emoji_1F312" msgid="4458575672576125401">"Simbolul Lună nouă"</string> + <string name="spoken_emoji_1F313" msgid="7599181787989497294">"Simbolul Primul pătrar"</string> + <string name="spoken_emoji_1F314" msgid="4898293184964365413">"Simbolul Înainte de lună plină"</string> + <string name="spoken_emoji_1F315" msgid="3218117051779496309">"Simbolul Lună plină"</string> + <string name="spoken_emoji_1F316" msgid="2061317145777689569">"Simbolul După lună plină"</string> + <string name="spoken_emoji_1F317" msgid="2721090687319539049">"Simbolul Ultimul pătrar"</string> + <string name="spoken_emoji_1F318" msgid="3814091755648887570">"Simbolul Înainte de lună nouă"</string> + <string name="spoken_emoji_1F319" msgid="4074299824890459465">"Semilună"</string> + <string name="spoken_emoji_1F31A" msgid="3092285278116977103">"Lună nouă, cu chip"</string> + <string name="spoken_emoji_1F31B" msgid="2658562138386927881">"Lună în primul pătrar, cu chip"</string> + <string name="spoken_emoji_1F31C" msgid="7914768515547867384">"Lună în ultimul pătrar, cu chip"</string> + <string name="spoken_emoji_1F31D" msgid="1925730459848297182">"Lună plină, cu chip"</string> + <string name="spoken_emoji_1F31E" msgid="8022112382524084418">"Soare cu chip"</string> + <string name="spoken_emoji_1F31F" msgid="1051661214137766369">"Stea strălucitoare"</string> + <string name="spoken_emoji_1F320" msgid="5450591979068216115">"Stea căzătoare"</string> + <string name="spoken_emoji_1F330" msgid="3115760035618051575">"Castană"</string> + <string name="spoken_emoji_1F331" msgid="5658888205290008691">"Puiet"</string> + <string name="spoken_emoji_1F332" msgid="2935650450421165938">"Copac veșnic verde"</string> + <string name="spoken_emoji_1F333" msgid="5898847427062482675">"Copac cu frunze căzătoare"</string> + <string name="spoken_emoji_1F334" msgid="6183375224678417894">"Palmier"</string> + <string name="spoken_emoji_1F335" msgid="5352418412103584941">"Cactus"</string> + <string name="spoken_emoji_1F337" msgid="3839107352363566289">"Lalea"</string> + <string name="spoken_emoji_1F338" msgid="6389970364260468490">"Floare de cireș"</string> + <string name="spoken_emoji_1F339" msgid="9128891447985256151">"Trandafir"</string> + <string name="spoken_emoji_1F33A" msgid="2025828400095233078">"Hibiscus"</string> + <string name="spoken_emoji_1F33B" msgid="8163868254348448552">"Floarea-soarelui"</string> + <string name="spoken_emoji_1F33C" msgid="6850371206262335812">"Floare"</string> + <string name="spoken_emoji_1F33D" msgid="9033484052864509610">"Spic de porumb"</string> + <string name="spoken_emoji_1F33E" msgid="2540173396638444120">"Spic de orez"</string> + <string name="spoken_emoji_1F33F" msgid="4384823344364908558">"Plantă"</string> + <string name="spoken_emoji_1F340" msgid="3494255459156499305">"Trifoi cu patru foi"</string> + <string name="spoken_emoji_1F341" msgid="4581959481754990158">"Frunză de arțar"</string> + <string name="spoken_emoji_1F342" msgid="3119068426871821222">"Frunză căzută"</string> + <string name="spoken_emoji_1F343" msgid="2663317495805149004">"Frunză în vânt"</string> + <string name="spoken_emoji_1F344" msgid="2738517881678722159">"Ciupercă"</string> + <string name="spoken_emoji_1F345" msgid="6135288642349085554">"Roșie"</string> + <string name="spoken_emoji_1F346" msgid="2075395322785406367">"Vânătă"</string> + <string name="spoken_emoji_1F347" msgid="7753453754963890571">"Struguri"</string> + <string name="spoken_emoji_1F348" msgid="1247076837284932788">"Pepene galben"</string> + <string name="spoken_emoji_1F349" msgid="5563054555180611086">"Pepene verde"</string> + <string name="spoken_emoji_1F34A" msgid="4688661208570160524">"Mandarină"</string> + <string name="spoken_emoji_1F34B" msgid="4335318423164185706">"Lămâie"</string> + <string name="spoken_emoji_1F34C" msgid="3712827239858159474">"Banană"</string> + <string name="spoken_emoji_1F34D" msgid="7712521967162622936">"Ananas"</string> + <string name="spoken_emoji_1F34E" msgid="1859466882598614228">"Măr roșu"</string> + <string name="spoken_emoji_1F34F" msgid="8251711032295005633">"Măr verde"</string> + <string name="spoken_emoji_1F350" msgid="625802980159197701">"Pară"</string> + <string name="spoken_emoji_1F351" msgid="4269460120610911895">"Piersic"</string> + <string name="spoken_emoji_1F352" msgid="965600953360182635">"Cireșe"</string> + <string name="spoken_emoji_1F353" msgid="7068623879906925592">"Căpșună"</string> + <string name="spoken_emoji_1F354" msgid="45162285238888494">"Hamburger"</string> + <string name="spoken_emoji_1F355" msgid="9157587635526433283">"Felie de pizza"</string> + <string name="spoken_emoji_1F356" msgid="2667196119149852244">"Carne pe os"</string> + <string name="spoken_emoji_1F357" msgid="8022817413851052256">"Picior de pasăre"</string> + <string name="spoken_emoji_1F358" msgid="3042693264748036476">"Biscuite de orez"</string> + <string name="spoken_emoji_1F359" msgid="3988148661730121958">"Chiftea de orez"</string> + <string name="spoken_emoji_1F35A" msgid="1763824172198327268">"Orez gătit"</string> + <string name="spoken_emoji_1F35B" msgid="62530406745717835">"Orez cu curry"</string> + <string name="spoken_emoji_1F35C" msgid="7537756539198945509">"Castron aburind"</string> + <string name="spoken_emoji_1F35D" msgid="8173523083861875196">"Spaghete"</string> + <string name="spoken_emoji_1F35E" msgid="2935428307894662571">"Pâine"</string> + <string name="spoken_emoji_1F35F" msgid="4840297386785728443">"Cartofi prăjiți"</string> + <string name="spoken_emoji_1F360" msgid="4094659855684686801">"Cartof dulce copt"</string> + <string name="spoken_emoji_1F361" msgid="6475486395784096109">"Dango"</string> + <string name="spoken_emoji_1F362" msgid="5004692577661076275">"Oden"</string> + <string name="spoken_emoji_1F363" msgid="1606603765717743806">"Sushi"</string> + <string name="spoken_emoji_1F364" msgid="6550457766169570811">"Creveți prăjiți"</string> + <string name="spoken_emoji_1F365" msgid="4963815540953316307">"Plăcintă de pește cu model ondulat"</string> + <string name="spoken_emoji_1F366" msgid="7862401745277049404">"Cornet de înghețată"</string> + <string name="spoken_emoji_1F367" msgid="7447972978281980414">"Fulgi de gheață"</string> + <string name="spoken_emoji_1F368" msgid="7790003146142724913">"Înghețată"</string> + <string name="spoken_emoji_1F369" msgid="7383712944084857350">"Gogoașă"</string> + <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_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> + <string name="spoken_emoji_1F371" msgid="6731527040552916358">"Cutie Bento"</string> + <string name="spoken_emoji_1F372" msgid="1635035323832181733">"Oală cu mâncare"</string> + <string name="spoken_emoji_1F373" msgid="7799289534289221045">"Gătit"</string> + <string name="spoken_emoji_1F374" msgid="5973820884987069131">"Furculiță și cuțit"</string> + <string name="spoken_emoji_1F375" msgid="1074832087699617700">"Ceașcă fără toartă"</string> + <string name="spoken_emoji_1F376" msgid="6499274685584852067">"Sticlă și ceașcă de sake"</string> + <string name="spoken_emoji_1F377" msgid="1762398562314172075">"Pahar de vin"</string> + <string name="spoken_emoji_1F378" msgid="5528234560590117516">"Pahar de cocktail"</string> + <string name="spoken_emoji_1F379" msgid="790581290787943325">"Băutură tropicală"</string> + <string name="spoken_emoji_1F37A" msgid="391966822450619516">"Halbă de bere"</string> + <string name="spoken_emoji_1F37B" msgid="9015043286465670662">"Halbe de bere ciocnindu-se"</string> + <string name="spoken_emoji_1F37C" msgid="2532113819464508894">"Biberon"</string> + <string name="spoken_emoji_1F380" msgid="3487363857092458827">"Panglică"</string> + <string name="spoken_emoji_1F381" msgid="614180683680675444">"Cadou ambalat"</string> + <string name="spoken_emoji_1F382" msgid="4720497171946687501">"Tort"</string> + <string name="spoken_emoji_1F383" msgid="3536505941578757623">"Felinar din dovleac"</string> + <string name="spoken_emoji_1F384" msgid="1797870204479059004">"Pom de Crăciun"</string> + <string name="spoken_emoji_1F385" msgid="1754174063483626367">"Moș Crăciun"</string> + <string name="spoken_emoji_1F386" msgid="2130445450758114746">"Focuri de artificii"</string> + <string name="spoken_emoji_1F387" msgid="3403182563117999933">"Artificiu"</string> + <string name="spoken_emoji_1F388" msgid="2903047203723251804">"Balon"</string> + <string name="spoken_emoji_1F389" msgid="2352830665883549388">"Tun de confetti"</string> + <string name="spoken_emoji_1F38A" msgid="6280428984773641322">"Minge de confetti"</string> + <string name="spoken_emoji_1F38B" msgid="4902225837479015489">"Copac Tanabata"</string> + <string name="spoken_emoji_1F38C" msgid="7623268024030989365">"Steaguri încrucișate"</string> + <string name="spoken_emoji_1F38D" msgid="8237542796124408528">"Decorațiune de Crăciun"</string> + <string name="spoken_emoji_1F38E" msgid="5373397476238212371">"Păpuși japoneze"</string> + <string name="spoken_emoji_1F38F" msgid="8754091376829552844">"Steaguri în formă de pește"</string> + <string name="spoken_emoji_1F390" msgid="8903307048095431374">"Clopoței de vânt"</string> + <string name="spoken_emoji_1F391" msgid="2134952069191911841">"Ceremonia contemplării lunii"</string> + <string name="spoken_emoji_1F392" msgid="6380405493914304737">"Ghiozdan"</string> + <string name="spoken_emoji_1F393" msgid="6947890064872470996">"Tocă de absolvire"</string> + <string name="spoken_emoji_1F3A0" msgid="3572095190082826057">"Cal de carusel"</string> + <string name="spoken_emoji_1F3A1" msgid="4300565511681058798">"Roata mare"</string> + <string name="spoken_emoji_1F3A2" msgid="15486093912232140">"Montaigne-russe"</string> + <string name="spoken_emoji_1F3A3" msgid="921739319504942924">"Undiță și pește"</string> + <string name="spoken_emoji_1F3A4" msgid="7497596355346856950">"Microfon"</string> + <string name="spoken_emoji_1F3A5" msgid="4290497821228183002">"Cameră video"</string> + <string name="spoken_emoji_1F3A6" msgid="26019057872319055">"Cinema"</string> + <string name="spoken_emoji_1F3A7" msgid="837856608794094105">"Căști"</string> + <string name="spoken_emoji_1F3A8" msgid="2332260356509244587">"Paletă de pictură"</string> + <string name="spoken_emoji_1F3A9" msgid="9045869366525115256">"Joben"</string> + <string name="spoken_emoji_1F3AA" msgid="5728760354237132">"Cort de circ"</string> + <string name="spoken_emoji_1F3AB" msgid="1657997517193216284">"Bilet"</string> + <string name="spoken_emoji_1F3AC" msgid="4317366554314492152">"Clachetă"</string> + <string name="spoken_emoji_1F3AD" msgid="607157286336130470">"Arta spectacolului"</string> + <string name="spoken_emoji_1F3AE" msgid="2902308174671548150">"Joc video"</string> + <string name="spoken_emoji_1F3AF" msgid="5420539221790296407">"Lovitură drept la țintă"</string> + <string name="spoken_emoji_1F3B0" msgid="7440244806527891956">"Jocuri mecanice"</string> + <string name="spoken_emoji_1F3B1" msgid="545544382391379234">"Biliard"</string> + <string name="spoken_emoji_1F3B2" msgid="8302262034774787493">"Zaruri"</string> + <string name="spoken_emoji_1F3B3" msgid="5180870610771027520">"Popice"</string> + <string name="spoken_emoji_1F3B4" msgid="4723852033266071564">"Cărți de joc cu flori"</string> + <string name="spoken_emoji_1F3B5" msgid="1998470239850548554">"Notă muzicală"</string> + <string name="spoken_emoji_1F3B6" msgid="3827730457113941705">"Mai multe note muzicale"</string> + <string name="spoken_emoji_1F3B7" msgid="5503403099445042180">"Saxofon"</string> + <string name="spoken_emoji_1F3B8" msgid="3985658156795011430">"Chitară"</string> + <string name="spoken_emoji_1F3B9" msgid="5596295757967881451">"Claviatură"</string> + <string name="spoken_emoji_1F3BA" msgid="4284064120340683558">"Trompetă"</string> + <string name="spoken_emoji_1F3BB" msgid="2856598510069988745">"Vioară"</string> + <string name="spoken_emoji_1F3BC" msgid="1608424748821446230">"Partitură"</string> + <string name="spoken_emoji_1F3BD" msgid="5490786111375627777">"Maieu de sport cu dungă"</string> + <string name="spoken_emoji_1F3BE" msgid="1851613105691627931">"Rachetă de tenis și minge"</string> + <string name="spoken_emoji_1F3BF" msgid="6862405997423247921">"Schi și gheată de schi"</string> + <string name="spoken_emoji_1F3C0" msgid="7421420756115104085">"Minge și inel de baschet"</string> + <string name="spoken_emoji_1F3C1" msgid="6926537251677319922">"Steag în carouri"</string> + <string name="spoken_emoji_1F3C2" msgid="5708596929237987082">"Practicant de snowboard"</string> + <string name="spoken_emoji_1F3C3" msgid="5850982999510115824">"Alergător"</string> + <string name="spoken_emoji_1F3C4" msgid="8468355585994639838">"Practicant de surf"</string> + <string name="spoken_emoji_1F3C6" msgid="9094474706847545409">"Trofeu"</string> + <string name="spoken_emoji_1F3C7" msgid="8172206200368370116">"Curse de cai"</string> + <string name="spoken_emoji_1F3C8" msgid="5619171461277597709">"Fotbal american"</string> + <string name="spoken_emoji_1F3C9" msgid="6371294008765871043">"Rugby"</string> + <string name="spoken_emoji_1F3CA" msgid="130977831787806932">"Înnotător"</string> + <string name="spoken_emoji_1F3E0" msgid="6277213201655811842">"Clădire"</string> + <string name="spoken_emoji_1F3E1" msgid="233476176077538885">"Casă cu grădină"</string> + <string name="spoken_emoji_1F3E2" msgid="919736380093964570">"Clădire de birouri"</string> + <string name="spoken_emoji_1F3E3" msgid="6177606081825094184">"Oficiu poștal japonez"</string> + <string name="spoken_emoji_1F3E4" msgid="717377871070970293">"Oficiu poștal european"</string> + <string name="spoken_emoji_1F3E5" msgid="1350532500431776780">"Spital"</string> + <string name="spoken_emoji_1F3E6" msgid="342132788513806214">"Bancă"</string> + <string name="spoken_emoji_1F3E7" msgid="6322352038284944265">"Bancomat"</string> + <string name="spoken_emoji_1F3E8" msgid="5864918444350599907">"Hotel"</string> + <string name="spoken_emoji_1F3E9" msgid="7830416185375326938">"Hoteluri pentru îndrăgostiți"</string> + <string name="spoken_emoji_1F3EA" msgid="5081084413084360479">"Alimentară"</string> + <string name="spoken_emoji_1F3EB" msgid="7010966528205150525">"Şcoală"</string> + <string name="spoken_emoji_1F3EC" msgid="4845978861878295154">"Magazin universal"</string> + <string name="spoken_emoji_1F3ED" msgid="3980316226665215370">"Fabrică"</string> + <string name="spoken_emoji_1F3EE" msgid="1253964276770550248">"Felinar Izakaya"</string> + <string name="spoken_emoji_1F3EF" msgid="1128975573507389883">"Castel japonez"</string> + <string name="spoken_emoji_1F3F0" msgid="1544632297502291578">"Castel european"</string> + <string name="spoken_emoji_1F400" msgid="2063034795679578294">"Șobolan"</string> + <string name="spoken_emoji_1F401" msgid="6736421616217369594">"Șoarece"</string> + <string name="spoken_emoji_1F402" msgid="7276670995895485604">"Bou"</string> + <string name="spoken_emoji_1F403" msgid="8045709541897118928">"Bivol de apă"</string> + <string name="spoken_emoji_1F404" msgid="5240777285676662335">"Vacă"</string> + <string name="spoken_emoji_1F406" msgid="5163461930159540018">"Leopard"</string> + <string name="spoken_emoji_1F407" msgid="6905370221172708160">"Iepure"</string> + <string name="spoken_emoji_1F408" msgid="1362164550508207284">"Pisică"</string> + <string name="spoken_emoji_1F409" msgid="8476130983168866013">"Dragon"</string> + <string name="spoken_emoji_1F40A" msgid="1149626786411545043">"Crocodil"</string> + <string name="spoken_emoji_1F40B" msgid="5199104921208397643">"Balenă"</string> + <string name="spoken_emoji_1F40C" msgid="2704006052881702675">"Melc"</string> + <string name="spoken_emoji_1F40D" msgid="8648186663643157522">"Șarpe"</string> + <string name="spoken_emoji_1F40E" msgid="7219137467573327268">"Cal"</string> + <string name="spoken_emoji_1F40F" msgid="7834336676729040395">"Berbec"</string> + <string name="spoken_emoji_1F410" msgid="8686765722255775031">"Capră"</string> + <string name="spoken_emoji_1F411" msgid="3585715397876383525">"Oaie"</string> + <string name="spoken_emoji_1F412" msgid="4924794582980077838">"Maimuţă"</string> + <string name="spoken_emoji_1F413" msgid="1460475310405677377">"Cocoș"</string> + <string name="spoken_emoji_1F414" msgid="5857296282631892219">"Pui"</string> + <string name="spoken_emoji_1F415" msgid="5920041074892949527">"Câine"</string> + <string name="spoken_emoji_1F416" msgid="4362403392912540286">"Porc"</string> + <string name="spoken_emoji_1F417" msgid="6836978415840795128">"Porc mistreț"</string> + <string name="spoken_emoji_1F418" msgid="7926161463897783691">"Elefant"</string> + <string name="spoken_emoji_1F419" msgid="1055233959755784186">"Caracatiță"</string> + <string name="spoken_emoji_1F41A" msgid="5195666556511558060">"Scoică spiralată"</string> + <string name="spoken_emoji_1F41B" msgid="7652480167465557832">"Insectă"</string> + <string name="spoken_emoji_1F41C" msgid="1123461148697574239">"Furnică"</string> + <string name="spoken_emoji_1F41D" msgid="718579308764058851">"Albină"</string> + <string name="spoken_emoji_1F41E" msgid="6766305509608115467">"Buburuză"</string> + <string name="spoken_emoji_1F41F" msgid="1207261298343160838">"Pești"</string> + <string name="spoken_emoji_1F420" msgid="1041145003133609221">"Pești tropicali"</string> + <string name="spoken_emoji_1F421" msgid="1748378324417438751">"Pește lună"</string> + <string name="spoken_emoji_1F422" msgid="4106724877523329148">"Broască țestoasă"</string> + <string name="spoken_emoji_1F423" msgid="4077407945958691907">"Pui ieșind din ou"</string> + <string name="spoken_emoji_1F424" msgid="6911326019270172283">"Pui de găină"</string> + <string name="spoken_emoji_1F425" msgid="5466514196557885577">"Pui văzut din față"</string> + <string name="spoken_emoji_1F426" msgid="2163979138772892755">"Pasăre"</string> + <string name="spoken_emoji_1F427" msgid="3585670324511212961">"Pinguin"</string> + <string name="spoken_emoji_1F428" msgid="7955440808647898579">"Urs koala"</string> + <string name="spoken_emoji_1F429" msgid="5028269352809819035">"Pudel"</string> + <string name="spoken_emoji_1F42A" msgid="4681926706404032484">"Dromader"</string> + <string name="spoken_emoji_1F42B" msgid="2725166074981558322">"Cămilă cu două cocoașe"</string> + <string name="spoken_emoji_1F42C" msgid="6764791873413727085">"Delfin"</string> + <string name="spoken_emoji_1F42D" msgid="1033643138546864251">"Cap de șoarece"</string> + <string name="spoken_emoji_1F42E" msgid="8099223337120508820">"Cap de vacă"</string> + <string name="spoken_emoji_1F42F" msgid="2104743989330781572">"Cap de tigru"</string> + <string name="spoken_emoji_1F430" msgid="525492897063150160">"Cap de iepure"</string> + <string name="spoken_emoji_1F431" msgid="6051358666235016851">"Cap de pisică"</string> + <string name="spoken_emoji_1F432" msgid="7698001871193018305">"Cap de dragon"</string> + <string name="spoken_emoji_1F433" msgid="3762356053512899326">"Balenă cenușie"</string> + <string name="spoken_emoji_1F434" msgid="3619943222159943226">"Cap de cal"</string> + <string name="spoken_emoji_1F435" msgid="59199202683252958">"Cap de maimuță"</string> + <string name="spoken_emoji_1F436" msgid="340544719369009828">"Cap de câine"</string> + <string name="spoken_emoji_1F437" msgid="1219818379784982585">"Cap de porc"</string> + <string name="spoken_emoji_1F438" msgid="9128124743321008210">"Cap de broască"</string> + <string name="spoken_emoji_1F439" msgid="1424161319554642266">"Cap de hamster"</string> + <string name="spoken_emoji_1F43A" msgid="6727645488430385584">"Cap de lup"</string> + <string name="spoken_emoji_1F43B" msgid="5397170068392865167">"Cap de urs"</string> + <string name="spoken_emoji_1F43C" msgid="2715995734367032431">"Cap de urs panda"</string> + <string name="spoken_emoji_1F43D" msgid="6005480717951776597">"Rât de porc"</string> + <string name="spoken_emoji_1F43E" msgid="8917626103219080547">"Urme de labe"</string> + <string name="spoken_emoji_1F440" msgid="7144338258163384433">"Ochi"</string> + <string name="spoken_emoji_1F442" msgid="1905515392292676124">"Ureche"</string> + <string name="spoken_emoji_1F443" msgid="1491504447758933115">"Nas"</string> + <string name="spoken_emoji_1F444" msgid="3654613047946080332">"Gură"</string> + <string name="spoken_emoji_1F445" msgid="7024905244040509204">"Limbă scoasă"</string> + <string name="spoken_emoji_1F446" msgid="2150365643636471745">"Deget arătător alb, orientat în sus"</string> + <string name="spoken_emoji_1F447" msgid="8794022344940891388">"Deget arătător alb, orientat în jos"</string> + <string name="spoken_emoji_1F448" msgid="3261812959215550650">"Deget arătător alb, orientat la stânga"</string> + <string name="spoken_emoji_1F449" msgid="4764447975177805991">"Deget arătător alb, orientat la dreapta"</string> + <string name="spoken_emoji_1F44A" msgid="7197417095486424841">"Semn pumn strâns"</string> + <string name="spoken_emoji_1F44B" msgid="1975968945250833117">"Semn palmă deschisă"</string> + <string name="spoken_emoji_1F44C" msgid="3185919567897876562">"Semn OK"</string> + <string name="spoken_emoji_1F44D" msgid="6182553970602667815">"Semn de aprobare"</string> + <string name="spoken_emoji_1F44E" msgid="8030851867365111809">"Semn de dezaprobare"</string> + <string name="spoken_emoji_1F44F" msgid="5148753662268213389">"Semn Aplauze"</string> + <string name="spoken_emoji_1F450" msgid="1012021072085157054">"Semn Palme deschise"</string> + <string name="spoken_emoji_1F451" msgid="8257466714629051320">"Coroană"</string> + <string name="spoken_emoji_1F452" msgid="4567394011149905466">"Pălărie de damă"</string> + <string name="spoken_emoji_1F453" msgid="5978410551173163010">"Ochelari"</string> + <string name="spoken_emoji_1F454" msgid="348469036193323252">"Cravată"</string> + <string name="spoken_emoji_1F455" msgid="5665118831861433578">"Tricou"</string> + <string name="spoken_emoji_1F456" msgid="1890991330923356408">"Blugi"</string> + <string name="spoken_emoji_1F457" msgid="3904310482655702620">"Rochie"</string> + <string name="spoken_emoji_1F458" msgid="5704243858031107692">"Kimono"</string> + <string name="spoken_emoji_1F459" msgid="3553148747050035251">"Bikini"</string> + <string name="spoken_emoji_1F45A" msgid="1389654639484716101">"Haine de damă"</string> + <string name="spoken_emoji_1F45B" msgid="1113293170254222904">"Poșetă"</string> + <string name="spoken_emoji_1F45C" msgid="3410257778598006936">"Geantă"</string> + <string name="spoken_emoji_1F45D" msgid="812176504300064819">"Borsetă"</string> + <string name="spoken_emoji_1F45E" msgid="2901741399934723562">"Pantofi bărbătești"</string> + <string name="spoken_emoji_1F45F" msgid="6828566359287798863">"Încălțăminte sport"</string> + <string name="spoken_emoji_1F460" msgid="305863879170420855">"Pantof cu toc înalt"</string> + <string name="spoken_emoji_1F461" msgid="5160493217831417630">"Sandale de damă"</string> + <string name="spoken_emoji_1F462" msgid="1722897795554863734">"Cizme de damă"</string> + <string name="spoken_emoji_1F463" msgid="5850772903593010699">"Urme de pași"</string> + <string name="spoken_emoji_1F464" msgid="1228335905487734913">"Bust siluetă"</string> + <string name="spoken_emoji_1F465" msgid="4461307702499679879">"Busturi siluetă"</string> + <string name="spoken_emoji_1F466" msgid="1938873085514108889">"Băiat"</string> + <string name="spoken_emoji_1F467" msgid="8237080594860144998">"Fată"</string> + <string name="spoken_emoji_1F468" msgid="6081300722526675382">"Bărbat"</string> + <string name="spoken_emoji_1F469" msgid="1090140923076108158">"Femeie"</string> + <string name="spoken_emoji_1F46A" msgid="5063570981942606595">"Familie"</string> + <string name="spoken_emoji_1F46B" msgid="6795882374287327952">"Bărbat și femeie ținându-se de mână"</string> + <string name="spoken_emoji_1F46C" msgid="6844464165783964495">"Doi bărbați ținându-se de mână"</string> + <string name="spoken_emoji_1F46D" msgid="2316773068014053180">"Două femei ținându-se de mână"</string> + <string name="spoken_emoji_1F46E" msgid="5897625605860822401">"Polițist"</string> + <string name="spoken_emoji_1F46F" msgid="7716871657717641490">"Femeie cu urechi de iepure"</string> + <string name="spoken_emoji_1F470" msgid="6409995400510338892">"Mireasă cu voal"</string> + <string name="spoken_emoji_1F471" msgid="3058247860441670806">"Persoană cu păr blond"</string> + <string name="spoken_emoji_1F472" msgid="3928854667819339142">"Bărbat cu gua pi mao"</string> + <string name="spoken_emoji_1F473" msgid="5921952095808988381">"Bărbat cu turban"</string> + <string name="spoken_emoji_1F474" msgid="1082237499496725183">"Bătrân"</string> + <string name="spoken_emoji_1F475" msgid="7280323988642212761">"Bătrână"</string> + <string name="spoken_emoji_1F476" msgid="4713322657821088296">"Bebeluș"</string> + <string name="spoken_emoji_1F477" msgid="2197036131029221370">"Muncitor în construcții"</string> + <string name="spoken_emoji_1F478" msgid="7245521193493488875">"Prinţesă"</string> + <string name="spoken_emoji_1F479" msgid="6876475321015553972">"Căpcăun japonez"</string> + <string name="spoken_emoji_1F47A" msgid="3900813633102703571">"Goblin japonez"</string> + <string name="spoken_emoji_1F47B" msgid="2608250873194079390">"Fantomă"</string> + <string name="spoken_emoji_1F47C" msgid="3838699131276537421">"Îngeraș"</string> + <string name="spoken_emoji_1F47D" msgid="2874077455888369538">"Extraterestru"</string> + <string name="spoken_emoji_1F47E" msgid="3642607168625579507">"Monstru extraterestru"</string> + <string name="spoken_emoji_1F47F" msgid="441605977269926252">"Demon"</string> + <string name="spoken_emoji_1F480" msgid="3696253485164878739">"Craniu"</string> + <string name="spoken_emoji_1F481" msgid="320408708521966893">"Persoană la ghișeul de informații"</string> + <string name="spoken_emoji_1F482" msgid="3424354860245608949">"Paznic"</string> + <string name="spoken_emoji_1F483" msgid="3221113594843849083">"Dansator"</string> + <string name="spoken_emoji_1F484" msgid="7348014979080444885">"Ruj"</string> + <string name="spoken_emoji_1F485" msgid="6133507975565116339">"Lac de unghii"</string> + <string name="spoken_emoji_1F486" msgid="9085459968247394155">"Masaj facial"</string> + <string name="spoken_emoji_1F487" msgid="1479113637259592150">"Tunsoare"</string> + <string name="spoken_emoji_1F488" msgid="6922559285234100252">"Simbol pentru bărbier"</string> + <string name="spoken_emoji_1F489" msgid="8114863680950147305">"Seringă"</string> + <string name="spoken_emoji_1F48A" msgid="8526843630145963032">"Pilulă"</string> + <string name="spoken_emoji_1F48B" msgid="2538528967897640292">"Semn de sărut"</string> + <string name="spoken_emoji_1F48C" msgid="1681173271652890232">"Scrisoare de dragoste"</string> + <string name="spoken_emoji_1F48D" msgid="8259886164999042373">"Inel"</string> + <string name="spoken_emoji_1F48E" msgid="8777981696011111101">"Piatră prețioasă"</string> + <string name="spoken_emoji_1F48F" msgid="741593675183677907">"Sărut"</string> + <string name="spoken_emoji_1F490" msgid="4482549128959806736">"Buchet"</string> + <string name="spoken_emoji_1F491" msgid="2305245307882441500">"Cuplu cu inimă"</string> + <string name="spoken_emoji_1F492" msgid="3884119934804475732">"Nuntă"</string> + <string name="spoken_emoji_1F493" msgid="1208828371565525121">"Inimă bătând"</string> + <string name="spoken_emoji_1F494" msgid="6198876398509338718">"Inimă zdrobită"</string> + <string name="spoken_emoji_1F495" msgid="9206202744967130919">"Două inimi"</string> + <string name="spoken_emoji_1F496" msgid="5436953041732207775">"Inimă strălucitoare"</string> + <string name="spoken_emoji_1F497" msgid="7285142863951448473">"Inimă care crește"</string> + <string name="spoken_emoji_1F498" msgid="7940131245037575715">"Inimă cu sageată"</string> + <string name="spoken_emoji_1F499" msgid="4453235040265550009">"Inimă albastră"</string> + <string name="spoken_emoji_1F49A" msgid="6262178648366971405">"Inimă verde"</string> + <string name="spoken_emoji_1F49B" msgid="8085384999750714368">"Inimă galbenă"</string> + <string name="spoken_emoji_1F49C" msgid="453829540120898698">"Inimă violet"</string> + <string name="spoken_emoji_1F49D" msgid="3460534750224161888">"Inimăcu panglică"</string> + <string name="spoken_emoji_1F49E" msgid="4490636226072523867">"Inimi care se rotesc"</string> + <string name="spoken_emoji_1F49F" msgid="2059319756421226336">"Ornament inimă"</string> + <string name="spoken_emoji_1F4A0" msgid="1954850380550212038">"Formă romboidală cu un punct înăuntru"</string> + <string name="spoken_emoji_1F4A1" msgid="403137413540909021">"Bec electric"</string> + <string name="spoken_emoji_1F4A2" msgid="2604192053295622063">"Simbol pentru furie"</string> + <string name="spoken_emoji_1F4A3" msgid="6378351742957821735">"Bombă"</string> + <string name="spoken_emoji_1F4A4" msgid="7217736258870346625">"Simbol pentru somn"</string> + <string name="spoken_emoji_1F4A5" msgid="5401995723541239858">"Simbol pentru accident"</string> + <string name="spoken_emoji_1F4A6" msgid="3837802182716483848">"Simbol stropi de transpirație"</string> + <string name="spoken_emoji_1F4A7" msgid="5718438987757885141">"Picătură"</string> + <string name="spoken_emoji_1F4A8" msgid="4472108229720006377">"Simbol liniuță"</string> + <string name="spoken_emoji_1F4A9" msgid="1240958472788430032">"Excremente"</string> + <string name="spoken_emoji_1F4AA" msgid="8427525538635146416">"Biceps flexat"</string> + <string name="spoken_emoji_1F4AB" msgid="5484114759939427459">"Simbol pentru amețeală"</string> + <string name="spoken_emoji_1F4AC" msgid="5571196638219612682">"Balon de dialog"</string> + <string name="spoken_emoji_1F4AD" msgid="353174619257798652">"Balon de gândire"</string> + <string name="spoken_emoji_1F4AE" msgid="1223142786927162641">"Floare albă"</string> + <string name="spoken_emoji_1F4AF" msgid="3526278354452138397">"Simbol pentru o sută de puncte"</string> + <string name="spoken_emoji_1F4B0" msgid="4124102195175124156">"Sac cu bani"</string> + <string name="spoken_emoji_1F4B1" msgid="8339494003418572905">"Schimb valutar"</string> + <string name="spoken_emoji_1F4B2" msgid="3179159430187243132">"Semn dolar îngroșat"</string> + <string name="spoken_emoji_1F4B3" msgid="5375412518221759596">"Card de credit"</string> + <string name="spoken_emoji_1F4B4" msgid="1068592463669453204">"Bancnotă cu simbolul yen"</string> + <string name="spoken_emoji_1F4B5" msgid="1426708699891832564">"Bancnotă cu simbolul dolar"</string> + <string name="spoken_emoji_1F4B6" msgid="8289249930736444837">"Bancnotă cu simbolul euro"</string> + <string name="spoken_emoji_1F4B7" msgid="5245100496860739429">"Bancnotă cu simbolul liră"</string> + <string name="spoken_emoji_1F4B8" msgid="4401099580477164440">"Bani cu aripi"</string> + <string name="spoken_emoji_1F4B9" msgid="647509393536679903">"Diagramă reprezentând o tendință crescătoare și simbolul yen"</string> + <string name="spoken_emoji_1F4BA" msgid="1269737854891046321">"Loc"</string> + <string name="spoken_emoji_1F4BB" msgid="6252883563347816451">"Computer"</string> + <string name="spoken_emoji_1F4BC" msgid="6182597732218446206">"Servietă"</string> + <string name="spoken_emoji_1F4BD" msgid="5820961044768829176">"Minidisc"</string> + <string name="spoken_emoji_1F4BE" msgid="4754542485835379808">"Dischetă"</string> + <string name="spoken_emoji_1F4BF" msgid="2237481756984721795">"Disc optic"</string> + <string name="spoken_emoji_1F4C0" msgid="491582501089694461">"DVD"</string> + <string name="spoken_emoji_1F4C1" msgid="6645461382494158111">"Dosar cu fișiere"</string> + <string name="spoken_emoji_1F4C2" msgid="8095638715523765338">"Dosar deschis"</string> + <string name="spoken_emoji_1F4C3" msgid="3727274466173970142">"Pagină îndoită"</string> + <string name="spoken_emoji_1F4C4" msgid="4382570710795501612">"Pagină orientată în sus"</string> + <string name="spoken_emoji_1F4C5" msgid="8693944622627762487">"Calendar"</string> + <string name="spoken_emoji_1F4C6" msgid="8469908708708424640">"Calendar cu file detașabile"</string> + <string name="spoken_emoji_1F4C7" msgid="2665313547987324495">"Index cărți de vizită"</string> + <string name="spoken_emoji_1F4C8" msgid="8007686702282833600">"Diagramă reprezentând o tendință crescătoare"</string> + <string name="spoken_emoji_1F4C9" msgid="2271951411192893684">"Diagramă reprezentând o tendință descrescătoare"</string> + <string name="spoken_emoji_1F4CA" msgid="3525692829622381444">"Diagramă cu bare"</string> + <string name="spoken_emoji_1F4CB" msgid="977639227554095521">"Clipboard"</string> + <string name="spoken_emoji_1F4CC" msgid="156107396088741574">"Piuneză"</string> + <string name="spoken_emoji_1F4CD" msgid="4266572175361190231">"Piuneză rotundă"</string> + <string name="spoken_emoji_1F4CE" msgid="6294288509864968290">"Agrafă de birou"</string> + <string name="spoken_emoji_1F4CF" msgid="149679400831136810">"Riglă dreaptă"</string> + <string name="spoken_emoji_1F4D0" msgid="8130339336619202915">"Riglă triunghiulară"</string> + <string name="spoken_emoji_1F4D1" msgid="5852176364856284968">"File de marcaj"</string> + <string name="spoken_emoji_1F4D2" msgid="2276810154105920052">"Registru"</string> + <string name="spoken_emoji_1F4D3" msgid="5873386492793610808">"Blocnotes"</string> + <string name="spoken_emoji_1F4D4" msgid="4754469936418776360">"Blocnotes cu copertă decorativă"</string> + <string name="spoken_emoji_1F4D5" msgid="4642713351802778905">"Carte închisă"</string> + <string name="spoken_emoji_1F4D6" msgid="6987347918381807186">"Carte deschisă"</string> + <string name="spoken_emoji_1F4D7" msgid="7813394163241379223">"Carte verde"</string> + <string name="spoken_emoji_1F4D8" msgid="7189799718984979521">"Carte albastră"</string> + <string name="spoken_emoji_1F4D9" msgid="3874664073186440225">"Carte portocalie"</string> + <string name="spoken_emoji_1F4DA" msgid="872212072924287762">"Cărți"</string> + <string name="spoken_emoji_1F4DB" msgid="2015183603583392969">"Insignă cu numele"</string> + <string name="spoken_emoji_1F4DC" msgid="5075845110932456783">"Sul"</string> + <string name="spoken_emoji_1F4DD" msgid="2494006707147586786">"Notă"</string> + <string name="spoken_emoji_1F4DE" msgid="7883008605002117671">"Receptor de telefon"</string> + <string name="spoken_emoji_1F4DF" msgid="3538610110623780465">"Pager"</string> + <string name="spoken_emoji_1F4E0" msgid="2960778342609543077">"Fax"</string> + <string name="spoken_emoji_1F4E1" msgid="6269733703719242108">"Antenă satelit"</string> + <string name="spoken_emoji_1F4E2" msgid="1987535386302883116">"Megafon"</string> + <string name="spoken_emoji_1F4E3" msgid="5588916572878599224">"Megafon"</string> + <string name="spoken_emoji_1F4E4" msgid="2063561529097749707">"Tavă de ieșiri"</string> + <string name="spoken_emoji_1F4E5" msgid="3232462702926143576">"Tavă de intrări"</string> + <string name="spoken_emoji_1F4E6" msgid="3399454337197561635">"Pachet"</string> + <string name="spoken_emoji_1F4E7" msgid="5557136988503873238">"Simbolul E-mail"</string> + <string name="spoken_emoji_1F4E8" msgid="30698793974124123">"Plic primit"</string> + <string name="spoken_emoji_1F4E9" msgid="5947550337678643166">"Plic cu săgeată în jos deasupra"</string> + <string name="spoken_emoji_1F4EA" msgid="772614045207213751">"Cutie poștală închisă cu steag coborât"</string> + <string name="spoken_emoji_1F4EB" msgid="6491414165464146137">"Cutie poștală închisă cu steag ridicat"</string> + <string name="spoken_emoji_1F4EC" msgid="7369517138779988438">"Cutie poștală deschisă cu steag ridicat"</string> + <string name="spoken_emoji_1F4ED" msgid="5657520436285454241">"Cutie poștală deschisă cu steag coborât"</string> + <string name="spoken_emoji_1F4EE" msgid="8464138906243608614">"Cutie poștală"</string> + <string name="spoken_emoji_1F4EF" msgid="8801427577198798226">"Simbolul poștal"</string> + <string name="spoken_emoji_1F4F0" msgid="6330208624731662525">"Ziar"</string> + <string name="spoken_emoji_1F4F1" msgid="3966503935581675695">"Telefon mobil"</string> + <string name="spoken_emoji_1F4F2" msgid="1057540341746100087">"Telefon mobil cu săgeată spre dreapta la stânga"</string> + <string name="spoken_emoji_1F4F3" msgid="5003984447315754658">"Modul vibrații"</string> + <string name="spoken_emoji_1F4F4" msgid="5549847566968306253">"Telefon mobil închis"</string> + <string name="spoken_emoji_1F4F5" msgid="3660199448671699238">"Telefoanele mobile interzise"</string> + <string name="spoken_emoji_1F4F6" msgid="2676974903233268860">"Antenă cu bare"</string> + <string name="spoken_emoji_1F4F7" msgid="2643891943105989039">"Cameră foto"</string> + <string name="spoken_emoji_1F4F9" msgid="4475626303058218048">"Cameră video"</string> + <string name="spoken_emoji_1F4FA" msgid="1079796186652960775">"Emisiuni TV"</string> + <string name="spoken_emoji_1F4FB" msgid="3848729587403760645">"Radio"</string> + <string name="spoken_emoji_1F4FC" msgid="8370432508874310054">"Casetă video"</string> + <string name="spoken_emoji_1F500" msgid="2389947994502144547">"Săgeți răsucite spre dreapta"</string> + <string name="spoken_emoji_1F501" msgid="2132188352433347009">"Săgeți în formă de cerc deschis, orientate spre dreapta și spre stânga, în sensul acelor de ceasornic"</string> + <string name="spoken_emoji_1F502" msgid="2361976580513178391">"Săgeți în formă de cerc deschis, orientate spre dreapta și spre stânga, în sensul acelor de ceasornic, cu suprapunerea cifrei 1 într-un cerc"</string> + <string name="spoken_emoji_1F503" msgid="8936283551917858793">"Săgeți în formă de cerc deschis, orientate în sus și în jos, în sens orar"</string> + <string name="spoken_emoji_1F504" msgid="708290317843535943">"Săgeți în formă de cerc deschis, orientate în sus și în jos, în sens antiorar"</string> + <string name="spoken_emoji_1F505" msgid="6348909939004951860">"Simbol de luminozitate scăzută"</string> + <string name="spoken_emoji_1F506" msgid="4449609297521280173">"Simbol de luminozitate ridicată"</string> + <string name="spoken_emoji_1F507" msgid="7136386694923708448">"Difuzor cu simbol de anulare"</string> + <string name="spoken_emoji_1F508" msgid="5063567689831527865">"Difuzor"</string> + <string name="spoken_emoji_1F509" msgid="3948050077992370791">"Difuzor cu un val de sunet"</string> + <string name="spoken_emoji_1F50A" msgid="5818194948677277197">"Difuzor cu trei valuri de sunet"</string> + <string name="spoken_emoji_1F50B" msgid="8083470451266295876">"Baterie"</string> + <string name="spoken_emoji_1F50C" msgid="7793219132036431680">"Alimentare electrică"</string> + <string name="spoken_emoji_1F50D" msgid="8140244710637926780">"Lupă orientată spre stânga"</string> + <string name="spoken_emoji_1F50E" msgid="4751821352839693365">"Lupă orientată spre dreapta"</string> + <string name="spoken_emoji_1F50F" msgid="915079280472199605">"Lacăt cu stilou"</string> + <string name="spoken_emoji_1F510" msgid="7658381761691758318">"Lacăt închis cu cheie"</string> + <string name="spoken_emoji_1F511" msgid="262319867774655688">"Cheie"</string> + <string name="spoken_emoji_1F512" msgid="5628688337255115175">"Lacăt"</string> + <string name="spoken_emoji_1F513" msgid="8579201846619420981">"Lacăt deschis"</string> + <string name="spoken_emoji_1F514" msgid="7027268683047322521">"Clopoțel"</string> + <string name="spoken_emoji_1F515" msgid="8903179856036069242">"Clopoțel cu simbol de anulare"</string> + <string name="spoken_emoji_1F516" msgid="108097933937925381">"Marcaj"</string> + <string name="spoken_emoji_1F517" msgid="2450846665734313397">"Simbol lanț"</string> + <string name="spoken_emoji_1F518" msgid="7028220286841437832">"Buton radio"</string> + <string name="spoken_emoji_1F519" msgid="8211189165075445687">"„BACK” cu săgeată spre stânga deasupra"</string> + <string name="spoken_emoji_1F51A" msgid="823966751787338892">"„END” cu săgeată spre stânga deasupra"</string> + <string name="spoken_emoji_1F51B" msgid="5920570742107943382">"„ON” cu semn de exclamare și cu săgeată stânga-dreapta deasupra"</string> + <string name="spoken_emoji_1F51C" msgid="110609810659826676">"„SOON” cu săgeată spre dreapta deasupra"</string> + <string name="spoken_emoji_1F51D" msgid="4087697222026095447">"„TOP” cu săgeată în sus deasupra"</string> + <string name="spoken_emoji_1F51E" msgid="8512873526157201775">"Simbolul „Interzis sub optsprezece ani”"</string> + <string name="spoken_emoji_1F51F" msgid="8673370823728653973">"Tastă zece"</string> + <string name="spoken_emoji_1F520" msgid="7335109890337048900">"Simbol de introducere a majusculelor latine"</string> + <string name="spoken_emoji_1F521" msgid="2693185864450925778">"Simbol de introducere a literelor mici latine"</string> + <string name="spoken_emoji_1F522" msgid="8419130286280673347">"Simbol de introducere a cifrelor"</string> + <string name="spoken_emoji_1F523" msgid="3318053476401719421">"Simbol de introducere a simbolurilor"</string> + <string name="spoken_emoji_1F524" msgid="1625073997522316331">"Simbol de introducere a literelor latine"</string> + <string name="spoken_emoji_1F525" msgid="4083884189172963790">"Foc"</string> + <string name="spoken_emoji_1F526" msgid="2035494936742643580">"Lanternă"</string> + <string name="spoken_emoji_1F527" msgid="134257142354034271">"Cheie"</string> + <string name="spoken_emoji_1F528" msgid="700627429570609375">"Ciocan"</string> + <string name="spoken_emoji_1F529" msgid="7480548235904988573">"Piuliță și șurub"</string> + <string name="spoken_emoji_1F52A" msgid="7613580031502317893">"Hocho"</string> + <string name="spoken_emoji_1F52B" msgid="4554906608328118613">"Pistol"</string> + <string name="spoken_emoji_1F52C" msgid="1330294501371770790">"Microscop"</string> + <string name="spoken_emoji_1F52D" msgid="7549551775445177140">"Telescop"</string> + <string name="spoken_emoji_1F52E" msgid="4457099417872625141">"Glob de cristal"</string> + <string name="spoken_emoji_1F52F" msgid="8899031001317442792">"Stea cu șase colțuri cu punct în mijloc"</string> + <string name="spoken_emoji_1F530" msgid="3572898444281774023">"Simbolul japonez pentru începători"</string> + <string name="spoken_emoji_1F531" msgid="5225633376450025396">"Emblemă trident"</string> + <string name="spoken_emoji_1F532" msgid="9169568490485180779">"Buton negru pătrat"</string> + <string name="spoken_emoji_1F533" msgid="6554193837201918598">"Buton alb pătrat"</string> + <string name="spoken_emoji_1F534" msgid="8339298801331865340">"Cerc mare roșu"</string> + <string name="spoken_emoji_1F535" msgid="1227403104835533512">"Cerc mare albastru"</string> + <string name="spoken_emoji_1F536" msgid="5477372445510469331">"Romb mare portocaliu"</string> + <string name="spoken_emoji_1F537" msgid="3158915214347274626">"Romb mare albastru"</string> + <string name="spoken_emoji_1F538" msgid="4300084249474451991">"Romb mic portocaliu"</string> + <string name="spoken_emoji_1F539" msgid="6535159756325742275">"Romb mic albastru"</string> + <string name="spoken_emoji_1F53A" msgid="3728196273988781389">"Triunghi roșu, îndreptat în sus"</string> + <string name="spoken_emoji_1F53B" msgid="7182097039614128707">"Triunghi roșu, îndreptat în jos"</string> + <string name="spoken_emoji_1F53C" msgid="4077022046319615029">"Triunghi mic roșu, îndreptat în sus"</string> + <string name="spoken_emoji_1F53D" msgid="3939112784894620713">"Triunghi mic roșu, îndreptat în jos"</string> + <string name="spoken_emoji_1F550" msgid="7761392621689986218">"Ceas cu ora unu"</string> + <string name="spoken_emoji_1F551" msgid="2699448504113431716">"Ceas cu ora două"</string> + <string name="spoken_emoji_1F552" msgid="5872107867411853750">"Ceas cu ora trei"</string> + <string name="spoken_emoji_1F553" msgid="8490966286158640743">"Ceas cu ora patru"</string> + <string name="spoken_emoji_1F554" msgid="7662585417832909280">"Ceas cu ora cinci"</string> + <string name="spoken_emoji_1F555" msgid="5564698204520412009">"Ceas cu ora șase"</string> + <string name="spoken_emoji_1F556" msgid="7325712194836512205">"Ceas cu ora șapte"</string> + <string name="spoken_emoji_1F557" msgid="4398343183682848693">"Ceas cu ora opt"</string> + <string name="spoken_emoji_1F558" msgid="3110507820404018172">"Ceas cu ora nouă"</string> + <string name="spoken_emoji_1F559" msgid="2972160366448337839">"Ceas cu ora zece"</string> + <string name="spoken_emoji_1F55A" msgid="5568112876681714834">"Ceas cu ora unsprezece"</string> + <string name="spoken_emoji_1F55B" msgid="6731739890330659276">"Ceas cu ora douăsprezece"</string> + <string name="spoken_emoji_1F55C" msgid="7838853679879115890">"Ceas cu ora unu și jumătate"</string> + <string name="spoken_emoji_1F55D" msgid="3518832144255922544">"Ceas cu ora două și jumătate"</string> + <string name="spoken_emoji_1F55E" msgid="3092760695634993002">"Ceas cu ora trei și jumătate"</string> + <string name="spoken_emoji_1F55F" msgid="2326720311892906763">"Ceas cu ora patru și jumătate"</string> + <string name="spoken_emoji_1F560" msgid="5771339179963924448">"Ceas cu ora cinci și jumătate"</string> + <string name="spoken_emoji_1F561" msgid="3139944777062475382">"Ceas cu ora șase și jumătate"</string> + <string name="spoken_emoji_1F562" msgid="8273944611162457084">"Ceas cu ora șapte și jumătate"</string> + <string name="spoken_emoji_1F563" msgid="8643976903718136299">"Ceas cu ora opt și jumătate"</string> + <string name="spoken_emoji_1F564" msgid="3511070239796141638">"Ceas cu ora nouă și jumătate"</string> + <string name="spoken_emoji_1F565" msgid="4567451985272963088">"Ceas cu ora zece și jumătate"</string> + <string name="spoken_emoji_1F566" msgid="2790552288169929810">"Ceas cu ora unsprezece și jumătate"</string> + <string name="spoken_emoji_1F567" msgid="9026037362100689337">"Ceas cu ora douăsprezece și jumătate"</string> + <string name="spoken_emoji_1F5FB" msgid="9037503671676124015">"Muntele Fuji"</string> + <string name="spoken_emoji_1F5FC" msgid="1409415995817242150">"Turnul Tokyo"</string> + <string name="spoken_emoji_1F5FD" msgid="2562726956654429582">"Statuia Libertății"</string> + <string name="spoken_emoji_1F5FE" msgid="1184469756905210580">"Conturul Japoniei"</string> + <string name="spoken_emoji_1F5FF" msgid="6003594799354942297">"Moyai"</string> + <string name="spoken_emoji_1F600" msgid="7601109464776835283">"Față rânjind"</string> + <string name="spoken_emoji_1F601" msgid="746026523967444503">"Față rânjind cu ochi zâmbitori"</string> + <string name="spoken_emoji_1F602" msgid="8354558091785198246">"Față cu lacrimi de bucurie"</string> + <string name="spoken_emoji_1F603" msgid="3861022912544159823">"Față zâmbitoare cu gura deschisă"</string> + <string name="spoken_emoji_1F604" msgid="5119021072966343531">"Față zâmbitoare cu gura deschisă și ochi zâmbitori"</string> + <string name="spoken_emoji_1F605" msgid="6140813923973561735">"Față zâmbitoare, cu gura deschisă și sudoare rece"</string> + <string name="spoken_emoji_1F606" msgid="3549936813966832799">"Față zâmbitoare cu gura deschisă și ochii închiși"</string> + <string name="spoken_emoji_1F607" msgid="2826424078212384817">"Față zâmbitoare cu aură"</string> + <string name="spoken_emoji_1F608" msgid="7343559595089811640">"Față zâmbitoare cu coarne"</string> + <string name="spoken_emoji_1F609" msgid="5481030187207504405">"Față care face cu ochiul"</string> + <string name="spoken_emoji_1F60A" msgid="5023337769148679767">"Față zâmbitoare, cu ochii zâmbitori"</string> + <string name="spoken_emoji_1F60B" msgid="3005248217216195694">"Față savurând mâncare delicioasă"</string> + <string name="spoken_emoji_1F60C" msgid="349384012958268496">"Față ușurată"</string> + <string name="spoken_emoji_1F60D" msgid="7921853137164938391">"Față zâmbitoare, cu ochii în formă de inimă"</string> + <string name="spoken_emoji_1F60E" msgid="441718886380605643">"Față zâmbitoare cu ochelari de soare"</string> + <string name="spoken_emoji_1F60F" msgid="2674453144890180538">"Față zâmbind șiret"</string> + <string name="spoken_emoji_1F610" msgid="3225675825334102369">"Față neutră"</string> + <string name="spoken_emoji_1F611" msgid="7199179827619679668">"Față fără expresie"</string> + <string name="spoken_emoji_1F612" msgid="985081329745137998">"Față serioasă"</string> + <string name="spoken_emoji_1F613" msgid="5548607684830303562">"Față cu sudoare rece"</string> + <string name="spoken_emoji_1F614" msgid="3196305665259916390">"Față meditativă"</string> + <string name="spoken_emoji_1F615" msgid="3051674239303969101">"Față confuză"</string> + <string name="spoken_emoji_1F616" msgid="8124887056243813089">"Față confuză"</string> + <string name="spoken_emoji_1F617" msgid="7052733625511122870">"Față care sărută"</string> + <string name="spoken_emoji_1F618" msgid="408207170572303753">"Față care trimite bezele"</string> + <string name="spoken_emoji_1F619" msgid="8645430335143153645">"Față care sărută cu ochi zâmbitori"</string> + <string name="spoken_emoji_1F61A" msgid="2882157190974340247">"Față care sărută cu ochii închiși"</string> + <string name="spoken_emoji_1F61B" msgid="3765927202787211499">"Față care scoate limba"</string> + <string name="spoken_emoji_1F61C" msgid="198943912107589389">"Față care scoate limba și face cu ochiul"</string> + <string name="spoken_emoji_1F61D" msgid="7643546385877816182">"Față care scoate limba, cu ochii închiși"</string> + <string name="spoken_emoji_1F61E" msgid="1528732952202098364">"Față dezamăgită"</string> + <string name="spoken_emoji_1F61F" msgid="1853664164636082404">"Față îngrijorată"</string> + <string name="spoken_emoji_1F620" msgid="6051942001307375830">"Față furioasă"</string> + <string name="spoken_emoji_1F621" msgid="2114711878097257704">"Față bosumflată"</string> + <string name="spoken_emoji_1F622" msgid="29291014645931822">"Față care plânge"</string> + <string name="spoken_emoji_1F623" msgid="7803959833595184773">"Față perseverentă"</string> + <string name="spoken_emoji_1F624" msgid="8637637647725752799">"Față cu privire triumfătoare"</string> + <string name="spoken_emoji_1F625" msgid="6153625183493635030">"Față dezamăgită dar ușurată"</string> + <string name="spoken_emoji_1F626" msgid="6179485689935562950">"Față încruntată cu gura deschisă"</string> + <string name="spoken_emoji_1F627" msgid="8566204052903012809">"Față chinuită"</string> + <string name="spoken_emoji_1F628" msgid="8875777401624904224">"Față înfricoșată"</string> + <string name="spoken_emoji_1F629" msgid="1411538490319190118">"Față obosită"</string> + <string name="spoken_emoji_1F62A" msgid="4726686726690289969">"Față somnoroasă"</string> + <string name="spoken_emoji_1F62B" msgid="3221980473921623613">"Față obosită"</string> + <string name="spoken_emoji_1F62C" msgid="4616356691941225182">"Față cu grimasă"</string> + <string name="spoken_emoji_1F62D" msgid="4283677508698812232">"Față plângând în hohote"</string> + <string name="spoken_emoji_1F62E" msgid="726083405284353894">"Față cu gura deschisă"</string> + <string name="spoken_emoji_1F62F" msgid="7746620088234710962">"Față redusă la tăcere"</string> + <string name="spoken_emoji_1F630" msgid="3298804852155581163">"Față cu gura deschisă și sudoare rece"</string> + <string name="spoken_emoji_1F631" msgid="1603391150954646779">"Față țipând de frică"</string> + <string name="spoken_emoji_1F632" msgid="4846193232203976013">"Față uimită"</string> + <string name="spoken_emoji_1F633" msgid="4023593836629700443">"Față congestionată"</string> + <string name="spoken_emoji_1F634" msgid="3155265083246248129">"Față care doarme"</string> + <string name="spoken_emoji_1F635" msgid="4616691133452764482">"Față amețită"</string> + <string name="spoken_emoji_1F636" msgid="947000211822375683">"Față fără gura"</string> + <string name="spoken_emoji_1F637" msgid="1269551267347165774">"Față cu mască medicală"</string> + <string name="spoken_emoji_1F638" msgid="3410766467496872301">"Față de pisică rânjind, cu ochi zâmbitori"</string> + <string name="spoken_emoji_1F639" msgid="1833417519781022031">"Față de pisică cu lacrimi de bucurie"</string> + <string name="spoken_emoji_1F63A" msgid="8566294484007152613">"Față zâmbitoare de pisică, cu gura deschisă"</string> + <string name="spoken_emoji_1F63B" msgid="74417995938927571">"Față zâmbitoare pisică, cu ochii în formă de inimă"</string> + <string name="spoken_emoji_1F63C" msgid="6472812005729468870">"Față de pisică cu zâmbet crispat"</string> + <string name="spoken_emoji_1F63D" msgid="1638398369553349509">"Față de pisică sărutând cu ochii închiși"</string> + <string name="spoken_emoji_1F63E" msgid="6788969063020278986">"Față de pisică bosumflată"</string> + <string name="spoken_emoji_1F63F" msgid="1207234562459550185">"Față de pisică plângând"</string> + <string name="spoken_emoji_1F640" msgid="6023054549904329638">"Față de pisică obosită"</string> + <string name="spoken_emoji_1F645" msgid="5202090629227587076">"Față cu gest „nu are niciun rost”"</string> + <string name="spoken_emoji_1F646" msgid="6734425134415138134">"Față cu gest „OK”"</string> + <string name="spoken_emoji_1F647" msgid="1090285518444205483">"Persoană care face plecăciuni"</string> + <string name="spoken_emoji_1F648" msgid="8978535230610522356">"Maimuță care-și acoperă ochii"</string> + <string name="spoken_emoji_1F649" msgid="8486145279809495102">"Maimuță care-și acoperă urechile"</string> + <string name="spoken_emoji_1F64A" msgid="1237524974033228660">"Maimuță care-și acoperă gura"</string> + <string name="spoken_emoji_1F64B" msgid="4251150782016370475">"Persoană fericită, ridicând o mână"</string> + <string name="spoken_emoji_1F64C" msgid="5446231430684558344">"Persoană fericită ridicând ambele mâini de bucurie"</string> + <string name="spoken_emoji_1F64D" msgid="4646485595309482342">"Persoană încruntată"</string> + <string name="spoken_emoji_1F64E" msgid="3376579939836656097">"Persoană cu fața bosumflată"</string> + <string name="spoken_emoji_1F64F" msgid="1044439574356230711">"Persoană cu mâinile împreunate"</string> + <string name="spoken_emoji_1F680" msgid="513263736012689059">"Rachetă"</string> + <string name="spoken_emoji_1F681" msgid="9201341783850525339">"Elicopter"</string> + <string name="spoken_emoji_1F682" msgid="8046933583867498698">"Locomotivă cu abur"</string> + <string name="spoken_emoji_1F683" msgid="8772750354339223092">"Vagon de tren"</string> + <string name="spoken_emoji_1F684" msgid="346396777356203608">"Tren de mare viteză"</string> + <string name="spoken_emoji_1F685" msgid="1237059817190832730">"Tren de mare viteză cu vârf ascuțit"</string> + <string name="spoken_emoji_1F686" msgid="3525197227223620343">"Tren"</string> + <string name="spoken_emoji_1F687" msgid="5110143437960392837">"Metrou"</string> + <string name="spoken_emoji_1F688" msgid="4702085029871797965">"Metrou ușor"</string> + <string name="spoken_emoji_1F689" msgid="2375851019798817094">"Stație"</string> + <string name="spoken_emoji_1F68A" msgid="6368370859718717198">"Tramvai"</string> + <string name="spoken_emoji_1F68B" msgid="2920160427117436633">"Vagon de tramvai"</string> + <string name="spoken_emoji_1F68C" msgid="1061520934758810864">"Autobuz"</string> + <string name="spoken_emoji_1F68D" msgid="2890059031360969304">"Autobuz sosind din față"</string> + <string name="spoken_emoji_1F68E" msgid="6234042976027309654">"Troleibuz"</string> + <string name="spoken_emoji_1F68F" msgid="5871099334672012107">"Stație de autobuz"</string> + <string name="spoken_emoji_1F690" msgid="8080964620200195262">"Maxi-taxi"</string> + <string name="spoken_emoji_1F691" msgid="999173032408730501">"Ambulanță"</string> + <string name="spoken_emoji_1F692" msgid="1712863785341849487">"Mașină de pompieri"</string> + <string name="spoken_emoji_1F693" msgid="7987109037389768934">"Mașină de poliție"</string> + <string name="spoken_emoji_1F694" msgid="6061658916653884608">"Mașină de poliție sosind din față"</string> + <string name="spoken_emoji_1F695" msgid="6913445460364247283">"Taxi"</string> + <string name="spoken_emoji_1F696" msgid="6391604457418285404">"Taxi sosind din față"</string> + <string name="spoken_emoji_1F697" msgid="7978399334396733790">"Automobil"</string> + <string name="spoken_emoji_1F698" msgid="7006050861129732018">"Automobil sosind din față"</string> + <string name="spoken_emoji_1F699" msgid="630317052666590607">"Vehicul de agrement"</string> + <string name="spoken_emoji_1F69A" msgid="4739797891735823577">"Camion pentru livrări"</string> + <string name="spoken_emoji_1F69B" msgid="4715997280786620649">"Camion cu remorcă"</string> + <string name="spoken_emoji_1F69C" msgid="5557395610750818161">"Tractor"</string> + <string name="spoken_emoji_1F69D" msgid="5467164189942951047">"Monoșină"</string> + <string name="spoken_emoji_1F69E" msgid="169238196389832234">"Cale ferată montană"</string> + <string name="spoken_emoji_1F69F" msgid="7508128757012845102">"Cale ferată suspendată"</string> + <string name="spoken_emoji_1F6A0" msgid="8733056213790160147">"Teleferic montan"</string> + <string name="spoken_emoji_1F6A1" msgid="4666516337749347253">"Tramvai aerian"</string> + <string name="spoken_emoji_1F6A2" msgid="4511220588943129583">"Vapor"</string> + <string name="spoken_emoji_1F6A3" msgid="8412962252222205387">"Barcă cu vâsle"</string> + <string name="spoken_emoji_1F6A4" msgid="8867571300266339211">"Barcă cu motor"</string> + <string name="spoken_emoji_1F6A5" msgid="7650260812741963884">"Semafor orizontal"</string> + <string name="spoken_emoji_1F6A6" msgid="485575967773793454">"Semafor vertical"</string> + <string name="spoken_emoji_1F6A7" msgid="6411048933816976794">"Semn de construcție"</string> + <string name="spoken_emoji_1F6A8" msgid="6345717218374788364">"Sirena de la mașina de poliție"</string> + <string name="spoken_emoji_1F6A9" msgid="6586380356807600412">"Stâlp cu steag triunghiular"</string> + <string name="spoken_emoji_1F6AA" msgid="8954448167261738885">"Ușă"</string> + <string name="spoken_emoji_1F6AB" msgid="5313946262888343544">"Semn Intrarea oprită"</string> + <string name="spoken_emoji_1F6AC" msgid="6946858177965948288">"Simbol Fumatul permis"</string> + <string name="spoken_emoji_1F6AD" msgid="6320088669185507241">"Simbol Fumatul interzis"</string> + <string name="spoken_emoji_1F6AE" msgid="1062469925352817189">"Simbol Aruncați gunoiul în locuri amenajate"</string> + <string name="spoken_emoji_1F6AF" msgid="2286668056123642208">"Simbol Păstrați curățenia"</string> + <string name="spoken_emoji_1F6B0" msgid="179424763882990952">"Simbol Apă potabilă"</string> + <string name="spoken_emoji_1F6B1" msgid="5585212805429161877">"Simbol Apă care nu este potabilă"</string> + <string name="spoken_emoji_1F6B2" msgid="1771885082068421875">"Bicicletă"</string> + <string name="spoken_emoji_1F6B3" msgid="8033779581263314408">"Interzis accesul cu bicicleta"</string> + <string name="spoken_emoji_1F6B4" msgid="1999538449018476947">"Biciclist"</string> + <string name="spoken_emoji_1F6B5" msgid="340846352660993117">"Biciclist pe trasee montane"</string> + <string name="spoken_emoji_1F6B6" msgid="4351024386495098336">"Pieton"</string> + <string name="spoken_emoji_1F6B7" msgid="4564800655866838802">"Accesul pietonilor interzis"</string> + <string name="spoken_emoji_1F6B8" msgid="3020531906940267349">"Copii traversând"</string> + <string name="spoken_emoji_1F6B9" msgid="1207095844125041251">"Simbol Bărbați"</string> + <string name="spoken_emoji_1F6BA" msgid="2346879310071017531">"Simbol Femei"</string> + <string name="spoken_emoji_1F6BB" msgid="2370172469642078526">"Toaletă"</string> + <string name="spoken_emoji_1F6BC" msgid="5558827593563530851">"Simbol Bebeluș"</string> + <string name="spoken_emoji_1F6BD" msgid="9213590243049835957">"Toaletă"</string> + <string name="spoken_emoji_1F6BE" msgid="394016533781742491">"WC"</string> + <string name="spoken_emoji_1F6BF" msgid="906336365928291207">"Duș"</string> + <string name="spoken_emoji_1F6C0" msgid="4592099854378821599">"Baie"</string> + <string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Cadă"</string> + <string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Control pașapoarte"</string> + <string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Vamă"</string> + <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Revendicarea bagajului"</string> + <string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Bagaj în custodie"</string> +</resources> diff --git a/java/res/values-ro/strings-talkback-descriptions.xml b/java/res/values-ro/strings-talkback-descriptions.xml index 2b428eb0b..6e5db4d02 100644 --- a/java/res/values-ro/strings-talkback-descriptions.xml +++ b/java/res/values-ro/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nu a fost introdus text"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> corectează <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> cu <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> efectuează corectare automată"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Tasta cu codul %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Tasta Shift este activată (apăsați pentru a o dezactiva)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Tasta Caps Lock este activată (apăsați pentru a o dezactiva)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Mai multe simboluri"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboluri"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Ștergeți"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simboluri"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Litere"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Înapoi"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Tasta Shift a fost activată"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Tasta Caps Lock este activată"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Tasta Shift a fost dezactivată"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Modul Simboluri"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Modul Mai multe simboluri"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Modul Alfanumeric"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Modul Telefon"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Modul Telefon cu simboluri"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Locații"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboluri"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emoticonuri"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 51d3c5901..8ab56bc7a 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Verificatorul ortografic utilizează intrări din lista de contacte"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sugeraţi nume din Agendă"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Sugestii personalizate"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Inserează punct spațiu"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubla atingere a barei de spațiu inserează punct urmat de spațiu"</string> <string name="auto_cap" msgid="1719746674854628252">"Scriere automată cu majuscule"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Limbi de introducere de text"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Atingeţi din nou pentru a salva"</string> <string name="has_dictionary" msgid="6071847973466625007">"Dicţionar disponibil"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Activaţi feedback de la utilizatori"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Ajutați la îmbunătățirea acestui instrument de editare a metodelor de introducere a textului trimițând în mod automat statistici de utilizare și rapoarte de blocare."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Temă pentru tastatură"</string> <string name="subtype_en_GB" msgid="88170601942311355">"engleză (Regatul Unit)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"engleză (S.U.A.)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Activaţi"</string> <string name="not_now" msgid="6172462888202790482">"Nu acum"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Acelaşi stil de introducere există deja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modul Studiu privind utilizarea"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Timpul apăsării lungi a tastei"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrare după apăsarea tastei"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Sunet la apăsarea tastelor"</string> diff --git a/java/res/values-ru/strings-action-keys.xml b/java/res/values-ru/strings-action-keys.xml index d5080ceb5..3edf2285c 100644 --- a/java/res/values-ru/strings-action-keys.xml +++ b/java/res/values-ru/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Назад"</string> <string name="label_done_key" msgid="7564866296502630852">"Готово"</string> <string name="label_send_key" msgid="482252074224462163">"Отправить"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Пауза"</string> <string name="label_wait_key" msgid="5891247853595466039">"Пауза"</string> </resources> diff --git a/java/res/values-ru/strings-talkback-descriptions.xml b/java/res/values-ru/strings-talkback-descriptions.xml index 7e64f11b0..f73ceab4e 100644 --- a/java/res/values-ru/strings-talkback-descriptions.xml +++ b/java/res/values-ru/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Текст не введен."</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"При нажатии клавиши <xliff:g id="KEY_NAME">%1$s</xliff:g> слово <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> будет исправлено на <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>."</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Клавиша <xliff:g id="KEY_NAME">%1$s</xliff:g> выполняет автоисправление."</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Код клавиши %d."</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Клавиша верхнего регистра."</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Верхний регистр включен. Нажмите, чтобы отключить."</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock включен. Нажмите, чтобы отключить."</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Дополнительные символы."</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Клавиша верхнего регистра."</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Символы."</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Клавиша верхнего регистра."</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Удалить."</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Символы."</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Буквы."</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Назад."</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Верхний регистр включен."</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock включен."</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Верхний регистр отключен."</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Режим добавления символов."</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Режим дополнительных символов."</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Режим ввода текста."</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Режим набора номера."</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Режим телефонных символов."</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Места."</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Символы."</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Смайлики."</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index bef248385..61c414c4b 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Обращаться к списку контактов при проверке правописания"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Подсказывать имена"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказывать исправления на основе имен из списка контактов"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Пользовательские словари"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Ставить точки автоматически"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Вводить точку с пробелом двойным нажатием кнопки \"Пробел\"."</string> <string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Языки ввода"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Нажмите, чтобы сохранить"</string> <string name="has_dictionary" msgid="6071847973466625007">"Доступен словарь"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Включить отправку сведений"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Помогите усовершенствовать редактор способа ввода, разрешив отправку статистики и отчетов о сбоях"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавиатуры"</string> <string name="subtype_en_GB" msgid="88170601942311355">"английский (Великобритания)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"английский (США)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Включить"</string> <string name="not_now" msgid="6172462888202790482">"Не сейчас"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такой стиль ввода уже существует: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим проверки удобства использования"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Долгое нажатие"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вибросигнал при нажатии клавиш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Звук при нажатии клавиш"</string> diff --git a/java/res/values-sk/strings-action-keys.xml b/java/res/values-sk/strings-action-keys.xml index 3586fb140..ffc5be2d6 100644 --- a/java/res/values-sk/strings-action-keys.xml +++ b/java/res/values-sk/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Pred."</string> <string name="label_done_key" msgid="7564866296502630852">"OK"</string> <string name="label_send_key" msgid="482252074224462163">"Posl."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pauza"</string> <string name="label_wait_key" msgid="5891247853595466039">"Čakať"</string> </resources> diff --git a/java/res/values-sk/strings-talkback-descriptions.xml b/java/res/values-sk/strings-talkback-descriptions.xml index 605aceb38..9fde012b3 100644 --- a/java/res/values-sk/strings-talkback-descriptions.xml +++ b/java/res/values-sk/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Nie je zadaný žiadny text"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Klávesom <xliff:g id="KEY_NAME">%1$s</xliff:g> opravíte slovo <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Klávesom <xliff:g id="KEY_NAME">%1$s</xliff:g> spustíte automatické opravy"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Kód klávesa %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Kláves Shift je zapnutý (zakážete ho klepnutím)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Kláves Caps Lock je zapnutý (zakážete ho klepnutím)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Ďalšie symboly"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboly"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Odstrániť"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboly"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Písmená"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Naspäť"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Kláves Shift je povolený"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Kláves Caps Lock je povolený"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Kláves Shift je zakázaný"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Režim symbolov"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Režim ďalších symbolov"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Režim písmen"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Režim telefónu"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Režim telefónnych symbolov"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Miesta"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboly"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Emotikony"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index 9a3564782..1d7a3dc0e 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používa záznamy z vášho zoznamu kontaktov"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Navrhnúť mená kontaktov"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Prispôsobené návrhy"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Bodka s medzerou"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dvojitým klepnutím na medzerník vložíte bodku a medzeru."</string> <string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Jazyky vstupu"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Opätovným dotykom uložíte"</string> <string name="has_dictionary" msgid="6071847973466625007">"K dispozícii je slovník"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Povoliť spätnú väzbu od používateľov"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Automatickým zasielaním štatistík o využívaní editora metódy vstupu a správ o jeho zlyhaní môžete prispieť k vylepšeniu tohto nástroja"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Motív klávesnice"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Anglická klávesnica (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Anglická klávesnica (US)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Povoliť"</string> <string name="not_now" msgid="6172462888202790482">"Teraz nie"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Rovnaký štýl vstupu už existuje: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Režim štúdie použiteľnosti"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Oneskor. pri stlač. a podržaní"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trvanie vibrov. pri stlač. kl."</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Hlasitosť pri stlačení klávesu"</string> diff --git a/java/res/values-sl/strings-action-keys.xml b/java/res/values-sl/strings-action-keys.xml index 0235887af..f8b764723 100644 --- a/java/res/values-sl/strings-action-keys.xml +++ b/java/res/values-sl/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Prej."</string> <string name="label_done_key" msgid="7564866296502630852">"Konec"</string> <string name="label_send_key" msgid="482252074224462163">"Pošl."</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Zaus."</string> <string name="label_wait_key" msgid="5891247853595466039">"Čakaj"</string> </resources> diff --git a/java/res/values-sl/strings-talkback-descriptions.xml b/java/res/values-sl/strings-talkback-descriptions.xml index 280393a70..27b8d3d6c 100644 --- a/java/res/values-sl/strings-talkback-descriptions.xml +++ b/java/res/values-sl/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Ni vnesenega besedila"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Tipka <xliff:g id="KEY_NAME">%1$s</xliff:g> popravi <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> v <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Tipka <xliff:g id="KEY_NAME">%1$s</xliff:g> izvede samopopravek"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Koda tipke %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Tipka Shift je vklopljena (dotaknite se, da jo onemogočite)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Tipka Caps lock je vklopljena (dotaknite se, da jo onemogočite)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Več simbolov"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simboli"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Izbriši"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simboli"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Črke"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Nazaj"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Način »Shift« je omogočen"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Funkcija »Caps Lock« je omogočena"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Način »Shift« je onemogočen"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Način simbolov"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Način za več simbolov"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Način črk"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Način telefona"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Način simbolov telefona"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Mesta"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simboli"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Čustveni simboli"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index 2594aaec4..85d40f4c9 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Črkovalnik uporablja vnose s seznama stikov"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Predlagaj imena stikov"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Prilagojeni predlogi"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dva presl. za vnos pike"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Z dvojnim dotikom preslednice vstavite piko in za njo presledek"</string> <string name="auto_cap" msgid="1719746674854628252">"Samod. velike začetnice"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Jeziki vnosa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Dotaknite se še enkrat, da shranite"</string> <string name="has_dictionary" msgid="6071847973466625007">"Slovar je na voljo"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Omogoči povratne informacije uporabnikov"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"S samodejnim pošiljanjem statističnih podatkov o uporabi in poročil o zrušitvah nam pomagate izboljšati urejevalnik načina vnosa."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema tipkovnice"</string> <string name="subtype_en_GB" msgid="88170601942311355">"angleščina (Združeno kraljestvo)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"angleščina (ZDA)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Omogoči"</string> <string name="not_now" msgid="6172462888202790482">"Ne zdaj"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isti slog vnosa že obstaja: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Način za preučevanje uporabnosti"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Zakasn. za dolg pritisk tipke"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Trajanje vibr. ob prit. tipke"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Glasn. zvoka ob pritisku tipke"</string> diff --git a/java/res/values-sr/strings-action-keys.xml b/java/res/values-sr/strings-action-keys.xml index 1ce0ed7fb..b3d9411c7 100644 --- a/java/res/values-sr/strings-action-keys.xml +++ b/java/res/values-sr/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Претх"</string> <string name="label_done_key" msgid="7564866296502630852">"Готов"</string> <string name="label_send_key" msgid="482252074224462163">"Шаљи"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Пауза"</string> <string name="label_wait_key" msgid="5891247853595466039">"Чекај"</string> </resources> diff --git a/java/res/values-sr/strings-talkback-descriptions.xml b/java/res/values-sr/strings-talkback-descriptions.xml index 402d45b91..d47258177 100644 --- a/java/res/values-sr/strings-talkback-descriptions.xml +++ b/java/res/values-sr/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Текст није унет"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> исправља <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> у <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> обавља аутоматско исправљање"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Кôд тастера %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift је укључен (додирните да бисте га онемогућили)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock је укључен (додирните да бисте га онемогућили)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Још симбола"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Симболи"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Избриши"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Симболи"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Слова"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Следеће"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift је омогућен"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock је омогућен"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift је онемогућен"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Режим симбола"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Режим Још симбола"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Режим слова"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Режим телефона"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Режим симбола телефона"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Места"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Симболи"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Емотикони"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index 7bd7f67e5..1862e8539 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Контролор правописа користи уносе са листе контаката"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Предложи имена контаката"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Персонализовани предлози"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Тачка и размак"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Двоструким додиром размака умеће се тачка праћена размаком"</string> <string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Језици уноса"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Поново додирните да бисте сачували"</string> <string name="has_dictionary" msgid="6071847973466625007">"Речник је доступан"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Омогући повратну информацију корисника"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Помозите нам да побољшамо овај уређивач метода уноса тако што ћете аутоматски слати статистику коришћења и извештаје о отказивању"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема тастатуре"</string> <string name="subtype_en_GB" msgid="88170601942311355">"енглески (УК)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"енглески (САД)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Омогући"</string> <string name="not_now" msgid="6172462888202790482">"Не сада"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Исти стил уноса већ постоји: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим за студију могућности коришћења"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Одлагање при дугом притиску"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Трајање вибрације при притиску"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Јачина звука при притиску"</string> diff --git a/java/res/values-sv/strings-action-keys.xml b/java/res/values-sv/strings-action-keys.xml index e13860811..053461d07 100644 --- a/java/res/values-sv/strings-action-keys.xml +++ b/java/res/values-sv/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Föreg"</string> <string name="label_done_key" msgid="7564866296502630852">"Klart"</string> <string name="label_send_key" msgid="482252074224462163">"Sänd"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pausa"</string> <string name="label_wait_key" msgid="5891247853595466039">"Vänta"</string> </resources> diff --git a/java/res/values-sv/strings-talkback-descriptions.xml b/java/res/values-sv/strings-talkback-descriptions.xml index 140202d99..9824b5a7c 100644 --- a/java/res/values-sv/strings-talkback-descriptions.xml +++ b/java/res/values-sv/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Ingen text har angetts"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Om du trycker på <xliff:g id="KEY_NAME">%1$s</xliff:g> rättas <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> till <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Om du trycker på <xliff:g id="KEY_NAME">%1$s</xliff:g> utförs autokorrigering"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Nyckelkod %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Skift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Skift är aktiverat (tryck för att inaktivera)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock på (tryck för att inaktivera)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Fler symboler"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Skift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Symboler"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Skift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Ta bort"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Symboler"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Bokstäver"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Föregående"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Skift aktiverat"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock är aktiverat"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Skift är inaktiverat"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbolläge"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Läge med fler symboler"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Bokstavsläge"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefonläge"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefonsymbolläge"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Platser"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Symboler"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Uttryckssymboler"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index 7092e6876..606140466 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"I stavningskontrollen används poster från kontaktlistan"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Föreslå kontaktnamn"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Anpassade förslag"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dubbelt blanksteg = punkt"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Dubbelt blanksteg ger en punkt följt av mellanslag"</string> <string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Inmatningsspråk"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Spara genom att trycka igen"</string> <string name="has_dictionary" msgid="6071847973466625007">"En ordlista är tillgänglig"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Aktivera synpunkter från användare"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Du kan hjälpa till att förbättra inmatningsmetoden genom att automatiskt skicka användningsstatistik och felrapporter"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tangentbordstema"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Engelskt (brittiskt)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Engelskt (amerikanskt)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Aktivera"</string> <string name="not_now" msgid="6172462888202790482">"Inte nu"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Samma indatastil finns redan: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Läge för studie av användbarhet"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Fördröjning vid långt tryck"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrationslängd för tangenter"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ljudvolym för tangenter"</string> diff --git a/java/res/values-sw/strings-action-keys.xml b/java/res/values-sw/strings-action-keys.xml index 7d8822e80..587cbfa6a 100644 --- a/java/res/values-sw/strings-action-keys.xml +++ b/java/res/values-sw/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Nyuma"</string> <string name="label_done_key" msgid="7564866296502630852">"Imekamilika"</string> <string name="label_send_key" msgid="482252074224462163">"Tuma"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Sitisha"</string> <string name="label_wait_key" msgid="5891247853595466039">"Subiri"</string> </resources> diff --git a/java/res/values-sw/strings-talkback-descriptions.xml b/java/res/values-sw/strings-talkback-descriptions.xml index e9ca282b9..e00ce6a9b 100644 --- a/java/res/values-sw/strings-talkback-descriptions.xml +++ b/java/res/values-sw/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Hakuna maandishi yaliyoingizwa"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> kuwa <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> hufanya marekebisho otomatiki"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Msimbo wa kitufe %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift imewashwa (gonga ili kuizima)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock imewashwa (gonga ili kuizima)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Alama zaidi"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Alama"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Futa"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Alama"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Herufi"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Iililotangulia"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift imewashwa"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps lock imewashwa"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift imezimwa"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Hali ya alama"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Hali ya alama zaidi"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Hali ya herufi"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Hali ya simu"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Hali ya alama za simu"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Maeneo"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Alama"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Vikaragosi"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 56312899d..2d130e0bf 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya unaowasiliana nao"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kikagua tahajia hutumia majina yaliyoingizwa katika orodha yako ya anwani"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Pendekeza majini ya Anwani"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Tumia majina kutoka kwa Anwani kwa mapendekezo na marekebisho"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Mapendekezo yaliyobadilishwa kukufaa"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Kitone baada ya nafasi mbili"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Kugonga mara mbili kwenye upau nafasi kunaingiza kitone kikifuatiwa na nafasi"</string> <string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Lugha zinazoruhusiwa"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Gusa tena ili kuhifadhi"</string> <string name="has_dictionary" msgid="6071847973466625007">"Kamusi inapatikana"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Wezesha maoni ya watumiaji"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Saidia kuimarisha mbinu hii ya uingizaji wa kihariri, kwa kutuma takwimu za matumizi na ripoti za kuvurugika kiotomatiki"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Maandhari ya kibodi"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Kiingereza cha (Uingereza)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Kiingereza cha (Marekani)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Washa"</string> <string name="not_now" msgid="6172462888202790482">"Sio sasa"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Mfumo sawa wa maingizo tayari upo: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modi ya uchunguzi wa utumizi"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ubofyaji kitufe kunakochelewa"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Bonyeza kitufe cha muda wa kutetema"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Bonyeza kitufe cha kiwango cha sauti"</string> diff --git a/java/res/values-sw600dp-land/config.xml b/java/res/values-sw600dp-land/config.xml index 00edde161..ba8b52f0b 100644 --- a/java/res/values-sw600dp-land/config.xml +++ b/java/res/values-sw600dp-land/config.xml @@ -48,7 +48,9 @@ <fraction name="config_key_letter_ratio_5row">62%</fraction> <fraction name="config_key_shifted_letter_hint_ratio_5row">36%</fraction> - <dimen name="config_suggestions_strip_horizontal_padding">252.0dp</dimen> + <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">180.0dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">54dp</dimen> <integer name="config_max_more_suggestions_row">5</integer> <fraction name="config_min_more_suggestions_width">50%</fraction> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index 3bd843928..d97538de0 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -63,12 +63,13 @@ <fraction name="config_key_shifted_letter_hint_ratio_5row">27%</fraction> <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">54dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">54dp</dimen> <dimen name="config_more_suggestions_row_height">44dp</dimen> <integer name="config_max_more_suggestions_row">6</integer> <fraction name="config_min_more_suggestions_width">90%</fraction> - <dimen name="config_suggestions_strip_horizontal_padding">94.5dp</dimen> <dimen name="config_suggestion_min_width">48.0dp</dimen> - <dimen name="config_suggestion_text_horizontal_padding">12dp</dimen> + <dimen name="config_suggestion_text_horizontal_padding">10dp</dimen> <dimen name="config_suggestion_text_size">22dp</dimen> <dimen name="config_more_suggestions_hint_text_size">33dp</dimen> diff --git a/java/res/values-sw768dp-land/config.xml b/java/res/values-sw768dp-land/config.xml index 3878a9e84..63f86ba81 100644 --- a/java/res/values-sw768dp-land/config.xml +++ b/java/res/values-sw768dp-land/config.xml @@ -49,7 +49,9 @@ <fraction name="config_key_letter_ratio_5row">53%</fraction> <fraction name="config_key_shifted_letter_hint_ratio_5row">30%</fraction> - <dimen name="config_suggestions_strip_horizontal_padding">252.0dp</dimen> + <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">340dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">54dp</dimen> <fraction name="config_min_more_suggestions_width">50%</fraction> <!-- Gesture floating preview text parameters --> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index 34eec38b8..94b38d850 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -61,12 +61,13 @@ <fraction name="config_key_shifted_letter_hint_ratio_5row">33%</fraction> <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">100dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">54dp</dimen> <dimen name="config_more_suggestions_row_height">44dp</dimen> <integer name="config_max_more_suggestions_row">6</integer> <fraction name="config_min_more_suggestions_width">90%</fraction> - <dimen name="config_suggestions_strip_horizontal_padding">94.5dp</dimen> <dimen name="config_suggestion_min_width">46dp</dimen> - <dimen name="config_suggestion_text_horizontal_padding">8dp</dimen> + <dimen name="config_suggestion_text_horizontal_padding">10dp</dimen> <dimen name="config_suggestion_text_size">22dp</dimen> <dimen name="config_more_suggestions_hint_text_size">33dp</dimen> diff --git a/java/res/values-th/strings-action-keys.xml b/java/res/values-th/strings-action-keys.xml index f23bfbc2a..6cbd491f9 100644 --- a/java/res/values-th/strings-action-keys.xml +++ b/java/res/values-th/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"ก่อน"</string> <string name="label_done_key" msgid="7564866296502630852">"เสร็จ"</string> <string name="label_send_key" msgid="482252074224462163">"ส่ง"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"หยุด"</string> <string name="label_wait_key" msgid="5891247853595466039">"รอ"</string> </resources> diff --git a/java/res/values-th/strings-talkback-descriptions.xml b/java/res/values-th/strings-talkback-descriptions.xml index eb712aeac..81c86fd5b 100644 --- a/java/res/values-th/strings-talkback-descriptions.xml +++ b/java/res/values-th/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"ไม่มีข้อความ"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> แก้ไข <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> เป็น <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> ทำการแก้ไขอัตโนมัติ"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"รหัสคีย์ %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift เปิดอยู่ (แตะเพื่อปิดใช้งาน)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock เปิดอยู่ (แตะเพื่อปิดใช้งาน)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"สัญลักษณ์เพิ่มเติม"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"สัญลักษณ์"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"ลบ"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"สัญลักษณ์"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"ตัวอักษร"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"ก่อนหน้า"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"เปิดใช้งาน Shift แล้ว"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"เปิดใช้งาน Caps Lock แล้ว"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"ปิดใช้งาน Shift แล้ว"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"โหมดสัญลักษณ์"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"โหมดสัญลักษณ์เพิ่มเติม"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"โหมดตัวอักษร"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"โหมดโทรศัพท์"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"โหมดสัญลักษณ์โทรศัพท์"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"สถานที่"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"สัญลักษณ์"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"ไอคอนสื่ออารมณ์"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index ece0c4ba9..83f196f87 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"เครื่องมือตรวจการสะกดใช้รายการจากรายชื่อติดต่อของคุณ"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"แนะนำชื่อผู้ติดต่อ"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"คำแนะนำในแบบของคุณ"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"แตะ Space สองครั้งแทรกจุด"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"แตะ Spacebar สองครั้งจะแทรกจุดตามด้วยช่องว่างหนึ่งช่อง"</string> <string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"ภาษาสำหรับการป้อนข้อมูล"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"แตะอีกครั้งเพื่อบันทึก"</string> <string name="has_dictionary" msgid="6071847973466625007">"มีพจนานุกรมให้ใช้งาน"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"เปิดใช้งานการแสดงความคิดเห็นจากผู้ใช้"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"ช่วยปรับปรุงตัวแก้ไขวิธีการป้อนข้อมูลนี้โดยการส่งสถิติการใช้งานและรายงานการขัดข้องโดยอัตโนมัติ"</string> <string name="keyboard_layout" msgid="8451164783510487501">"ชุดรูปแบบแป้นพิมพ์"</string> <string name="subtype_en_GB" msgid="88170601942311355">"อังกฤษ (สหราชอาณาจักร)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"อังกฤษ (อเมริกัน)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"เปิดใช้งาน"</string> <string name="not_now" msgid="6172462888202790482">"ข้ามไปก่อน"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"รูปแบบการป้อนข้อมูลเดียวกันนี้มีอยู่แล้ว: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"โหมดศึกษาประโยชน์ในการใช้งาน"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"การหน่วงเวลาของการกดแป้นค้าง"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ระยะเวลาการสั่นเมื่อกดแป้นพิมพ์"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ระดับเสียงเมื่อกดแป้นพิมพ์"</string> diff --git a/java/res/values-tl/strings-action-keys.xml b/java/res/values-tl/strings-action-keys.xml index a7f4cc78f..ac511d03f 100644 --- a/java/res/values-tl/strings-action-keys.xml +++ b/java/res/values-tl/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Nauna"</string> <string name="label_done_key" msgid="7564866296502630852">"Tapos"</string> <string name="label_send_key" msgid="482252074224462163">"Send"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Pause"</string> <string name="label_wait_key" msgid="5891247853595466039">"Intay"</string> </resources> diff --git a/java/res/values-tl/strings-talkback-descriptions.xml b/java/res/values-tl/strings-talkback-descriptions.xml index fbf276be8..06ba9211d 100644 --- a/java/res/values-tl/strings-talkback-descriptions.xml +++ b/java/res/values-tl/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Walang tekstong inilagay"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"Itinatama ng <xliff:g id="KEY_NAME">%1$s</xliff:g> ang <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> sa <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"Nagsasagawa ang <xliff:g id="KEY_NAME">%1$s</xliff:g> ng auto-correction"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Key code %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Naka-on ang shift (i-tap upang i-disable)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Naka-on ang caps lock (i-tap upang i-disable)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Marami pang simbolo"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Mga Simbolo"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Tanggalin"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Mga Simbolo"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Mga Titik"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Nauna"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Naka-enable ang shift"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Naka-enable ang caps lock"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Naka-disable ang shift"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Symbols mode"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Mode na marami pang simbolo"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Letters mode"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Phone mode"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Phone symbols mode"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Mga Lugar"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Mga Simbolo"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Mga Emoticon"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index ce1207a5f..b9216a607 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Gumagamit ang Spell Checker ng entries mula sa iyong contact list."</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Mungkahi pangalan Contact"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Personalized suggestions"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Double-space period"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Naglalagay ng tuldok na may puwang ang pag-double tap sa spacebar"</string> <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Mga wika ng input"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Pinduting muli upang i-save"</string> <string name="has_dictionary" msgid="6071847973466625007">"Available ang diksyunaryo"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Paganahin ang feedback ng user"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Tumulong na pahusayin ang editor ng pamamaraan ng pag-input na ito sa pamamagitan ng awtomatikong pagpapadala ng mga istatistika ng paggamit at ulat ng pag-crash"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Tema ng keyboard"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Ingles (UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Ingles (Estados Unidos)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Paganahin"</string> <string name="not_now" msgid="6172462888202790482">"Hindi ngayon"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Umiiral na ang parehong estilo ng input: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Study mode ng pagiging kapaki-pakinabang"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tagal ng vibration ng keypress"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume ng tunog ng keypress"</string> diff --git a/java/res/values-tr/strings-action-keys.xml b/java/res/values-tr/strings-action-keys.xml index b34c576b6..5592e6c69 100644 --- a/java/res/values-tr/strings-action-keys.xml +++ b/java/res/values-tr/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Geri"</string> <string name="label_done_key" msgid="7564866296502630852">"Bitti"</string> <string name="label_send_key" msgid="482252074224462163">"Gönder"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Dur"</string> <string name="label_wait_key" msgid="5891247853595466039">"Bekle"</string> </resources> diff --git a/java/res/values-tr/strings-talkback-descriptions.xml b/java/res/values-tr/strings-talkback-descriptions.xml index d06c9009c..a3f635cb9 100644 --- a/java/res/values-tr/strings-talkback-descriptions.xml +++ b/java/res/values-tr/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Metin girilmedi"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g>, <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> kelimesini <xliff:g id="CORRECTED_WORD">%3$s</xliff:g> olarak düzeltir"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> otomatik düzeltme yapar"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Tuş kodu: %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Üst karakter"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Üst karakter açık (devre dışı bırakmak için hafifçe vurun)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Büyük harf kilidi açık (devre dışı bırakmak için hafifçe vurun)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Diğer simgeler"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Üst karakter"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Simgeler"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Üst karakter"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Sil"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Simgeler"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Harfler"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Önceki"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Üst karakter etkin"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Büyük harf etkin"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Üst karakter devre dışı"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Sembol modu"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Diğer simgeler modu"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Harf modu"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Telefon modu"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Telefon sembolleri modu"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Yerler"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Simgeler"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"İfadeler"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index 3584ec5db..b1573c3d8 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Yazım denetleyici, kişi listenizdeki girişleri kullanır"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Kişi Adları öner"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Kişisel öneriler"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Çift boşlukla nokta ekleme"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Boşluk çubuğuna iki kez vurmak nokta ve ardından bir boşluk ekler"</string> <string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Giriş dilleri"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Kaydetmek için tekrar dokunun"</string> <string name="has_dictionary" msgid="6071847973466625007">"Sözlük kullanılabilir"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Kullanıcı geri bildirimini etkinleştir"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Kullanım istatistiklerini ve kilitlenme raporlarını otomatik olarak göndererek bu giriş yöntemi düzenleyicisinin iyileştirilmesine yardımcı olun."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Klavye teması"</string> <string name="subtype_en_GB" msgid="88170601942311355">"İngilizce (BK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"İngilizce (ABD)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Etkinleştir"</string> <string name="not_now" msgid="6172462888202790482">"Şimdi değil"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Aynı giriş stili zaten var: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Kullanılabilirlik çalışması modu"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tuşa uzun basma gecikmesi"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Tuşa basma titreşim süresi"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Tuşa basma ses seviyesi"</string> diff --git a/java/res/values-uk/strings-action-keys.xml b/java/res/values-uk/strings-action-keys.xml index 3e5762b3f..8b71498c1 100644 --- a/java/res/values-uk/strings-action-keys.xml +++ b/java/res/values-uk/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Назад"</string> <string name="label_done_key" msgid="7564866296502630852">"ОК"</string> <string name="label_send_key" msgid="482252074224462163">"Слати"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Пауза"</string> <string name="label_wait_key" msgid="5891247853595466039">"Ждати"</string> </resources> diff --git a/java/res/values-uk/strings-talkback-descriptions.xml b/java/res/values-uk/strings-talkback-descriptions.xml index 9b6d1142d..e4df115ae 100644 --- a/java/res/values-uk/strings-talkback-descriptions.xml +++ b/java/res/values-uk/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Текст не введено"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> виправляє слово \"<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>\" на \"<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>\""</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> автоматично виправляє"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Код клавіші – %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift увімкнено (торкніться, щоб вимкнути)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps Lock увімкнено (торкніться, щоб вимкнути)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Більше символів"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Клавіша Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Символи"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Клавіша Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Видалити"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Символи"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Літери"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Назад"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift увімкнено"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Caps Lock увімкнено"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift вимкнено"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Режим символів"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Режим \"Більше символів\""</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Режим літер"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Режим номерів телефону"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Режим телефонних символів"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Місця"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Символи"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Смайли"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index 15a30805e..f2993fc03 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Програма перевірки правопису використ. записи зі списку контактів"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр. при натисканні клавіш"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Пропон. імена контактів"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Персональні пропозиції"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Крапка подвійним пробілом"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Подвійне натискання пробілу вставляє крапку з пробілом після неї"</string> <string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Мови введення"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Торкніться знову, щоб зберегти"</string> <string name="has_dictionary" msgid="6071847973466625007">"Словник доступний"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Увімк. відгуки корист."</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Допоможіть покращити цей редактор методу введення, автоматично надсилаючи в Google статистику використання та звіти про аварійне завершення роботи."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Тема клавіатури"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Англійська (Великобританія)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Англійська (США)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Увімкнути"</string> <string name="not_now" msgid="6172462888202790482">"Не зараз"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такий стиль введення вже існує: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Режим вивчення зручності у використанні"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Затримка довгого натискання"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Вібрація при натисканні клавіш"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Гучність натискання клавіш"</string> diff --git a/java/res/values-v20/platform-theme.xml b/java/res/values-v20/platform-theme.xml index 06062047b..52e7f3521 100644 --- a/java/res/values-v20/platform-theme.xml +++ b/java/res/values-v20/platform-theme.xml @@ -21,6 +21,6 @@ <!-- TODO: This file is temporarily placed under values-v20. --> <!-- TODO: It might be moved under values-v21. --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="platformActivityTheme" parent="@android:style/Theme.Quantum.Light" /> - <style name="platformDialogTheme" parent="@android:style/Theme.Quantum.Light.Dialog" /> + <style name="platformActivityTheme" parent="@android:style/Theme.Material.Light" /> + <style name="platformDialogTheme" parent="@android:style/Theme.Material.Light.Dialog" /> </resources> diff --git a/java/res/values-vi/strings-action-keys.xml b/java/res/values-vi/strings-action-keys.xml index ceb780ed1..16b7c959e 100644 --- a/java/res/values-vi/strings-action-keys.xml +++ b/java/res/values-vi/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Trước"</string> <string name="label_done_key" msgid="7564866296502630852">"Xong"</string> <string name="label_send_key" msgid="482252074224462163">"Gửi"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Tdừng"</string> <string name="label_wait_key" msgid="5891247853595466039">"Đợi"</string> </resources> diff --git a/java/res/values-vi/strings-talkback-descriptions.xml b/java/res/values-vi/strings-talkback-descriptions.xml index 9202883c8..ad63fa52f 100644 --- a/java/res/values-vi/strings-talkback-descriptions.xml +++ b/java/res/values-vi/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Không có ký tự nào được nhập"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"<xliff:g id="KEY_NAME">%1$s</xliff:g> sửa <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> thành <xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"<xliff:g id="KEY_NAME">%1$s</xliff:g> tự động sửa"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Mã phím %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift đang bật (nhấn để tắt)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Caps lock đang bật (nhấn để tắt)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Biểu tượng khác"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Biểu tượng"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Xóa"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Biểu tượng"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Chữ cái"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Trước"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Đã bật Shift"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Đã bật Caps lock"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Đã tắt Shift"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Chế độ biểu tượng"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Chế độ biểu tượng khác"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Chế độ chữ cái"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Chế độ điện thoại"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Chế độ biểu tượng điện thoại"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Địa điểm"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Biểu tượng"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Biểu tượng cảm xúc"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 529dcec17..5a47ad06a 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Trình kiểm tra chính tả sử dụng các mục nhập từ danh sách liên hệ của bạn"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Đề xuất tên liên hệ"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Đề xuất được cá nhân hóa"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Dấu cách đôi"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Nhấn đúp vào phím cách sẽ chèn thêm một dấu sau dấu cách"</string> <string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Ngôn ngữ nhập"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Chạm lại để lưu"</string> <string name="has_dictionary" msgid="6071847973466625007">"Có sẵn từ điển"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Bật phản hồi của người dùng"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Giúp cải tiến trình chỉnh sửa phương thức nhập này bằng cách tự động gửi thống kê sử dụng và báo cáo sự cố"</string> <string name="keyboard_layout" msgid="8451164783510487501">"Chủ đề bàn phím"</string> <string name="subtype_en_GB" msgid="88170601942311355">"Tiếng Anh (Anh)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"Tiếng Anh (Mỹ)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Bật"</string> <string name="not_now" msgid="6172462888202790482">"Để sau"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Đã tồn tại kiểu nhập tương tự: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Chế độ nghiên cứu tính khả dụng"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Tgian chờ cho nhấn và giữ phím"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Thời gian rung khi nhấn phím"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Âm lượng khi nhấn phím"</string> diff --git a/java/res/values-zh-rCN/strings-action-keys.xml b/java/res/values-zh-rCN/strings-action-keys.xml index aacbb5fc7..a3629da2f 100644 --- a/java/res/values-zh-rCN/strings-action-keys.xml +++ b/java/res/values-zh-rCN/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"上个"</string> <string name="label_done_key" msgid="7564866296502630852">"完成"</string> <string name="label_send_key" msgid="482252074224462163">"发送"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"暂停"</string> <string name="label_wait_key" msgid="5891247853595466039">"等待"</string> </resources> diff --git a/java/res/values-zh-rCN/strings-talkback-descriptions.xml b/java/res/values-zh-rCN/strings-talkback-descriptions.xml index 93f89e091..dda7638e4 100644 --- a/java/res/values-zh-rCN/strings-talkback-descriptions.xml +++ b/java/res/values-zh-rCN/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"未输入文字"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"按<xliff:g id="KEY_NAME">%1$s</xliff:g>键可将<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>更正为<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"按<xliff:g id="KEY_NAME">%1$s</xliff:g>键可进行自动更正"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"键码为%d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"已开启Shift模式(点按即可关闭)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"已锁定大写模式(点按即可关闭)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"更多符号"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift键"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"符号"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift键"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"删除"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"符号"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"字母"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"上一个"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"已开启Shift模式"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"已锁定大写模式"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"已关闭Shift模式"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"符号模式"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"更多符号模式"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"字母模式"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"电话模式"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"电话符号模式"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"地点"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"符号"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"表情图标"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index 41fe0bfb6..7725118e8 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼写检查工具会使用您的联系人列表中的条目"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键振动"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"联系人姓名建议"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"根据通讯录中的姓名提供建议和更正"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"个性化建议"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"双击空格插入句号"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"双击空格键可插入句号并后跟空格"</string> <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"输入语言"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次触摸即可保存"</string> <string name="has_dictionary" msgid="6071847973466625007">"有可用词典"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"启用用户反馈"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"自动发送使用情况统计信息和崩溃报告,帮助改进此输入法。"</string> <string name="keyboard_layout" msgid="8451164783510487501">"键盘主题"</string> <string name="subtype_en_GB" msgid="88170601942311355">"英语(英国)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"英语(美国)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"启用"</string> <string name="not_now" msgid="6172462888202790482">"以后再说"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已经存在相同的输入风格:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按键长按延迟"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按键振动时长"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按键音量"</string> diff --git a/java/res/values-zh-rHK/strings-action-keys.xml b/java/res/values-zh-rHK/strings-action-keys.xml index e95251696..4dc5c8450 100644 --- a/java/res/values-zh-rHK/strings-action-keys.xml +++ b/java/res/values-zh-rHK/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"上一步"</string> <string name="label_done_key" msgid="7564866296502630852">"完成"</string> <string name="label_send_key" msgid="482252074224462163">"傳送"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"暫停"</string> <string name="label_wait_key" msgid="5891247853595466039">"等候"</string> </resources> diff --git a/java/res/values-zh-rHK/strings-talkback-descriptions.xml b/java/res/values-zh-rHK/strings-talkback-descriptions.xml index 8b60504d9..a962492ed 100644 --- a/java/res/values-zh-rHK/strings-talkback-descriptions.xml +++ b/java/res/values-zh-rHK/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"未輸入文字"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"按「<xliff:g id="KEY_NAME">%1$s</xliff:g>」可將「<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>」修正為「<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>」"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"按「<xliff:g id="KEY_NAME">%1$s</xliff:g>」可自動修正"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"按鍵代碼 %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift 鍵"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift 鍵已開啟 (輕按即可停用)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"大寫鎖定已開啟 (輕按即可停用)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"更多符號"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift 鍵"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"符號"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift 鍵"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"刪除"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"符號"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"字母"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"上一個"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift 鍵已啟用"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"大寫鎖定已啟用"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift 鍵已停用"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"符號模式"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"更多符號模式"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"字母模式"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"撥號模式"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"符號撥號模式"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"地點"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"符號"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"表情符號"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index 34fa9c037..564db46fe 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"研究記錄指令"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找聯絡人姓名"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人名單中的各項記錄"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用「聯絡人」的名稱提供建議與修正"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"個人化建議"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"按兩下空格鍵插入句號"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"只要輕按兩下空格鍵,即可插入句號並在後面加上一個空格"</string> <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"輸入語言"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次輕觸即可儲存"</string> <string name="has_dictionary" msgid="6071847973466625007">"可使用字典"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"啟用用戶意見反映"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"自動傳送使用統計資料和當機報告,協助改良這個輸入法編輯器"</string> <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string> <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英國)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美國)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"啟用"</string> <string name="not_now" msgid="6172462888202790482">"暫時不要"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"長按鍵延遲"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> diff --git a/java/res/values-zh-rTW/strings-action-keys.xml b/java/res/values-zh-rTW/strings-action-keys.xml index 00daa5c63..9b8be9426 100644 --- a/java/res/values-zh-rTW/strings-action-keys.xml +++ b/java/res/values-zh-rTW/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"返回"</string> <string name="label_done_key" msgid="7564866296502630852">"完成"</string> <string name="label_send_key" msgid="482252074224462163">"傳送"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"暫停"</string> <string name="label_wait_key" msgid="5891247853595466039">"等待"</string> </resources> diff --git a/java/res/values-zh-rTW/strings-talkback-descriptions.xml b/java/res/values-zh-rTW/strings-talkback-descriptions.xml index 6351a985a..c097b28b7 100644 --- a/java/res/values-zh-rTW/strings-talkback-descriptions.xml +++ b/java/res/values-zh-rTW/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"未輸入文字"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"按下「<xliff:g id="KEY_NAME">%1$s</xliff:g>」可將「<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>」修正為「<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>」"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"按下「<xliff:g id="KEY_NAME">%1$s</xliff:g>」可執行自動修正"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"按鍵代碼 %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"Shift 鍵"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"Shift 鍵已開啟 (輕按即可停用)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"大寫鎖定已開啟 (輕按即可停用)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"更多符號"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"Shift 鍵"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"符號"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"Shift 鍵"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"刪除"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"符號"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"字母"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"上一個"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"Shift 鍵已啟用"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"大寫鎖定已啟用"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"Shift 鍵已停用"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"符號模式"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"更多符號模式"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"字母模式"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"撥號模式"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"撥號符號模式"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"地點"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"符號"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"表情"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 9c9f5e052..176d5b4a7 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人清單項目"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"聯絡人姓名建議"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"根據「聯絡人」名稱提供建議與修正"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"個人化建議"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"輕按兩下空格鍵即插入句號"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"輕按兩下空格鍵可插入句號另加一個空格"</string> <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"輸入語言"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次輕觸即可儲存"</string> <string name="has_dictionary" msgid="6071847973466625007">"可用的字典"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"啟用使用者意見回饋"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"自動傳送使用統計資料和當機報告,協助改善此輸入法編輯器"</string> <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string> <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英國)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美國)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"啟用"</string> <string name="not_now" msgid="6172462888202790482">"暫時不要"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"使用習慣學習模式"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"按鍵長按延遲"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動持續時間"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string> diff --git a/java/res/values-zu/strings-action-keys.xml b/java/res/values-zu/strings-action-keys.xml index 6cd5e8c99..5d60a6cca 100644 --- a/java/res/values-zu/strings-action-keys.xml +++ b/java/res/values-zu/strings-action-keys.xml @@ -25,6 +25,8 @@ <string name="label_previous_key" msgid="1421141755779895275">"Okwedlule"</string> <string name="label_done_key" msgid="7564866296502630852">"Kwenziwe"</string> <string name="label_send_key" msgid="482252074224462163">"Thumela"</string> + <!-- no translation found for label_search_key (7965186050435796642) --> + <skip /> <string name="label_pause_key" msgid="2225922926459730642">"Misa isikhashana"</string> <string name="label_wait_key" msgid="5891247853595466039">"Linda"</string> </resources> diff --git a/java/res/values-zu/strings-talkback-descriptions.xml b/java/res/values-zu/strings-talkback-descriptions.xml index e01f7347d..9f344d8bf 100644 --- a/java/res/values-zu/strings-talkback-descriptions.xml +++ b/java/res/values-zu/strings-talkback-descriptions.xml @@ -25,10 +25,13 @@ <string name="spoken_no_text_entered" msgid="1711276837961785646">"Awukho umbhalo ofakiwe"</string> <string name="spoken_auto_correct" msgid="8989324692167993804">"I-<xliff:g id="KEY_NAME">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> kube yi-<xliff:g id="CORRECTED_WORD">%3$s</xliff:g>"</string> <string name="spoken_auto_correct_obscured" msgid="7769449372355268412">"I-<xliff:g id="KEY_NAME">%1$s</xliff:g> yenza ukulungisa okuzenzakalelayo"</string> - <string name="spoken_description_unknown" msgid="2382510329910793539">"Ikhodi yokhiye %d"</string> + <!-- no translation found for spoken_description_unknown (5139930082759824442) --> + <skip /> <string name="spoken_description_shift" msgid="7209798151676638728">"U-Shift"</string> - <string name="spoken_description_shift_shifted" msgid="1609924271343916689">"U-Shift uvuliwe (thepha ukuwuvimbela)"</string> - <string name="spoken_description_caps_lock" msgid="5020582161133170892">"Ofeleba bavuliwe (thepha ukubavimbela)"</string> + <string name="spoken_description_symbols_shift" msgid="3483198879916435717">"Amasimbuli amaningi"</string> + <string name="spoken_description_shift_shifted" msgid="3122704922642232605">"U-Shift"</string> + <string name="spoken_description_symbols_shift_shifted" msgid="5179175466878186081">"Amasimbuli"</string> + <string name="spoken_description_caps_lock" msgid="1224851412185975036">"U-Shift"</string> <string name="spoken_description_delete" msgid="3878902286264983302">"Susa"</string> <string name="spoken_description_to_symbol" msgid="8244903740201126590">"Amasimbuli"</string> <string name="spoken_description_to_alpha" msgid="4081215210530031950">"Izinhlamvu"</string> @@ -46,8 +49,8 @@ <string name="spoken_description_action_previous" msgid="2919072174697865110">"Okwangaphambilini"</string> <string name="spoken_description_shiftmode_on" msgid="5107180516341258979">"U-Shift uvunyelwe"</string> <string name="spoken_description_shiftmode_locked" msgid="7307477738053606881">"Ofeleba bavunyelwe"</string> - <string name="spoken_description_shiftmode_off" msgid="5039126122829961331">"U-Shift uvimbelwe"</string> <string name="spoken_description_mode_symbol" msgid="111186851131446691">"Imodi yezimpawu"</string> + <string name="spoken_description_mode_symbol_shift" msgid="4305607977537665389">"Imodi yamasimbuli amaningi"</string> <string name="spoken_description_mode_alpha" msgid="4676004119618778911">"Imodi yezinhlamvu"</string> <string name="spoken_description_mode_phone" msgid="2061220553756692903">"Imodi yefoni"</string> <string name="spoken_description_mode_phone_shift" msgid="7879963803547701090">"Imodi yezimpawu zefoni"</string> @@ -69,4 +72,22 @@ <string name="spoken_descrption_emoji_category_places" msgid="1163315840948545317">"Izindawo"</string> <string name="spoken_descrption_emoji_category_symbols" msgid="474680659024880601">"Amasimbuli"</string> <string name="spoken_descrption_emoji_category_emoticons" msgid="456737544787823539">"Izithombe-mzwelo"</string> + <!-- no translation found for spoken_description_upper_case (4904835255229433916) --> + <skip /> + <!-- no translation found for spoken_letter_0049 (4743162182646977944) --> + <skip /> + <!-- no translation found for spoken_letter_0130 (4766619646231612274) --> + <skip /> + <!-- no translation found for spoken_symbol_unknown (717298227061173706) --> + <skip /> + <!-- no translation found for spoken_emoji_unknown (5981009928135394306) --> + <skip /> + <!-- no translation found for spoken_open_more_keys_keyboard (6832897688371903747) --> + <skip /> + <!-- no translation found for spoken_close_more_keys_keyboard (3524914657934712026) --> + <skip /> + <!-- no translation found for spoken_open_more_suggestions (4231720702882969760) --> + <skip /> + <!-- no translation found for spoken_close_more_suggestions (9118455416075032839) --> + <skip /> </resources> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index c79d81dcc..d330c0785 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -21,7 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string> <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string> <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Isihloli sokupela sisebenzisa okungenayo kusuka kuhlu lalabo oxhumana nabo"</string> <string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string> @@ -47,6 +46,8 @@ <string name="use_contacts_dict" msgid="4435317977804180815">"Sikisela amagama Othintana nabo"</string> <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Amagama abasebenzisi kusuka Kothintana nabo bokusikisela nokulungisa"</string> <string name="use_personalized_dicts" msgid="5167396352105467626">"Iziphakamiso ezenziwe okomuntu siqu"</string> + <!-- no translation found for enable_metrics_logging (5506372337118822837) --> + <skip /> <string name="use_double_space_period" msgid="8781529969425082860">"Isikhathi se-Double-space"</string> <string name="use_double_space_period_summary" msgid="6532892187247952799">"Ukuthepha kabili kubha yesikhala kufaka isikhathi esilandelwa yisikhala"</string> <string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string> @@ -84,8 +85,6 @@ <string name="select_language" msgid="3693815588777926848">"Izilimi zokufakwayo"</string> <string name="hint_add_to_dictionary" msgid="573678656946085380">"Thinta futhi ukuze ulondoloze"</string> <string name="has_dictionary" msgid="6071847973466625007">"Isichazamazwi siyatholakala"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Vumela impendulo yomsebenzisi"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Siza ukuthuthukisa lo mhleli wendlela yokufaka ngokusithumela ngokuzenzakalela izibalo zokusetshenziswa nokukhubeka."</string> <string name="keyboard_layout" msgid="8451164783510487501">"Indikimba yekhibhodi"</string> <string name="subtype_en_GB" msgid="88170601942311355">"i-English(UK)"</string> <string name="subtype_en_US" msgid="6160452336634534239">"i-English (US)"</string> @@ -118,7 +117,6 @@ <string name="enable" msgid="5031294444630523247">"Nika amandla"</string> <string name="not_now" msgid="6172462888202790482">"Hhayi manje"</string> <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Isitayela sokufaka esifanayo sesivele sikhona: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Imodi yesitadi yokusebenziseka"</string> <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ukulibazisa ukucindezela isikhashana ukhiye"</string> <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Ubude besikhathi sokudlidliza ukucindezela ukhiye"</string> <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Ivolumu yomsindo wokucindezela ukhiye"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 475e92f2e..a1f478bd5 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -41,13 +41,17 @@ </declare-styleable> <declare-styleable name="KeyboardView"> - <!-- Image for the key. This image needs to be a {@link StateListDrawable}, with the - following possible states: normal, pressed, checkable, checkable+pressed, + <!-- Background image for the key. This image needs to be a {@link StateListDrawable}, + with the following possible states: normal, pressed, checkable, checkable+pressed, checkable+checked, checkable+checked+pressed. --> <attr name="keyBackground" format="reference" /> - <!-- Image for the functional key used in Emoji layout. --> - <attr name="keyBackgroundEmojiFunctional" format="reference" /> - + <!-- Background image for the functional key. This image needs to be a + {@link StateListDrawable}, with the following possible states: normal, pressed. --> + <attr name="functionalKeyBackground" format="reference" /> + <!-- Background image for the spacebar. This image needs to be a + {@link StateListDrawable}, with the following possible states: normal, pressed. --> + <attr name="spacebarBackground" format="reference" /> + <attr name="spacebarIconWidthRatio" format="float" /> <!-- Horizontal padding of left/right aligned key label to the edge of the key. --> <attr name="keyLabelHorizontalPadding" format="dimension" /> <!-- Right padding of hint letter to the edge of the key.--> @@ -69,14 +73,11 @@ </declare-styleable> <declare-styleable name="MainKeyboardView"> - <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" /> - <attr name="autoCorrectionSpacebarLedIcon" format="reference" /> <!-- Size of the text for spacebar language label, in the proportion of key height. --> <attr name="languageOnSpacebarTextRatio" format="fraction" /> <attr name="languageOnSpacebarTextColor" format="color" /> + <attr name="languageOnSpacebarTextShadowRadius" format="float" /> <attr name="languageOnSpacebarTextShadowColor" format="color" /> - <!-- Background image for the spacebar. --> - <attr name="spacebarBackground" format="reference" /> <!-- Fadeout animator for spacebar language label. --> <attr name="languageOnSpacebarFinalAlpha" format="integer" /> <attr name="languageOnSpacebarFadeoutAnimator" format="reference" /> @@ -171,7 +172,18 @@ </declare-styleable> <declare-styleable name="EmojiPalettesView"> - <attr name="emojiTabLabelColor" format="reference" /> + <attr name="categoryIndicatorEnabled" format="boolean" /> + <attr name="categoryIndicatorDrawable" format="reference" /> + <attr name="categoryIndicatorBackground" format="reference" /> + <attr name="categoryPageIndicatorColor" format="color" /> + <attr name="categoryPageIndicatorBackground" format="color" /> + <attr name="iconEmojiRecentsTab" format="reference" /> + <attr name="iconEmojiCategory1Tab" format="reference" /> + <attr name="iconEmojiCategory2Tab" format="reference" /> + <attr name="iconEmojiCategory3Tab" format="reference" /> + <attr name="iconEmojiCategory4Tab" format="reference" /> + <attr name="iconEmojiCategory5Tab" format="reference" /> + <attr name="iconEmojiCategory6Tab" format="reference" /> </declare-styleable> <declare-styleable name="SuggestionStripView"> @@ -217,7 +229,12 @@ <attr name="iconSettingsKey" format="reference" /> <attr name="iconSpaceKey" format="reference" /> <attr name="iconEnterKey" format="reference" /> + <attr name="iconGoKey" format="reference" /> <attr name="iconSearchKey" format="reference" /> + <attr name="iconSendKey" format="reference" /> + <attr name="iconNextKey" format="reference" /> + <attr name="iconDoneKey" format="reference" /> + <attr name="iconPreviousKey" format="reference" /> <attr name="iconTabKey" format="reference" /> <attr name="iconShortcutKey" format="reference" /> <attr name="iconSpaceKeyForNumberLayout" format="reference" /> @@ -279,6 +296,7 @@ <!-- This should be aligned with Key.LABEL_FLAGS__* --> <flag name="alignLeft" value="0x01" /> <flag name="alignRight" value="0x02" /> + <flag name="alignButtom" value="0x04" /> <flag name="alignLeftOfCenter" value="0x08" /> <flag name="fontNormal" value="0x10" /> <flag name="fontMonoSpace" value="0x20" /> @@ -304,6 +322,9 @@ <flag name="shiftedLetterActivated" value="0x20000" /> <!-- If true, use EditorInfo.actionLabel for the key label. --> <flag name="fromCustomActionLabel" value="0x40000" /> + <!-- If true, use functionalTextColor instead of ketTextColor to drawing the label on + the key --> + <flag name="followFunctionalTextColor" value="0x80000" /> <!-- If true, disable keyHintLabel. --> <flag name="disableKeyHintLabel" value="0x40000000" /> <!-- If true, disable additionalMoreKeys. --> @@ -360,6 +381,8 @@ <attr name="keyTextShadowColor" format="color" /> <!-- Color to use for the label in a key when in inactivated state. --> <attr name="keyTextInactivatedColor" format="color" /> + <!-- Color to use for the label in a key that has followFunctionalTextColor keyLabelFlags. --> + <attr name="functionalTextColor" format="color" /> <!-- Key hint letter (= one character hint label) color --> <attr name="keyHintLetterColor" format="color" /> <!-- Key hint label color --> @@ -414,7 +437,6 @@ <attr name="navigatePrevious" format="boolean" /> <attr name="passwordInput" format="boolean" /> <attr name="clobberSettingsKey" format="boolean" /> - <attr name="supportsSwitchingToShortcutIme" format="boolean" /> <attr name="hasShortcutKey" format="boolean" /> <attr name="languageSwitchKeyEnabled" format="boolean" /> <attr name="isMultiLine" format="boolean" /> @@ -431,6 +453,7 @@ <!-- This should be aligned with KeyboardId.IME_ACTION_* --> <enum name="actionCustomLabel" value="0x100" /> </attr> + <attr name="isIconDefined" format="string" /> <attr name="localeCode" format="string" /> <attr name="languageCode" format="string" /> <attr name="countryCode" format="string" /> diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml index 824928c6b..0eee08de2 100644 --- a/java/res/values/colors.xml +++ b/java/res/values/colors.xml @@ -26,7 +26,6 @@ <color name="suggested_word_color_ics">#B233B5E5</color> <color name="highlight_translucent_color_ics">#9933B5E5</color> <color name="key_text_color_holo">@android:color/white</color> - <color name="key_text_shadow_color_holo">@android:color/transparent</color> <color name="key_text_inactivated_color_holo">#66E0E4E5</color> <color name="key_hint_letter_color_holo">#80000000</color> <color name="key_hint_label_color_holo">#A0FFFFFF</color> @@ -35,21 +34,31 @@ <color name="spacebar_text_color_holo">#FFC0C0C0</color> <color name="spacebar_text_shadow_color_holo">#80000000</color> <color name="gesture_floating_preview_color_holo">#C0000000</color> + <color name="emoji_tab_page_indicator_background_holo">#111111</color> <!-- Color resources for KLP theme. Base color = F0F0F0 --> <color name="highlight_color_klp">#FFF0F0F0</color> <color name="typed_word_color_klp">#D8F0F0F0</color> <color name="suggested_word_color_klp">#B2F0F0F0</color> <color name="highlight_translucent_color_klp">#99E0E0E0</color> + <!-- Color resources for LXX theme. Base color = F0F0F0 --> + <color name="key_text_color_lxx_dark">#FFFFFF</color> + <color name="key_text_inactive_color_lxx_dark">#808184</color> + <color name="key_hint_letter_color_lxx_dark">#808184</color> + <color name="highlight_color_lxx_dark">#7FCAC3</color> + <color name="typed_word_color_lxx_dark">#D87FCAC3</color> + <color name="suggested_word_color_lxx_dark">#B27FCAC3</color> + <color name="highlight_translucent_color_lxx_dark">#997FCAC3</color> + <color name="keyboard_background_lxx_dark">#384248</color> + <color name="key_background_lxx_dark">#384248</color> + <color name="key_background_pressed_lxx_dark">#546872</color> + <color name="suggestions_strip_background_lxx_dark">#263238</color> + <color name="suggested_word_background_selected_lxx_dark">#384248</color> + <color name="gesture_floating_preview_color_lxx_dark">#C0000000</color> + <color name="emoji_tab_page_indicator_background_lxx_dark">#263238</color> <!-- Color resources for setup wizard and tutorial --> <color name="setup_background">#FFEBEBEB</color> <color name="setup_text_dark">#FF707070</color> <color name="setup_text_action">@android:color/holo_blue_light</color> <color name="setup_step_background">@android:color/background_light</color> <color name="setup_welcome_video_margin_color">#FFCCCCCC</color> - <color name="emoji_category_page_id_view_background">#FF000000</color> - <color name="emoji_category_page_id_view_foreground">#80FFFFFF</color> - - <!-- TODO: Color which should be included in the theme --> - <color name="emoji_key_background_color">#00000000</color> - <color name="emoji_key_pressed_background_color">#30FFFFFF</color> </resources> diff --git a/java/res/values/config-common.xml b/java/res/values/config-common.xml index 3fe4b947c..58f3e9827 100644 --- a/java/res/values/config-common.xml +++ b/java/res/values/config-common.xml @@ -24,9 +24,6 @@ at input history to suggest a hopefully helpful suggestions for the next word? --> <bool name="config_default_next_word_prediction">true</bool> - <!-- This configuration must be aligned with {@link KeyboardTheme#DEFAULT_THEME_ID}. --> - <string name="config_default_keyboard_theme_id" translatable="false">2</string> - <integer name="config_delay_update_shift_state">100</integer> <integer name="config_double_space_period_timeout">1100</integer> @@ -51,6 +48,7 @@ <integer name="config_max_longpress_timeout">700</integer> <integer name="config_min_longpress_timeout">100</integer> <integer name="config_longpress_timeout_step">10</integer> + <integer name="config_accessibility_long_press_key_timeout">1500</integer> <integer name="config_max_more_keys_column">5</integer> <integer name="config_more_keys_keyboard_fadein_anim_time">0</integer> <integer name="config_more_keys_keyboard_fadeout_anim_time">100</integer> @@ -136,7 +134,7 @@ <integer name="config_gesture_trail_shadow_ratio">-1</integer> <!-- Common configuration of Emoji keyboard --> - <dimen name="config_emoji_category_page_id_height">3dp</dimen> + <dimen name="config_emoji_category_page_id_height">2dp</dimen> <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. --> <dimen name="config_accessibility_edge_slop">8dp</dimen> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 45ea48392..9f556a6e4 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -64,10 +64,11 @@ <fraction name="config_key_shifted_letter_hint_ratio_5row">41%</fraction> <dimen name="config_suggestions_strip_height">40dp</dimen> + <dimen name="config_suggestions_strip_horizontal_margin">36dp</dimen> + <dimen name="config_suggestions_strip_edge_key_width">36dp</dimen> <dimen name="config_more_suggestions_row_height">40dp</dimen> <integer name="config_max_more_suggestions_row">6</integer> <fraction name="config_min_more_suggestions_width">90%</fraction> - <dimen name="config_suggestions_strip_horizontal_padding">0dp</dimen> <dimen name="config_suggestion_min_width">44dp</dimen> <dimen name="config_suggestion_text_horizontal_padding">6dp</dimen> <dimen name="config_suggestion_text_size">18dp</dimen> diff --git a/java/res/values/donottranslate-config-spacing-and-punctuations.xml b/java/res/values/donottranslate-config-spacing-and-punctuations.xml index 1be5cf888..2faf578d2 100644 --- a/java/res/values/donottranslate-config-spacing-and-punctuations.xml +++ b/java/res/values/donottranslate-config-spacing-and-punctuations.xml @@ -26,6 +26,8 @@ <string name="symbols_preceded_by_space">([{&</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> <string name="symbols_followed_by_space">.,;:!?)]}&</string> + <!-- Symbols that behave like a single punctuation when typed next to each other --> + <string name="symbols_clustering_together"></string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> <string name="symbols_word_separators">"	 
 "()[]{}*&<>+=|.,;:!?/_\"</string> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 9a610a0d0..83d082f82 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -47,17 +47,6 @@ <string name="prefs_debug_mode">Debug Mode</string> <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string> - <!-- For keyboard color scheme option dialog. --> - <string-array name="keyboard_theme_names"> - <item>@string/keyboard_color_scheme_white</item> - <item>@string/keyboard_color_scheme_blue</item> - </string-array> - <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. --> - <string-array name="keyboard_theme_ids"> - <item>2</item> - <item>0</item> - </string-array> - <!-- Subtype locale display name exceptions. For each exception, there should be related string resources for display name that may have explicit keyboard layout. The string resource name must be "subtype_<locale>" or @@ -148,6 +137,4 @@ <item>tr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> </string-array> - - <string name="settings_warning_researcher_mode">Attention! You are using the special keyboard for research purposes.</string> </resources> diff --git a/java/res/values/keyboard-icons-holo.xml b/java/res/values/keyboard-icons-holo.xml index 4c888d570..669d2c07d 100644 --- a/java/res/values/keyboard-icons-holo.xml +++ b/java/res/values/keyboard-icons-holo.xml @@ -21,9 +21,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="KeyboardIcons.Holo"> <!-- Keyboard icons --> - <!-- TODO: The following holo icon for phone (drawable-hdpi and drawable-xhdpi) are missing. - sym_keyboard_123_mic_holo - --> <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item> diff --git a/java/res/values/keyboard-icons-lxx-dark.xml b/java/res/values/keyboard-icons-lxx-dark.xml new file mode 100644 index 000000000..6e9c4d1c1 --- /dev/null +++ b/java/res/values/keyboard-icons-lxx-dark.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="KeyboardIcons.LXX_Dark"> + <!-- Keyboard icons --> + <!-- TODO: Update those icons for LXX theme. --> + <item name="iconShiftKey">@drawable/sym_keyboard_shift_lxx_dark</item> + <item name="iconDeleteKey">@drawable/sym_keyboard_delete_lxx_dark</item> + <item name="iconSettingsKey">@drawable/sym_keyboard_settings_lxx_dark</item> + <item name="iconSpaceKey">@drawable/sym_keyboard_spacebar_lxx_dark</item> + <item name="iconEnterKey">@drawable/sym_keyboard_return_lxx_dark</item> + <item name="iconGoKey">@drawable/sym_keyboard_go_lxx_dark</item> + <item name="iconSearchKey">@drawable/sym_keyboard_search_lxx_dark</item> + <item name="iconSendKey">@drawable/sym_keyboard_send_lxx_dark</item> + <item name="iconNextKey">@drawable/sym_keyboard_next_lxx_dark</item> + <item name="iconDoneKey">@drawable/sym_keyboard_done_lxx_dark</item> + <item name="iconPreviousKey">@drawable/sym_keyboard_previous_lxx_dark</item> + <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item> + <item name="iconShortcutKey">@drawable/sym_keyboard_voice_lxx_dark</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item> + <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_lxx_dark</item> + <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_lxx_dark</item> + <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item> + <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_dark</item> + <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item> + <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item> + <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + </style> +</resources> diff --git a/java/res/values/keyboard-themes.xml b/java/res/values/keyboard-themes.xml new file mode 100644 index 000000000..a06082c6e --- /dev/null +++ b/java/res/values/keyboard-themes.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- For keyboard color scheme option dialog. --> + <string-array name="keyboard_theme_names" translatable="false"> + <!-- TODO: Make this item as translatable string resource. --> + <item>Material</item> + <item>@string/keyboard_color_scheme_white</item> + <item>@string/keyboard_color_scheme_blue</item> + </string-array> + <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. --> + <string-array name="keyboard_theme_ids" translatable="false"> + <item>3</item> + <item>2</item> + <item>0</item> + </string-array> +</resources> diff --git a/java/res/values/research_strings.xml b/java/res/values/research_strings.xml deleted file mode 100644 index e73871109..000000000 --- a/java/res/values/research_strings.xml +++ /dev/null @@ -1,41 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2012, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Contents of note explaining what data is collected and how. --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_splash_content" translatable="false"></string> - <!-- Account type allowed for inclusion in user-invoked feedback logs [CHAR LIMIT=38] --> - <string name="research_account_type" translatable="false"></string> - <!-- Account domain allowed for inclusion in user-invoked feedback logs [CHAR LIMIT=38] --> - <string name="research_allowed_account_domain" translatable="false"></string> - - <!-- Menu option that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_menu_option" translatable="false">Send feedback</string> - <!-- Title of dialog box that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_dialog_title" translatable="false">Send feedback</string> - <!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_hint" translatable="false">Enter your feedback here.</string> - <!-- Message informing the user that the feedback string must not be empty [CHAR LIMIT=100] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_empty_feedback_error_message" translatable="false">The feedback field must not be empty.</string> -</resources> diff --git a/java/res/values/strings-action-keys.xml b/java/res/values/strings-action-keys.xml index 7003784c6..96b2e7dda 100644 --- a/java/res/values/strings-action-keys.xml +++ b/java/res/values/strings-action-keys.xml @@ -29,6 +29,8 @@ <string name="label_done_key">Done</string> <!-- Label for soft enter key when it performs SEND action. Must be short to fit on key. 5 chars or less is preferable. [CHAR LIMIT=7] --> <string name="label_send_key">Send</string> + <!-- Label for soft enter key when it performs SEARCH action. Must be short to fit on key. 5 chars or less is preferable. [CHAR LIMIT=7] --> + <string name="label_search_key">Search</string> <!-- Label for "Pause" key of phone number keyboard. Must be short to fit on key. 5 chars or less is preferable. [CHAR LIMIT=7] --> <string name="label_pause_key">Pause</string> <!-- Label for "Wait" key of phone number keyboard. Must be short to fit on key. 5 chars or less is preferable. [CHAR LIMIT=7]--> diff --git a/java/res/values/strings-emoji-descriptions.xml b/java/res/values/strings-emoji-descriptions.xml new file mode 100644 index 000000000..8cbde264c --- /dev/null +++ b/java/res/values/strings-emoji-descriptions.xml @@ -0,0 +1,1671 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<!-- + These Emoji symbols are unsupported by TTS. + TODO: Remove this file when TTS/TalkBack support these Emoji symbols. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Spoken description for Unicode code point U+00A9: "©" COPYRIGHT SIGN --> + <string name="spoken_emoji_00A9">Copyright sign</string> + <!-- Spoken description for Unicode code point U+00AE: "®" REGISTERED SIGN --> + <string name="spoken_emoji_00AE">Registered sign</string> + <!-- Spoken description for Unicode code point U+203C: "‼" DOUBLE EXCLAMATION MARK --> + <string name="spoken_emoji_203C">Double exclamation mark</string> + <!-- Spoken description for Unicode code point U+2049: "⁉" EXCLAMATION QUESTION MARK --> + <string name="spoken_emoji_2049">Exclamation question mark</string> + <!-- Spoken description for Unicode code point U+2122: "™" TRADE MARK SIGN --> + <string name="spoken_emoji_2122">Trade mark sign</string> + <!-- Spoken description for Unicode code point U+2139: "ℹ" INFORMATION SOURCE --> + <string name="spoken_emoji_2139">Information source</string> + <!-- Spoken description for Unicode code point U+2194: "↔" LEFT RIGHT ARROW --> + <string name="spoken_emoji_2194">Left right arrow</string> + <!-- Spoken description for Unicode code point U+2195: "↕" UP DOWN ARROW --> + <string name="spoken_emoji_2195">Up down arrow</string> + <!-- Spoken description for Unicode code point U+2196: "↖" NORTH WEST ARROW --> + <string name="spoken_emoji_2196">North west arrow</string> + <!-- Spoken description for Unicode code point U+2197: "↗" NORTH EAST ARROW --> + <string name="spoken_emoji_2197">North east arrow</string> + <!-- Spoken description for Unicode code point U+2198: "↘" SOUTH EAST ARROW --> + <string name="spoken_emoji_2198">South east arrow</string> + <!-- Spoken description for Unicode code point U+2199: "↙" SOUTH WEST ARROW --> + <string name="spoken_emoji_2199">South west arrow</string> + <!-- Spoken description for Unicode code point U+21A9: "↩" LEFTWARDS ARROW WITH HOOK --> + <string name="spoken_emoji_21A9">Leftwards arrow with hook</string> + <!-- Spoken description for Unicode code point U+21AA: "↪" RIGHTWARDS ARROW WITH HOOK --> + <string name="spoken_emoji_21AA">Rightwards arrow with hook</string> + <!-- Spoken description for Unicode code point U+231A: "⌚" WATCH --> + <string name="spoken_emoji_231A">Watch</string> + <!-- Spoken description for Unicode code point U+231B: "⌛" HOURGLASS --> + <string name="spoken_emoji_231B">Hourglass</string> + <!-- Spoken description for Unicode code point U+23E9: "⏩" BLACK RIGHT-POINTING DOUBLE TRIANGLE --> + <string name="spoken_emoji_23E9">Black right-pointing double triangle</string> + <!-- Spoken description for Unicode code point U+23EA: "⏪" BLACK LEFT-POINTING DOUBLE TRIANGLE --> + <string name="spoken_emoji_23EA">Black left-pointing double triangle</string> + <!-- Spoken description for Unicode code point U+23EB: "⏫" BLACK UP-POINTING DOUBLE TRIANGLE --> + <string name="spoken_emoji_23EB">Black up-pointing double triangle</string> + <!-- Spoken description for Unicode code point U+23EC: "⏬" BLACK DOWN-POINTING DOUBLE TRIANGLE --> + <string name="spoken_emoji_23EC">Black down-pointing double triangle</string> + <!-- Spoken description for Unicode code point U+23F0: "⏰" ALARM CLOCK --> + <string name="spoken_emoji_23F0">Alarm clock</string> + <!-- Spoken description for Unicode code point U+23F3: "⏳" HOURGLASS WITH FLOWING SAND --> + <string name="spoken_emoji_23F3">Hourglass with flowing sand</string> + <!-- Spoken description for Unicode code point U+24C2: "Ⓜ" CIRCLED LATIN CAPITAL LETTER M --> + <string name="spoken_emoji_24C2">Circled latin capital letter m</string> + <!-- Spoken description for Unicode code point U+25AA: "▪" BLACK SMALL SQUARE --> + <string name="spoken_emoji_25AA">Black small square</string> + <!-- Spoken description for Unicode code point U+25AB: "▫" WHITE SMALL SQUARE --> + <string name="spoken_emoji_25AB">White small square</string> + <!-- Spoken description for Unicode code point U+25B6: "▶" BLACK RIGHT-POINTING TRIANGLE --> + <string name="spoken_emoji_25B6">Black right-pointing triangle</string> + <!-- Spoken description for Unicode code point U+25C0: "◀" BLACK LEFT-POINTING TRIANGLE --> + <string name="spoken_emoji_25C0">Black left-pointing triangle</string> + <!-- Spoken description for Unicode code point U+25FB: "◻" WHITE MEDIUM SQUARE --> + <string name="spoken_emoji_25FB">White medium square</string> + <!-- Spoken description for Unicode code point U+25FC: "◼" BLACK MEDIUM SQUARE --> + <string name="spoken_emoji_25FC">Black medium square</string> + <!-- Spoken description for Unicode code point U+25FD: "◽" WHITE MEDIUM SMALL SQUARE --> + <string name="spoken_emoji_25FD">White medium small square</string> + <!-- Spoken description for Unicode code point U+25FE: "◾" BLACK MEDIUM SMALL SQUARE --> + <string name="spoken_emoji_25FE">Black medium small square</string> + <!-- Spoken description for Unicode code point U+2600: "☀" BLACK SUN WITH RAYS --> + <string name="spoken_emoji_2600">Black sun with rays</string> + <!-- Spoken description for Unicode code point U+2601: "☁" CLOUD --> + <string name="spoken_emoji_2601">Cloud</string> + <!-- Spoken description for Unicode code point U+260E: "☎" BLACK TELEPHONE --> + <string name="spoken_emoji_260E">Black telephone</string> + <!-- Spoken description for Unicode code point U+2611: "☑" BALLOT BOX WITH CHECK --> + <string name="spoken_emoji_2611">Ballot box with check</string> + <!-- Spoken description for Unicode code point U+2614: "☔" UMBRELLA WITH RAIN DROPS --> + <string name="spoken_emoji_2614">Umbrella with rain drops</string> + <!-- Spoken description for Unicode code point U+2615: "☕" HOT BEVERAGE --> + <string name="spoken_emoji_2615">Hot beverage</string> + <!-- Spoken description for Unicode code point U+261D: "☝" WHITE UP POINTING INDEX --> + <string name="spoken_emoji_261D">White up pointing index</string> + <!-- Spoken description for Unicode code point U+263A: "☺" WHITE SMILING FACE --> + <string name="spoken_emoji_263A">White smiling face</string> + <!-- Spoken description for Unicode code point U+2648: "♈" ARIES --> + <string name="spoken_emoji_2648">Aries</string> + <!-- Spoken description for Unicode code point U+2649: "♉" TAURUS --> + <string name="spoken_emoji_2649">Taurus</string> + <!-- Spoken description for Unicode code point U+264A: "♊" GEMINI --> + <string name="spoken_emoji_264A">Gemini</string> + <!-- Spoken description for Unicode code point U+264B: "♋" CANCER --> + <string name="spoken_emoji_264B">Cancer</string> + <!-- Spoken description for Unicode code point U+264C: "♌" LEO --> + <string name="spoken_emoji_264C">Leo</string> + <!-- Spoken description for Unicode code point U+264D: "♍" VIRGO --> + <string name="spoken_emoji_264D">Virgo</string> + <!-- Spoken description for Unicode code point U+264E: "♎" LIBRA --> + <string name="spoken_emoji_264E">Libra</string> + <!-- Spoken description for Unicode code point U+264F: "♏" SCORPIUS --> + <string name="spoken_emoji_264F">Scorpius</string> + <!-- Spoken description for Unicode code point U+2650: "♐" SAGITTARIUS --> + <string name="spoken_emoji_2650">Sagittarius</string> + <!-- Spoken description for Unicode code point U+2651: "♑" CAPRICORN --> + <string name="spoken_emoji_2651">Capricorn</string> + <!-- Spoken description for Unicode code point U+2652: "♒" AQUARIUS --> + <string name="spoken_emoji_2652">Aquarius</string> + <!-- Spoken description for Unicode code point U+2653: "♓" PISCES --> + <string name="spoken_emoji_2653">Pisces</string> + <!-- Spoken description for Unicode code point U+2660: "♠" BLACK SPADE SUIT --> + <string name="spoken_emoji_2660">Black spade suit</string> + <!-- Spoken description for Unicode code point U+2663: "♣" BLACK CLUB SUIT --> + <string name="spoken_emoji_2663">Black club suit</string> + <!-- Spoken description for Unicode code point U+2665: "♥" BLACK HEART SUIT --> + <string name="spoken_emoji_2665">Black heart suit</string> + <!-- Spoken description for Unicode code point U+2666: "♦" BLACK DIAMOND SUIT --> + <string name="spoken_emoji_2666">Black diamond suit</string> + <!-- Spoken description for Unicode code point U+2668: "♨" HOT SPRINGS --> + <string name="spoken_emoji_2668">Hot springs</string> + <!-- Spoken description for Unicode code point U+267B: "♻" BLACK UNIVERSAL RECYCLING SYMBOL --> + <string name="spoken_emoji_267B">Black universal recycling symbol</string> + <!-- Spoken description for Unicode code point U+267F: "♿" WHEELCHAIR SYMBOL --> + <string name="spoken_emoji_267F">Wheelchair symbol</string> + <!-- Spoken description for Unicode code point U+2693: "⚓" ANCHOR --> + <string name="spoken_emoji_2693">Anchor</string> + <!-- Spoken description for Unicode code point U+26A0: "⚠" WARNING SIGN --> + <string name="spoken_emoji_26A0">Warning sign</string> + <!-- Spoken description for Unicode code point U+26A1: "⚡" HIGH VOLTAGE SIGN --> + <string name="spoken_emoji_26A1">High voltage sign</string> + <!-- Spoken description for Unicode code point U+26AA: "⚪" MEDIUM WHITE CIRCLE --> + <string name="spoken_emoji_26AA">Medium white circle</string> + <!-- Spoken description for Unicode code point U+26AB: "⚫" MEDIUM BLACK CIRCLE --> + <string name="spoken_emoji_26AB">Medium black circle</string> + <!-- Spoken description for Unicode code point U+26BD: "⚽" SOCCER BALL --> + <string name="spoken_emoji_26BD">Soccer ball</string> + <!-- Spoken description for Unicode code point U+26BE: "⚾" BASEBALL --> + <string name="spoken_emoji_26BE">Baseball</string> + <!-- Spoken description for Unicode code point U+26C4: "⛄" SNOWMAN WITHOUT SNOW --> + <string name="spoken_emoji_26C4">Snowman without snow</string> + <!-- Spoken description for Unicode code point U+26C5: "⛅" SUN BEHIND CLOUD --> + <string name="spoken_emoji_26C5">Sun behind cloud</string> + <!-- Spoken description for Unicode code point U+26CE: "⛎" OPHIUCHUS --> + <string name="spoken_emoji_26CE">Ophiuchus</string> + <!-- Spoken description for Unicode code point U+26D4: "⛔" NO ENTRY --> + <string name="spoken_emoji_26D4">No entry</string> + <!-- Spoken description for Unicode code point U+26EA: "⛪" CHURCH --> + <string name="spoken_emoji_26EA">Church</string> + <!-- Spoken description for Unicode code point U+26F2: "⛲" FOUNTAIN --> + <string name="spoken_emoji_26F2">Fountain</string> + <!-- Spoken description for Unicode code point U+26F3: "⛳" FLAG IN HOLE --> + <string name="spoken_emoji_26F3">Flag in hole</string> + <!-- Spoken description for Unicode code point U+26F5: "⛵" SAILBOAT --> + <string name="spoken_emoji_26F5">Sailboat</string> + <!-- Spoken description for Unicode code point U+26FA: "⛺" TENT --> + <string name="spoken_emoji_26FA">Tent</string> + <!-- Spoken description for Unicode code point U+26FD: "⛽" FUEL PUMP --> + <string name="spoken_emoji_26FD">Fuel pump</string> + <!-- Spoken description for Unicode code point U+2702: "✂" BLACK SCISSORS --> + <string name="spoken_emoji_2702">Black scissors</string> + <!-- Spoken description for Unicode code point U+2705: "✅" WHITE HEAVY CHECK MARK --> + <string name="spoken_emoji_2705">White heavy check mark</string> + <!-- Spoken description for Unicode code point U+2708: "✈" AIRPLANE --> + <string name="spoken_emoji_2708">Airplane</string> + <!-- Spoken description for Unicode code point U+2709: "✉" ENVELOPE --> + <string name="spoken_emoji_2709">Envelope</string> + <!-- Spoken description for Unicode code point U+270A: "✊" RAISED FIST --> + <string name="spoken_emoji_270A">Raised fist</string> + <!-- Spoken description for Unicode code point U+270B: "✋" RAISED HAND --> + <string name="spoken_emoji_270B">Raised hand</string> + <!-- Spoken description for Unicode code point U+270C: "✌" VICTORY HAND --> + <string name="spoken_emoji_270C">Victory hand</string> + <!-- Spoken description for Unicode code point U+270F: "✏" PENCIL --> + <string name="spoken_emoji_270F">Pencil</string> + <!-- Spoken description for Unicode code point U+2712: "✒" BLACK NIB --> + <string name="spoken_emoji_2712">Black nib</string> + <!-- Spoken description for Unicode code point U+2714: "✔" HEAVY CHECK MARK --> + <string name="spoken_emoji_2714">Heavy check mark</string> + <!-- Spoken description for Unicode code point U+2716: "✖" HEAVY MULTIPLICATION X --> + <string name="spoken_emoji_2716">Heavy multiplication x</string> + <!-- Spoken description for Unicode code point U+2728: "✨" SPARKLES --> + <string name="spoken_emoji_2728">Sparkles</string> + <!-- Spoken description for Unicode code point U+2733: "✳" EIGHT SPOKED ASTERISK --> + <string name="spoken_emoji_2733">Eight spoked asterisk</string> + <!-- Spoken description for Unicode code point U+2734: "✴" EIGHT POINTED BLACK STAR --> + <string name="spoken_emoji_2734">Eight pointed black star</string> + <!-- Spoken description for Unicode code point U+2744: "❄" SNOWFLAKE --> + <string name="spoken_emoji_2744">Snowflake</string> + <!-- Spoken description for Unicode code point U+2747: "❇" SPARKLE --> + <string name="spoken_emoji_2747">Sparkle</string> + <!-- Spoken description for Unicode code point U+274C: "❌" CROSS MARK --> + <string name="spoken_emoji_274C">Cross mark</string> + <!-- Spoken description for Unicode code point U+274E: "❎" NEGATIVE SQUARED CROSS MARK --> + <string name="spoken_emoji_274E">Negative squared cross mark</string> + <!-- Spoken description for Unicode code point U+2753: "❓" BLACK QUESTION MARK ORNAMENT --> + <string name="spoken_emoji_2753">Black question mark ornament</string> + <!-- Spoken description for Unicode code point U+2754: "❔" WHITE QUESTION MARK ORNAMENT --> + <string name="spoken_emoji_2754">White question mark ornament</string> + <!-- Spoken description for Unicode code point U+2755: "❕" WHITE EXCLAMATION MARK ORNAMENT --> + <string name="spoken_emoji_2755">White exclamation mark ornament</string> + <!-- Spoken description for Unicode code point U+2757: "❗" HEAVY EXCLAMATION MARK SYMBOL --> + <string name="spoken_emoji_2757">Heavy exclamation mark symbol</string> + <!-- Spoken description for Unicode code point U+2764: "❤" HEAVY BLACK HEART --> + <string name="spoken_emoji_2764">Heavy black heart</string> + <!-- Spoken description for Unicode code point U+2795: "➕" HEAVY PLUS SIGN --> + <string name="spoken_emoji_2795">Heavy plus sign</string> + <!-- Spoken description for Unicode code point U+2796: "➖" HEAVY MINUS SIGN --> + <string name="spoken_emoji_2796">Heavy minus sign</string> + <!-- Spoken description for Unicode code point U+2797: "➗" HEAVY DIVISION SIGN --> + <string name="spoken_emoji_2797">Heavy division sign</string> + <!-- Spoken description for Unicode code point U+27A1: "➡" BLACK RIGHTWARDS ARROW --> + <string name="spoken_emoji_27A1">Black rightwards arrow</string> + <!-- Spoken description for Unicode code point U+27B0: "➰" CURLY LOOP --> + <string name="spoken_emoji_27B0">Curly loop</string> + <!-- Spoken description for Unicode code point U+27BF: "➿" DOUBLE CURLY LOOP --> + <string name="spoken_emoji_27BF">Double curly loop</string> + <!-- Spoken description for Unicode code point U+2934: "⤴" ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS --> + <string name="spoken_emoji_2934">Arrow pointing rightwards then curving upwards</string> + <!-- Spoken description for Unicode code point U+2935: "⤵" ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS --> + <string name="spoken_emoji_2935">Arrow pointing rightwards then curving downwards</string> + <!-- Spoken description for Unicode code point U+2B05: "⬅" LEFTWARDS BLACK ARROW --> + <string name="spoken_emoji_2B05">Leftwards black arrow</string> + <!-- Spoken description for Unicode code point U+2B06: "⬆" UPWARDS BLACK ARROW --> + <string name="spoken_emoji_2B06">Upwards black arrow</string> + <!-- Spoken description for Unicode code point U+2B07: "⬇" DOWNWARDS BLACK ARROW --> + <string name="spoken_emoji_2B07">Downwards black arrow</string> + <!-- Spoken description for Unicode code point U+2B1B: "⬛" BLACK LARGE SQUARE --> + <string name="spoken_emoji_2B1B">Black large square</string> + <!-- Spoken description for Unicode code point U+2B1C: "⬜" WHITE LARGE SQUARE --> + <string name="spoken_emoji_2B1C">White large square</string> + <!-- Spoken description for Unicode code point U+2B50: "⭐" WHITE MEDIUM STAR --> + <string name="spoken_emoji_2B50">White medium star</string> + <!-- Spoken description for Unicode code point U+2B55: "⭕" HEAVY LARGE CIRCLE --> + <string name="spoken_emoji_2B55">Heavy large circle</string> + <!-- Spoken description for Unicode code point U+3030: "〰" WAVY DASH --> + <string name="spoken_emoji_3030">Wavy dash</string> + <!-- Spoken description for Unicode code point U+303D: "〽" PART ALTERNATION MARK --> + <string name="spoken_emoji_303D">Part alternation mark</string> + <!-- Spoken description for Unicode code point U+3297: "㊗" CIRCLED IDEOGRAPH CONGRATULATION --> + <string name="spoken_emoji_3297">Circled ideograph congratulation</string> + <!-- Spoken description for Unicode code point U+3299: "㊙" CIRCLED IDEOGRAPH SECRET --> + <string name="spoken_emoji_3299">Circled ideograph secret</string> + <!-- Spoken description for Unicode code point U+1F004: "🀄" MAHJONG TILE RED DRAGON --> + <string name="spoken_emoji_1F004">Mahjong tile red dragon</string> + <!-- Spoken description for Unicode code point U+1F0CF: "🃏" PLAYING CARD BLACK JOKER --> + <string name="spoken_emoji_1F0CF">Playing card black joker</string> + <!-- Spoken description for Unicode code point U+1F170: "🅰" NEGATIVE SQUARED LATIN CAPITAL LETTER A --> + <string name="spoken_emoji_1F170">Blood type A</string> + <!-- Spoken description for Unicode code point U+1F171: "🅱" NEGATIVE SQUARED LATIN CAPITAL LETTER B --> + <string name="spoken_emoji_1F171">Blood type B</string> + <!-- Spoken description for Unicode code point U+1F17E: "🅾" NEGATIVE SQUARED LATIN CAPITAL LETTER O --> + <string name="spoken_emoji_1F17E">Blood type O</string> + <!-- Spoken description for Unicode code point U+1F17F: "🅿" NEGATIVE SQUARED LATIN CAPITAL LETTER P --> + <string name="spoken_emoji_1F17F">Parking lot</string> + <!-- Spoken description for Unicode code point U+1F18E: "🆎" NEGATIVE SQUARED AB --> + <string name="spoken_emoji_1F18E">Blood type AB</string> + <!-- Spoken description for Unicode code point U+1F191: "🆑" SQUARED CL --> + <string name="spoken_emoji_1F191">Squared CL</string> + <!-- Spoken description for Unicode code point U+1F192: "🆒" SQUARED COOL --> + <string name="spoken_emoji_1F192">Squared cool</string> + <!-- Spoken description for Unicode code point U+1F193: "🆓" SQUARED FREE --> + <string name="spoken_emoji_1F193">Squared free</string> + <!-- Spoken description for Unicode code point U+1F194: "🆔" SQUARED ID --> + <string name="spoken_emoji_1F194">Squared ID</string> + <!-- Spoken description for Unicode code point U+1F195: "🆕" SQUARED NEW --> + <string name="spoken_emoji_1F195">Squared new</string> + <!-- Spoken description for Unicode code point U+1F196: "🆖" SQUARED NG --> + <string name="spoken_emoji_1F196">Squared N G</string> + <!-- Spoken description for Unicode code point U+1F197: "🆗" SQUARED OK --> + <string name="spoken_emoji_1F197">Squared OK</string> + <!-- Spoken description for Unicode code point U+1F198: "🆘" SQUARED SOS --> + <string name="spoken_emoji_1F198">Squared SOS</string> + <!-- Spoken description for Unicode code point U+1F199: "🆙" SQUARED UP WITH EXCLAMATION MARK --> + <string name="spoken_emoji_1F199">Squared up with exclamation mark</string> + <!-- Spoken description for Unicode code point U+1F19A: "🆚" SQUARED VS --> + <string name="spoken_emoji_1F19A">Squared vs</string> + <!-- Spoken description for Unicode code point U+1F201: "🈁" SQUARED KATAKANA KOKO --> + <string name="spoken_emoji_1F201">Squared katakana here</string> + <!-- Spoken description for Unicode code point U+1F202: "🈂" SQUARED KATAKANA SA --> + <string name="spoken_emoji_1F202">Squared katakana service</string> + <!-- Spoken description for Unicode code point U+1F21A: "🈚" SQUARED CJK UNIFIED IDEOGRAPH-7121 --> + <string name="spoken_emoji_1F21A">Squared ideograph charge-free</string> + <!-- Spoken description for Unicode code point U+1F22F: "🈯" SQUARED CJK UNIFIED IDEOGRAPH-6307 --> + <string name="spoken_emoji_1F22F">Squared ideograph reserved-seat</string> + <!-- Spoken description for Unicode code point U+1F232: "🈲" SQUARED CJK UNIFIED IDEOGRAPH-7981 --> + <string name="spoken_emoji_1F232">Squared ideograph prohibitation</string> + <!-- Spoken description for Unicode code point U+1F233: "🈳" SQUARED CJK UNIFIED IDEOGRAPH-7A7A --> + <string name="spoken_emoji_1F233">Squared ideograph vacancy</string> + <!-- Spoken description for Unicode code point U+1F234: "🈴" SQUARED CJK UNIFIED IDEOGRAPH-5408 --> + <string name="spoken_emoji_1F234">Squared ideograph acceptance</string> + <!-- Spoken description for Unicode code point U+1F235: "🈵" SQUARED CJK UNIFIED IDEOGRAPH-6E80 --> + <string name="spoken_emoji_1F235">Squared ideograph full occupancy</string> + <!-- Spoken description for Unicode code point U+1F236: "🈶" SQUARED CJK UNIFIED IDEOGRAPH-6709 --> + <string name="spoken_emoji_1F236">Squared ideograph paid</string> + <!-- Spoken description for Unicode code point U+1F237: "🈷" SQUARED CJK UNIFIED IDEOGRAPH-6708 --> + <string name="spoken_emoji_1F237">Squared ideograph monthly</string> + <!-- Spoken description for Unicode code point U+1F238: "🈸" SQUARED CJK UNIFIED IDEOGRAPH-7533 --> + <string name="spoken_emoji_1F238">Squared ideograph application</string> + <!-- Spoken description for Unicode code point U+1F239: "🈹" SQUARED CJK UNIFIED IDEOGRAPH-5272 --> + <string name="spoken_emoji_1F239">Squared ideograph discount</string> + <!-- Spoken description for Unicode code point U+1F23A: "🈺" SQUARED CJK UNIFIED IDEOGRAPH-55B6 --> + <string name="spoken_emoji_1F23A">Squared ideograph in business</string> + <!-- Spoken description for Unicode code point U+1F250: "🉐" CIRCLED IDEOGRAPH ADVANTAGE --> + <string name="spoken_emoji_1F250">Circled ideograph advantage</string> + <!-- Spoken description for Unicode code point U+1F251: "🉑" CIRCLED IDEOGRAPH ACCEPT --> + <string name="spoken_emoji_1F251">Circled ideograph accept</string> + <!-- Spoken description for Unicode code point U+1F300: "🌀" CYCLONE --> + <string name="spoken_emoji_1F300">Cyclone</string> + <!-- Spoken description for Unicode code point U+1F301: "🌁" FOGGY --> + <string name="spoken_emoji_1F301">Foggy</string> + <!-- Spoken description for Unicode code point U+1F302: "🌂" CLOSED UMBRELLA --> + <string name="spoken_emoji_1F302">Closed umbrella</string> + <!-- Spoken description for Unicode code point U+1F303: "🌃" NIGHT WITH STARS --> + <string name="spoken_emoji_1F303">Night with stars</string> + <!-- Spoken description for Unicode code point U+1F304: "🌄" SUNRISE OVER MOUNTAINS --> + <string name="spoken_emoji_1F304">Sunrise over mountains</string> + <!-- Spoken description for Unicode code point U+1F305: "🌅" SUNRISE --> + <string name="spoken_emoji_1F305">Sunrise</string> + <!-- Spoken description for Unicode code point U+1F306: "🌆" CITYSCAPE AT DUSK --> + <string name="spoken_emoji_1F306">Cityscape at dusk</string> + <!-- Spoken description for Unicode code point U+1F307: "🌇" SUNSET OVER BUILDINGS --> + <string name="spoken_emoji_1F307">Sunset over buildings</string> + <!-- Spoken description for Unicode code point U+1F308: "🌈" RAINBOW --> + <string name="spoken_emoji_1F308">Rainbow</string> + <!-- Spoken description for Unicode code point U+1F309: "🌉" BRIDGE AT NIGHT --> + <string name="spoken_emoji_1F309">Bridge at night</string> + <!-- Spoken description for Unicode code point U+1F30A: "🌊" WATER WAVE --> + <string name="spoken_emoji_1F30A">Water wave</string> + <!-- Spoken description for Unicode code point U+1F30B: "🌋" VOLCANO --> + <string name="spoken_emoji_1F30B">Volcano</string> + <!-- Spoken description for Unicode code point U+1F30C: "🌌" MILKY WAY --> + <string name="spoken_emoji_1F30C">Milky way</string> + <!-- Spoken description for Unicode code point U+1F30D: "🌍" EARTH GLOBE EUROPE-AFRICA --> + <string name="spoken_emoji_1F30D">Earth globe europe-africa</string> + <!-- Spoken description for Unicode code point U+1F30E: "🌎" EARTH GLOBE AMERICAS --> + <string name="spoken_emoji_1F30E">Earth globe americas</string> + <!-- Spoken description for Unicode code point U+1F30F: "🌏" EARTH GLOBE ASIA-AUSTRALIA --> + <string name="spoken_emoji_1F30F">Earth globe asia-australia</string> + <!-- Spoken description for Unicode code point U+1F310: "🌐" GLOBE WITH MERIDIANS --> + <string name="spoken_emoji_1F310">Globe with meridians</string> + <!-- Spoken description for Unicode code point U+1F311: "🌑" NEW MOON SYMBOL --> + <string name="spoken_emoji_1F311">New moon symbol</string> + <!-- Spoken description for Unicode code point U+1F312: "🌒" WAXING CRESCENT MOON SYMBOL --> + <string name="spoken_emoji_1F312">Waxing crescent moon symbol</string> + <!-- Spoken description for Unicode code point U+1F313: "🌓" FIRST QUARTER MOON SYMBOL --> + <string name="spoken_emoji_1F313">First quarter moon symbol</string> + <!-- Spoken description for Unicode code point U+1F314: "🌔" WAXING GIBBOUS MOON SYMBOL --> + <string name="spoken_emoji_1F314">Waxing gibbous moon symbol</string> + <!-- Spoken description for Unicode code point U+1F315: "🌕" FULL MOON SYMBOL --> + <string name="spoken_emoji_1F315">Full moon symbol</string> + <!-- Spoken description for Unicode code point U+1F316: "🌖" WANING GIBBOUS MOON SYMBOL --> + <string name="spoken_emoji_1F316">Waning gibbous moon symbol</string> + <!-- Spoken description for Unicode code point U+1F317: "🌗" LAST QUARTER MOON SYMBOL --> + <string name="spoken_emoji_1F317">Last quarter moon symbol</string> + <!-- Spoken description for Unicode code point U+1F318: "🌘" WANING CRESCENT MOON SYMBOL --> + <string name="spoken_emoji_1F318">Waning crescent moon symbol</string> + <!-- Spoken description for Unicode code point U+1F319: "🌙" CRESCENT MOON --> + <string name="spoken_emoji_1F319">Crescent moon</string> + <!-- Spoken description for Unicode code point U+1F31A: "🌚" NEW MOON WITH FACE --> + <string name="spoken_emoji_1F31A">New moon with face</string> + <!-- Spoken description for Unicode code point U+1F31B: "🌛" FIRST QUARTER MOON WITH FACE --> + <string name="spoken_emoji_1F31B">First quarter moon with face</string> + <!-- Spoken description for Unicode code point U+1F31C: "🌜" LAST QUARTER MOON WITH FACE --> + <string name="spoken_emoji_1F31C">Last quarter moon with face</string> + <!-- Spoken description for Unicode code point U+1F31D: "🌝" FULL MOON WITH FACE --> + <string name="spoken_emoji_1F31D">Full moon with face</string> + <!-- Spoken description for Unicode code point U+1F31E: "🌞" SUN WITH FACE --> + <string name="spoken_emoji_1F31E">Sun with face</string> + <!-- Spoken description for Unicode code point U+1F31F: "🌟" GLOWING STAR --> + <string name="spoken_emoji_1F31F">Glowing star</string> + <!-- Spoken description for Unicode code point U+1F320: "🌠" SHOOTING STAR --> + <string name="spoken_emoji_1F320">Shooting star</string> + <!-- Spoken description for Unicode code point U+1F330: "🌰" CHESTNUT --> + <string name="spoken_emoji_1F330">Chestnut</string> + <!-- Spoken description for Unicode code point U+1F331: "🌱" SEEDLING --> + <string name="spoken_emoji_1F331">Seedling</string> + <!-- Spoken description for Unicode code point U+1F332: "🌲" EVERGREEN TREE --> + <string name="spoken_emoji_1F332">Evergreen tree</string> + <!-- Spoken description for Unicode code point U+1F333: "🌳" DECIDUOUS TREE --> + <string name="spoken_emoji_1F333">Deciduous tree</string> + <!-- Spoken description for Unicode code point U+1F334: "🌴" PALM TREE --> + <string name="spoken_emoji_1F334">Palm tree</string> + <!-- Spoken description for Unicode code point U+1F335: "🌵" CACTUS --> + <string name="spoken_emoji_1F335">Cactus</string> + <!-- Spoken description for Unicode code point U+1F337: "🌷" TULIP --> + <string name="spoken_emoji_1F337">Tulip</string> + <!-- Spoken description for Unicode code point U+1F338: "🌸" CHERRY BLOSSOM --> + <string name="spoken_emoji_1F338">Cherry blossom</string> + <!-- Spoken description for Unicode code point U+1F339: "🌹" ROSE --> + <string name="spoken_emoji_1F339">Rose</string> + <!-- Spoken description for Unicode code point U+1F33A: "🌺" HIBISCUS --> + <string name="spoken_emoji_1F33A">Hibiscus</string> + <!-- Spoken description for Unicode code point U+1F33B: "🌻" SUNFLOWER --> + <string name="spoken_emoji_1F33B">Sunflower</string> + <!-- Spoken description for Unicode code point U+1F33C: "🌼" BLOSSOM --> + <string name="spoken_emoji_1F33C">Blossom</string> + <!-- Spoken description for Unicode code point U+1F33D: "🌽" EAR OF MAIZE --> + <string name="spoken_emoji_1F33D">Ear of maize</string> + <!-- Spoken description for Unicode code point U+1F33E: "🌾" EAR OF RICE --> + <string name="spoken_emoji_1F33E">Ear of rice</string> + <!-- Spoken description for Unicode code point U+1F33F: "🌿" HERB --> + <string name="spoken_emoji_1F33F">Herb</string> + <!-- Spoken description for Unicode code point U+1F340: "🍀" FOUR LEAF CLOVER --> + <string name="spoken_emoji_1F340">Four leaf clover</string> + <!-- Spoken description for Unicode code point U+1F341: "🍁" MAPLE LEAF --> + <string name="spoken_emoji_1F341">Maple leaf</string> + <!-- Spoken description for Unicode code point U+1F342: "🍂" FALLEN LEAF --> + <string name="spoken_emoji_1F342">Fallen leaf</string> + <!-- Spoken description for Unicode code point U+1F343: "🍃" LEAF FLUTTERING IN WIND --> + <string name="spoken_emoji_1F343">Leaf fluttering in wind</string> + <!-- Spoken description for Unicode code point U+1F344: "🍄" MUSHROOM --> + <string name="spoken_emoji_1F344">Mushroom</string> + <!-- Spoken description for Unicode code point U+1F345: "🍅" TOMATO --> + <string name="spoken_emoji_1F345">Tomato</string> + <!-- Spoken description for Unicode code point U+1F346: "🍆" AUBERGINE --> + <string name="spoken_emoji_1F346">Aubergine</string> + <!-- Spoken description for Unicode code point U+1F347: "🍇" GRAPES --> + <string name="spoken_emoji_1F347">Grapes</string> + <!-- Spoken description for Unicode code point U+1F348: "🍈" MELON --> + <string name="spoken_emoji_1F348">Melon</string> + <!-- Spoken description for Unicode code point U+1F349: "🍉" WATERMELON --> + <string name="spoken_emoji_1F349">Watermelon</string> + <!-- Spoken description for Unicode code point U+1F34A: "🍊" TANGERINE --> + <string name="spoken_emoji_1F34A">Tangerine</string> + <!-- Spoken description for Unicode code point U+1F34B: "🍋" LEMON --> + <string name="spoken_emoji_1F34B">Lemon</string> + <!-- Spoken description for Unicode code point U+1F34C: "🍌" BANANA --> + <string name="spoken_emoji_1F34C">Banana</string> + <!-- Spoken description for Unicode code point U+1F34D: "🍍" PINEAPPLE --> + <string name="spoken_emoji_1F34D">Pineapple</string> + <!-- Spoken description for Unicode code point U+1F34E: "🍎" RED APPLE --> + <string name="spoken_emoji_1F34E">Red apple</string> + <!-- Spoken description for Unicode code point U+1F34F: "🍏" GREEN APPLE --> + <string name="spoken_emoji_1F34F">Green apple</string> + <!-- Spoken description for Unicode code point U+1F350: "🍐" PEAR --> + <string name="spoken_emoji_1F350">Pear</string> + <!-- Spoken description for Unicode code point U+1F351: "🍑" PEACH --> + <string name="spoken_emoji_1F351">Peach</string> + <!-- Spoken description for Unicode code point U+1F352: "🍒" CHERRIES --> + <string name="spoken_emoji_1F352">Cherries</string> + <!-- Spoken description for Unicode code point U+1F353: "🍓" STRAWBERRY --> + <string name="spoken_emoji_1F353">Strawberry</string> + <!-- Spoken description for Unicode code point U+1F354: "🍔" HAMBURGER --> + <string name="spoken_emoji_1F354">Hamburger</string> + <!-- Spoken description for Unicode code point U+1F355: "🍕" SLICE OF PIZZA --> + <string name="spoken_emoji_1F355">Slice of pizza</string> + <!-- Spoken description for Unicode code point U+1F356: "🍖" MEAT ON BONE --> + <string name="spoken_emoji_1F356">Meat on bone</string> + <!-- Spoken description for Unicode code point U+1F357: "🍗" POULTRY LEG --> + <string name="spoken_emoji_1F357">Poultry leg</string> + <!-- Spoken description for Unicode code point U+1F358: "🍘" RICE CRACKER --> + <string name="spoken_emoji_1F358">Rice cracker</string> + <!-- Spoken description for Unicode code point U+1F359: "🍙" RICE BALL --> + <string name="spoken_emoji_1F359">Rice ball</string> + <!-- Spoken description for Unicode code point U+1F35A: "🍚" COOKED RICE --> + <string name="spoken_emoji_1F35A">Cooked rice</string> + <!-- Spoken description for Unicode code point U+1F35B: "🍛" CURRY AND RICE --> + <string name="spoken_emoji_1F35B">Curry and rice</string> + <!-- Spoken description for Unicode code point U+1F35C: "🍜" STEAMING BOWL --> + <string name="spoken_emoji_1F35C">Steaming bowl</string> + <!-- Spoken description for Unicode code point U+1F35D: "🍝" SPAGHETTI --> + <string name="spoken_emoji_1F35D">Spaghetti</string> + <!-- Spoken description for Unicode code point U+1F35E: "🍞" BREAD --> + <string name="spoken_emoji_1F35E">Bread</string> + <!-- Spoken description for Unicode code point U+1F35F: "🍟" FRENCH FRIES --> + <string name="spoken_emoji_1F35F">French fries</string> + <!-- Spoken description for Unicode code point U+1F360: "🍠" ROASTED SWEET POTATO --> + <string name="spoken_emoji_1F360">Roasted sweet potato</string> + <!-- Spoken description for Unicode code point U+1F361: "🍡" DANGO --> + <string name="spoken_emoji_1F361">Dango</string> + <!-- Spoken description for Unicode code point U+1F362: "🍢" ODEN --> + <string name="spoken_emoji_1F362">Oden</string> + <!-- Spoken description for Unicode code point U+1F363: "🍣" SUSHI --> + <string name="spoken_emoji_1F363">Sushi</string> + <!-- Spoken description for Unicode code point U+1F364: "🍤" FRIED SHRIMP --> + <string name="spoken_emoji_1F364">Fried shrimp</string> + <!-- Spoken description for Unicode code point U+1F365: "🍥" FISH CAKE WITH SWIRL DESIGN --> + <string name="spoken_emoji_1F365">Fish cake with swirl design</string> + <!-- Spoken description for Unicode code point U+1F366: "🍦" SOFT ICE CREAM --> + <string name="spoken_emoji_1F366">Soft ice cream</string> + <!-- Spoken description for Unicode code point U+1F367: "🍧" SHAVED ICE --> + <string name="spoken_emoji_1F367">Shaved ice</string> + <!-- Spoken description for Unicode code point U+1F368: "🍨" ICE CREAM --> + <string name="spoken_emoji_1F368">Ice cream</string> + <!-- Spoken description for Unicode code point U+1F369: "🍩" DOUGHNUT --> + <string name="spoken_emoji_1F369">Doughnut</string> + <!-- Spoken description for Unicode code point U+1F36A: "🍪" COOKIE --> + <string name="spoken_emoji_1F36A">Cookie</string> + <!-- Spoken description for Unicode code point U+1F36B: "🍫" CHOCOLATE BAR --> + <string name="spoken_emoji_1F36B">Chocolate bar</string> + <!-- Spoken description for Unicode code point U+1F36C: "🍬" CANDY --> + <string name="spoken_emoji_1F36C">Candy</string> + <!-- Spoken description for Unicode code point U+1F36D: "🍭" LOLLIPOP --> + <string name="spoken_emoji_1F36D">Lollipop</string> + <!-- Spoken description for Unicode code point U+1F36E: "🍮" CUSTARD --> + <string name="spoken_emoji_1F36E">Custard</string> + <!-- Spoken description for Unicode code point U+1F36F: "🍯" HONEY POT --> + <string name="spoken_emoji_1F36F">Honey pot</string> + <!-- Spoken description for Unicode code point U+1F370: "🍰" SHORTCAKE --> + <string name="spoken_emoji_1F370">Shortcake</string> + <!-- Spoken description for Unicode code point U+1F371: "🍱" BENTO BOX --> + <string name="spoken_emoji_1F371">Bento box</string> + <!-- Spoken description for Unicode code point U+1F372: "🍲" POT OF FOOD --> + <string name="spoken_emoji_1F372">Pot of food</string> + <!-- Spoken description for Unicode code point U+1F373: "🍳" COOKING --> + <string name="spoken_emoji_1F373">Cooking</string> + <!-- Spoken description for Unicode code point U+1F374: "🍴" FORK AND KNIFE --> + <string name="spoken_emoji_1F374">Fork and knife</string> + <!-- Spoken description for Unicode code point U+1F375: "🍵" TEACUP WITHOUT HANDLE --> + <string name="spoken_emoji_1F375">Teacup without handle</string> + <!-- Spoken description for Unicode code point U+1F376: "🍶" SAKE BOTTLE AND CUP --> + <string name="spoken_emoji_1F376">Sake bottle and cup</string> + <!-- Spoken description for Unicode code point U+1F377: "🍷" WINE GLASS --> + <string name="spoken_emoji_1F377">Wine glass</string> + <!-- Spoken description for Unicode code point U+1F378: "🍸" COCKTAIL GLASS --> + <string name="spoken_emoji_1F378">Cocktail glass</string> + <!-- Spoken description for Unicode code point U+1F379: "🍹" TROPICAL DRINK --> + <string name="spoken_emoji_1F379">Tropical drink</string> + <!-- Spoken description for Unicode code point U+1F37A: "🍺" BEER MUG --> + <string name="spoken_emoji_1F37A">Beer mug</string> + <!-- Spoken description for Unicode code point U+1F37B: "🍻" CLINKING BEER MUGS --> + <string name="spoken_emoji_1F37B">Clinking beer mugs</string> + <!-- Spoken description for Unicode code point U+1F37C: "🍼" BABY BOTTLE --> + <string name="spoken_emoji_1F37C">Baby bottle</string> + <!-- Spoken description for Unicode code point U+1F380: "🎀" RIBBON --> + <string name="spoken_emoji_1F380">Ribbon</string> + <!-- Spoken description for Unicode code point U+1F381: "🎁" WRAPPED PRESENT --> + <string name="spoken_emoji_1F381">Wrapped present</string> + <!-- Spoken description for Unicode code point U+1F382: "🎂" BIRTHDAY CAKE --> + <string name="spoken_emoji_1F382">Birthday cake</string> + <!-- Spoken description for Unicode code point U+1F383: "🎃" JACK-O-LANTERN --> + <string name="spoken_emoji_1F383">Jack-o-lantern</string> + <!-- Spoken description for Unicode code point U+1F384: "🎄" CHRISTMAS TREE --> + <string name="spoken_emoji_1F384">Christmas tree</string> + <!-- Spoken description for Unicode code point U+1F385: "🎅" FATHER CHRISTMAS --> + <string name="spoken_emoji_1F385">Father christmas</string> + <!-- Spoken description for Unicode code point U+1F386: "🎆" FIREWORKS --> + <string name="spoken_emoji_1F386">Fireworks</string> + <!-- Spoken description for Unicode code point U+1F387: "🎇" FIREWORK SPARKLER --> + <string name="spoken_emoji_1F387">Firework sparkler</string> + <!-- Spoken description for Unicode code point U+1F388: "🎈" BALLOON --> + <string name="spoken_emoji_1F388">Balloon</string> + <!-- Spoken description for Unicode code point U+1F389: "🎉" PARTY POPPER --> + <string name="spoken_emoji_1F389">Party popper</string> + <!-- Spoken description for Unicode code point U+1F38A: "🎊" CONFETTI BALL --> + <string name="spoken_emoji_1F38A">Confetti ball</string> + <!-- Spoken description for Unicode code point U+1F38B: "🎋" TANABATA TREE --> + <string name="spoken_emoji_1F38B">Tanabata tree</string> + <!-- Spoken description for Unicode code point U+1F38C: "🎌" CROSSED FLAGS --> + <string name="spoken_emoji_1F38C">Crossed flags</string> + <!-- Spoken description for Unicode code point U+1F38D: "🎍" PINE DECORATION --> + <string name="spoken_emoji_1F38D">Pine decoration</string> + <!-- Spoken description for Unicode code point U+1F38E: "🎎" JAPANESE DOLLS --> + <string name="spoken_emoji_1F38E">Japanese dolls</string> + <!-- Spoken description for Unicode code point U+1F38F: "🎏" CARP STREAMER --> + <string name="spoken_emoji_1F38F">Carp streamer</string> + <!-- Spoken description for Unicode code point U+1F390: "🎐" WIND CHIME --> + <string name="spoken_emoji_1F390">Wind chime</string> + <!-- Spoken description for Unicode code point U+1F391: "🎑" MOON VIEWING CEREMONY --> + <string name="spoken_emoji_1F391">Moon viewing ceremony</string> + <!-- Spoken description for Unicode code point U+1F392: "🎒" SCHOOL SATCHEL --> + <string name="spoken_emoji_1F392">School satchel</string> + <!-- Spoken description for Unicode code point U+1F393: "🎓" GRADUATION CAP --> + <string name="spoken_emoji_1F393">Graduation cap</string> + <!-- Spoken description for Unicode code point U+1F3A0: "🎠" CAROUSEL HORSE --> + <string name="spoken_emoji_1F3A0">Carousel horse</string> + <!-- Spoken description for Unicode code point U+1F3A1: "🎡" FERRIS WHEEL --> + <string name="spoken_emoji_1F3A1">Ferris wheel</string> + <!-- Spoken description for Unicode code point U+1F3A2: "🎢" ROLLER COASTER --> + <string name="spoken_emoji_1F3A2">Roller coaster</string> + <!-- Spoken description for Unicode code point U+1F3A3: "🎣" FISHING POLE AND FISH --> + <string name="spoken_emoji_1F3A3">Fishing pole and fish</string> + <!-- Spoken description for Unicode code point U+1F3A4: "🎤" MICROPHONE --> + <string name="spoken_emoji_1F3A4">Microphone</string> + <!-- Spoken description for Unicode code point U+1F3A5: "🎥" MOVIE CAMERA --> + <string name="spoken_emoji_1F3A5">Movie camera</string> + <!-- Spoken description for Unicode code point U+1F3A6: "🎦" CINEMA --> + <string name="spoken_emoji_1F3A6">Cinema</string> + <!-- Spoken description for Unicode code point U+1F3A7: "🎧" HEADPHONE --> + <string name="spoken_emoji_1F3A7">Headphone</string> + <!-- Spoken description for Unicode code point U+1F3A8: "🎨" ARTIST PALETTE --> + <string name="spoken_emoji_1F3A8">Artist palette</string> + <!-- Spoken description for Unicode code point U+1F3A9: "🎩" TOP HAT --> + <string name="spoken_emoji_1F3A9">Top hat</string> + <!-- Spoken description for Unicode code point U+1F3AA: "🎪" CIRCUS TENT --> + <string name="spoken_emoji_1F3AA">Circus tent</string> + <!-- Spoken description for Unicode code point U+1F3AB: "🎫" TICKET --> + <string name="spoken_emoji_1F3AB">Ticket</string> + <!-- Spoken description for Unicode code point U+1F3AC: "🎬" CLAPPER BOARD --> + <string name="spoken_emoji_1F3AC">Clapper board</string> + <!-- Spoken description for Unicode code point U+1F3AD: "🎭" PERFORMING ARTS --> + <string name="spoken_emoji_1F3AD">Performing arts</string> + <!-- Spoken description for Unicode code point U+1F3AE: "🎮" VIDEO GAME --> + <string name="spoken_emoji_1F3AE">Video game</string> + <!-- Spoken description for Unicode code point U+1F3AF: "🎯" DIRECT HIT --> + <string name="spoken_emoji_1F3AF">Direct hit</string> + <!-- Spoken description for Unicode code point U+1F3B0: "🎰" SLOT MACHINE --> + <string name="spoken_emoji_1F3B0">Slot machine</string> + <!-- Spoken description for Unicode code point U+1F3B1: "🎱" BILLIARDS --> + <string name="spoken_emoji_1F3B1">Billiards</string> + <!-- Spoken description for Unicode code point U+1F3B2: "🎲" GAME DIE --> + <string name="spoken_emoji_1F3B2">Game die</string> + <!-- Spoken description for Unicode code point U+1F3B3: "🎳" BOWLING --> + <string name="spoken_emoji_1F3B3">Bowling</string> + <!-- Spoken description for Unicode code point U+1F3B4: "🎴" FLOWER PLAYING CARDS --> + <string name="spoken_emoji_1F3B4">Flower playing cards</string> + <!-- Spoken description for Unicode code point U+1F3B5: "🎵" MUSICAL NOTE --> + <string name="spoken_emoji_1F3B5">Musical note</string> + <!-- Spoken description for Unicode code point U+1F3B6: "🎶" MULTIPLE MUSICAL NOTES --> + <string name="spoken_emoji_1F3B6">Multiple musical notes</string> + <!-- Spoken description for Unicode code point U+1F3B7: "🎷" SAXOPHONE --> + <string name="spoken_emoji_1F3B7">Saxophone</string> + <!-- Spoken description for Unicode code point U+1F3B8: "🎸" GUITAR --> + <string name="spoken_emoji_1F3B8">Guitar</string> + <!-- Spoken description for Unicode code point U+1F3B9: "🎹" MUSICAL KEYBOARD --> + <string name="spoken_emoji_1F3B9">Musical keyboard</string> + <!-- Spoken description for Unicode code point U+1F3BA: "🎺" TRUMPET --> + <string name="spoken_emoji_1F3BA">Trumpet</string> + <!-- Spoken description for Unicode code point U+1F3BB: "🎻" VIOLIN --> + <string name="spoken_emoji_1F3BB">Violin</string> + <!-- Spoken description for Unicode code point U+1F3BC: "🎼" MUSICAL SCORE --> + <string name="spoken_emoji_1F3BC">Musical score</string> + <!-- Spoken description for Unicode code point U+1F3BD: "🎽" RUNNING SHIRT WITH SASH --> + <string name="spoken_emoji_1F3BD">Running shirt with sash</string> + <!-- Spoken description for Unicode code point U+1F3BE: "🎾" TENNIS RACQUET AND BALL --> + <string name="spoken_emoji_1F3BE">Tennis racquet and ball</string> + <!-- Spoken description for Unicode code point U+1F3BF: "🎿" SKI AND SKI BOOT --> + <string name="spoken_emoji_1F3BF">Ski and ski boot</string> + <!-- Spoken description for Unicode code point U+1F3C0: "🏀" BASKETBALL AND HOOP --> + <string name="spoken_emoji_1F3C0">Basketball and hoop</string> + <!-- Spoken description for Unicode code point U+1F3C1: "🏁" CHEQUERED FLAG --> + <string name="spoken_emoji_1F3C1">Chequered flag</string> + <!-- Spoken description for Unicode code point U+1F3C2: "🏂" SNOWBOARDER --> + <string name="spoken_emoji_1F3C2">Snowboarder</string> + <!-- Spoken description for Unicode code point U+1F3C3: "🏃" RUNNER --> + <string name="spoken_emoji_1F3C3">Runner</string> + <!-- Spoken description for Unicode code point U+1F3C4: "🏄" SURFER --> + <string name="spoken_emoji_1F3C4">Surfer</string> + <!-- Spoken description for Unicode code point U+1F3C6: "🏆" TROPHY --> + <string name="spoken_emoji_1F3C6">Trophy</string> + <!-- Spoken description for Unicode code point U+1F3C7: "🏇" HORSE RACING --> + <string name="spoken_emoji_1F3C7">Horse racing</string> + <!-- Spoken description for Unicode code point U+1F3C8: "🏈" AMERICAN FOOTBALL --> + <string name="spoken_emoji_1F3C8">American football</string> + <!-- Spoken description for Unicode code point U+1F3C9: "🏉" RUGBY FOOTBALL --> + <string name="spoken_emoji_1F3C9">Rugby football</string> + <!-- Spoken description for Unicode code point U+1F3CA: "🏊" SWIMMER --> + <string name="spoken_emoji_1F3CA">Swimmer</string> + <!-- Spoken description for Unicode code point U+1F3E0: "🏠" HOUSE BUILDING --> + <string name="spoken_emoji_1F3E0">House building</string> + <!-- Spoken description for Unicode code point U+1F3E1: "🏡" HOUSE WITH GARDEN --> + <string name="spoken_emoji_1F3E1">House with garden</string> + <!-- Spoken description for Unicode code point U+1F3E2: "🏢" OFFICE BUILDING --> + <string name="spoken_emoji_1F3E2">Office building</string> + <!-- Spoken description for Unicode code point U+1F3E3: "🏣" JAPANESE POST OFFICE --> + <string name="spoken_emoji_1F3E3">Japanese post office</string> + <!-- Spoken description for Unicode code point U+1F3E4: "🏤" EUROPEAN POST OFFICE --> + <string name="spoken_emoji_1F3E4">European post office</string> + <!-- Spoken description for Unicode code point U+1F3E5: "🏥" HOSPITAL --> + <string name="spoken_emoji_1F3E5">Hospital</string> + <!-- Spoken description for Unicode code point U+1F3E6: "🏦" BANK --> + <string name="spoken_emoji_1F3E6">Bank</string> + <!-- Spoken description for Unicode code point U+1F3E7: "🏧" AUTOMATED TELLER MACHINE --> + <string name="spoken_emoji_1F3E7">Automated teller machine</string> + <!-- Spoken description for Unicode code point U+1F3E8: "🏨" HOTEL --> + <string name="spoken_emoji_1F3E8">Hotel</string> + <!-- Spoken description for Unicode code point U+1F3E9: "🏩" LOVE HOTEL --> + <string name="spoken_emoji_1F3E9">Love hotel</string> + <!-- Spoken description for Unicode code point U+1F3EA: "🏪" CONVENIENCE STORE --> + <string name="spoken_emoji_1F3EA">Convenience store</string> + <!-- Spoken description for Unicode code point U+1F3EB: "🏫" SCHOOL --> + <string name="spoken_emoji_1F3EB">School</string> + <!-- Spoken description for Unicode code point U+1F3EC: "🏬" DEPARTMENT STORE --> + <string name="spoken_emoji_1F3EC">Department store</string> + <!-- Spoken description for Unicode code point U+1F3ED: "🏭" FACTORY --> + <string name="spoken_emoji_1F3ED">Factory</string> + <!-- Spoken description for Unicode code point U+1F3EE: "🏮" IZAKAYA LANTERN --> + <string name="spoken_emoji_1F3EE">Izakaya lantern</string> + <!-- Spoken description for Unicode code point U+1F3EF: "🏯" JAPANESE CASTLE --> + <string name="spoken_emoji_1F3EF">Japanese castle</string> + <!-- Spoken description for Unicode code point U+1F3F0: "🏰" EUROPEAN CASTLE --> + <string name="spoken_emoji_1F3F0">European castle</string> + <!-- Spoken description for Unicode code point U+1F400: "🐀" RAT --> + <string name="spoken_emoji_1F400">Rat</string> + <!-- Spoken description for Unicode code point U+1F401: "🐁" MOUSE --> + <string name="spoken_emoji_1F401">Mouse</string> + <!-- Spoken description for Unicode code point U+1F402: "🐂" OX --> + <string name="spoken_emoji_1F402">Ox</string> + <!-- Spoken description for Unicode code point U+1F403: "🐃" WATER BUFFALO --> + <string name="spoken_emoji_1F403">Water buffalo</string> + <!-- Spoken description for Unicode code point U+1F404: "🐄" COW --> + <string name="spoken_emoji_1F404">Cow</string> + <!-- Spoken description for Unicode code point U+1F406: "🐆" LEOPARD --> + <string name="spoken_emoji_1F406">Leopard</string> + <!-- Spoken description for Unicode code point U+1F407: "🐇" RABBIT --> + <string name="spoken_emoji_1F407">Rabbit</string> + <!-- Spoken description for Unicode code point U+1F408: "🐈" CAT --> + <string name="spoken_emoji_1F408">Cat</string> + <!-- Spoken description for Unicode code point U+1F409: "🐉" DRAGON --> + <string name="spoken_emoji_1F409">Dragon</string> + <!-- Spoken description for Unicode code point U+1F40A: "🐊" CROCODILE --> + <string name="spoken_emoji_1F40A">Crocodile</string> + <!-- Spoken description for Unicode code point U+1F40B: "🐋" WHALE --> + <string name="spoken_emoji_1F40B">Whale</string> + <!-- Spoken description for Unicode code point U+1F40C: "🐌" SNAIL --> + <string name="spoken_emoji_1F40C">Snail</string> + <!-- Spoken description for Unicode code point U+1F40D: "🐍" SNAKE --> + <string name="spoken_emoji_1F40D">Snake</string> + <!-- Spoken description for Unicode code point U+1F40E: "🐎" HORSE --> + <string name="spoken_emoji_1F40E">Horse</string> + <!-- Spoken description for Unicode code point U+1F40F: "🐏" RAM --> + <string name="spoken_emoji_1F40F">Ram</string> + <!-- Spoken description for Unicode code point U+1F410: "🐐" GOAT --> + <string name="spoken_emoji_1F410">Goat</string> + <!-- Spoken description for Unicode code point U+1F411: "🐑" SHEEP --> + <string name="spoken_emoji_1F411">Sheep</string> + <!-- Spoken description for Unicode code point U+1F412: "🐒" MONKEY --> + <string name="spoken_emoji_1F412">Monkey</string> + <!-- Spoken description for Unicode code point U+1F413: "🐓" ROOSTER --> + <string name="spoken_emoji_1F413">Rooster</string> + <!-- Spoken description for Unicode code point U+1F414: "🐔" CHICKEN --> + <string name="spoken_emoji_1F414">Chicken</string> + <!-- Spoken description for Unicode code point U+1F415: "🐕" DOG --> + <string name="spoken_emoji_1F415">Dog</string> + <!-- Spoken description for Unicode code point U+1F416: "🐖" PIG --> + <string name="spoken_emoji_1F416">Pig</string> + <!-- Spoken description for Unicode code point U+1F417: "🐗" BOAR --> + <string name="spoken_emoji_1F417">Boar</string> + <!-- Spoken description for Unicode code point U+1F418: "🐘" ELEPHANT --> + <string name="spoken_emoji_1F418">Elephant</string> + <!-- Spoken description for Unicode code point U+1F419: "🐙" OCTOPUS --> + <string name="spoken_emoji_1F419">Octopus</string> + <!-- Spoken description for Unicode code point U+1F41A: "🐚" SPIRAL SHELL --> + <string name="spoken_emoji_1F41A">Spiral shell</string> + <!-- Spoken description for Unicode code point U+1F41B: "🐛" BUG --> + <string name="spoken_emoji_1F41B">Bug</string> + <!-- Spoken description for Unicode code point U+1F41C: "🐜" ANT --> + <string name="spoken_emoji_1F41C">Ant</string> + <!-- Spoken description for Unicode code point U+1F41D: "🐝" HONEYBEE --> + <string name="spoken_emoji_1F41D">Honeybee</string> + <!-- Spoken description for Unicode code point U+1F41E: "🐞" LADY BEETLE --> + <string name="spoken_emoji_1F41E">Lady beetle</string> + <!-- Spoken description for Unicode code point U+1F41F: "🐟" FISH --> + <string name="spoken_emoji_1F41F">Fish</string> + <!-- Spoken description for Unicode code point U+1F420: "🐠" TROPICAL FISH --> + <string name="spoken_emoji_1F420">Tropical fish</string> + <!-- Spoken description for Unicode code point U+1F421: "🐡" BLOWFISH --> + <string name="spoken_emoji_1F421">Blowfish</string> + <!-- Spoken description for Unicode code point U+1F422: "🐢" TURTLE --> + <string name="spoken_emoji_1F422">Turtle</string> + <!-- Spoken description for Unicode code point U+1F423: "🐣" HATCHING CHICK --> + <string name="spoken_emoji_1F423">Hatching chick</string> + <!-- Spoken description for Unicode code point U+1F424: "🐤" BABY CHICK --> + <string name="spoken_emoji_1F424">Baby chick</string> + <!-- Spoken description for Unicode code point U+1F425: "🐥" FRONT-FACING BABY CHICK --> + <string name="spoken_emoji_1F425">Front-facing baby chick</string> + <!-- Spoken description for Unicode code point U+1F426: "🐦" BIRD --> + <string name="spoken_emoji_1F426">Bird</string> + <!-- Spoken description for Unicode code point U+1F427: "🐧" PENGUIN --> + <string name="spoken_emoji_1F427">Penguin</string> + <!-- Spoken description for Unicode code point U+1F428: "🐨" KOALA --> + <string name="spoken_emoji_1F428">Koala</string> + <!-- Spoken description for Unicode code point U+1F429: "🐩" POODLE --> + <string name="spoken_emoji_1F429">Poodle</string> + <!-- Spoken description for Unicode code point U+1F42A: "🐪" DROMEDARY CAMEL --> + <string name="spoken_emoji_1F42A">Dromedary camel</string> + <!-- Spoken description for Unicode code point U+1F42B: "🐫" BACTRIAN CAMEL --> + <string name="spoken_emoji_1F42B">Bactrian camel</string> + <!-- Spoken description for Unicode code point U+1F42C: "🐬" DOLPHIN --> + <string name="spoken_emoji_1F42C">Dolphin</string> + <!-- Spoken description for Unicode code point U+1F42D: "🐭" MOUSE FACE --> + <string name="spoken_emoji_1F42D">Mouse face</string> + <!-- Spoken description for Unicode code point U+1F42E: "🐮" COW FACE --> + <string name="spoken_emoji_1F42E">Cow face</string> + <!-- Spoken description for Unicode code point U+1F42F: "🐯" TIGER FACE --> + <string name="spoken_emoji_1F42F">Tiger face</string> + <!-- Spoken description for Unicode code point U+1F430: "🐰" RABBIT FACE --> + <string name="spoken_emoji_1F430">Rabbit face</string> + <!-- Spoken description for Unicode code point U+1F431: "🐱" CAT FACE --> + <string name="spoken_emoji_1F431">Cat face</string> + <!-- Spoken description for Unicode code point U+1F432: "🐲" DRAGON FACE --> + <string name="spoken_emoji_1F432">Dragon face</string> + <!-- Spoken description for Unicode code point U+1F433: "🐳" SPOUTING WHALE --> + <string name="spoken_emoji_1F433">Spouting whale</string> + <!-- Spoken description for Unicode code point U+1F434: "🐴" HORSE FACE --> + <string name="spoken_emoji_1F434">Horse face</string> + <!-- Spoken description for Unicode code point U+1F435: "🐵" MONKEY FACE --> + <string name="spoken_emoji_1F435">Monkey face</string> + <!-- Spoken description for Unicode code point U+1F436: "🐶" DOG FACE --> + <string name="spoken_emoji_1F436">Dog face</string> + <!-- Spoken description for Unicode code point U+1F437: "🐷" PIG FACE --> + <string name="spoken_emoji_1F437">Pig face</string> + <!-- Spoken description for Unicode code point U+1F438: "🐸" FROG FACE --> + <string name="spoken_emoji_1F438">Frog face</string> + <!-- Spoken description for Unicode code point U+1F439: "🐹" HAMSTER FACE --> + <string name="spoken_emoji_1F439">Hamster face</string> + <!-- Spoken description for Unicode code point U+1F43A: "🐺" WOLF FACE --> + <string name="spoken_emoji_1F43A">Wolf face</string> + <!-- Spoken description for Unicode code point U+1F43B: "🐻" BEAR FACE --> + <string name="spoken_emoji_1F43B">Bear face</string> + <!-- Spoken description for Unicode code point U+1F43C: "🐼" PANDA FACE --> + <string name="spoken_emoji_1F43C">Panda face</string> + <!-- Spoken description for Unicode code point U+1F43D: "🐽" PIG NOSE --> + <string name="spoken_emoji_1F43D">Pig nose</string> + <!-- Spoken description for Unicode code point U+1F43E: "🐾" PAW PRINTS --> + <string name="spoken_emoji_1F43E">Paw prints</string> + <!-- Spoken description for Unicode code point U+1F440: "👀" EYES --> + <string name="spoken_emoji_1F440">Eyes</string> + <!-- Spoken description for Unicode code point U+1F442: "👂" EAR --> + <string name="spoken_emoji_1F442">Ear</string> + <!-- Spoken description for Unicode code point U+1F443: "👃" NOSE --> + <string name="spoken_emoji_1F443">Nose</string> + <!-- Spoken description for Unicode code point U+1F444: "👄" MOUTH --> + <string name="spoken_emoji_1F444">Mouth</string> + <!-- Spoken description for Unicode code point U+1F445: "👅" TONGUE --> + <string name="spoken_emoji_1F445">Tongue</string> + <!-- Spoken description for Unicode code point U+1F446: "👆" WHITE UP POINTING BACKHAND INDEX --> + <string name="spoken_emoji_1F446">White up pointing backhand index</string> + <!-- Spoken description for Unicode code point U+1F447: "👇" WHITE DOWN POINTING BACKHAND INDEX --> + <string name="spoken_emoji_1F447">White down pointing backhand index</string> + <!-- Spoken description for Unicode code point U+1F448: "👈" WHITE LEFT POINTING BACKHAND INDEX --> + <string name="spoken_emoji_1F448">White left pointing backhand index</string> + <!-- Spoken description for Unicode code point U+1F449: "👉" WHITE RIGHT POINTING BACKHAND INDEX --> + <string name="spoken_emoji_1F449">White right pointing backhand index</string> + <!-- Spoken description for Unicode code point U+1F44A: "👊" FISTED HAND SIGN --> + <string name="spoken_emoji_1F44A">Fisted hand sign</string> + <!-- Spoken description for Unicode code point U+1F44B: "👋" WAVING HAND SIGN --> + <string name="spoken_emoji_1F44B">Waving hand sign</string> + <!-- Spoken description for Unicode code point U+1F44C: "👌" OK HAND SIGN --> + <string name="spoken_emoji_1F44C">Ok hand sign</string> + <!-- Spoken description for Unicode code point U+1F44D: "👍" THUMBS UP SIGN --> + <string name="spoken_emoji_1F44D">Thumbs up sign</string> + <!-- Spoken description for Unicode code point U+1F44E: "👎" THUMBS DOWN SIGN --> + <string name="spoken_emoji_1F44E">Thumbs down sign</string> + <!-- Spoken description for Unicode code point U+1F44F: "👏" CLAPPING HANDS SIGN --> + <string name="spoken_emoji_1F44F">Clapping hands sign</string> + <!-- Spoken description for Unicode code point U+1F450: "👐" OPEN HANDS SIGN --> + <string name="spoken_emoji_1F450">Open hands sign</string> + <!-- Spoken description for Unicode code point U+1F451: "👑" CROWN --> + <string name="spoken_emoji_1F451">Crown</string> + <!-- Spoken description for Unicode code point U+1F452: "👒" WOMANS HAT --> + <string name="spoken_emoji_1F452">Womans hat</string> + <!-- Spoken description for Unicode code point U+1F453: "👓" EYEGLASSES --> + <string name="spoken_emoji_1F453">Eyeglasses</string> + <!-- Spoken description for Unicode code point U+1F454: "👔" NECKTIE --> + <string name="spoken_emoji_1F454">Necktie</string> + <!-- Spoken description for Unicode code point U+1F455: "👕" T-SHIRT --> + <string name="spoken_emoji_1F455">T-shirt</string> + <!-- Spoken description for Unicode code point U+1F456: "👖" JEANS --> + <string name="spoken_emoji_1F456">Jeans</string> + <!-- Spoken description for Unicode code point U+1F457: "👗" DRESS --> + <string name="spoken_emoji_1F457">Dress</string> + <!-- Spoken description for Unicode code point U+1F458: "👘" KIMONO --> + <string name="spoken_emoji_1F458">Kimono</string> + <!-- Spoken description for Unicode code point U+1F459: "👙" BIKINI --> + <string name="spoken_emoji_1F459">Bikini</string> + <!-- Spoken description for Unicode code point U+1F45A: "👚" WOMANS CLOTHES --> + <string name="spoken_emoji_1F45A">Womans clothes</string> + <!-- Spoken description for Unicode code point U+1F45B: "👛" PURSE --> + <string name="spoken_emoji_1F45B">Purse</string> + <!-- Spoken description for Unicode code point U+1F45C: "👜" HANDBAG --> + <string name="spoken_emoji_1F45C">Handbag</string> + <!-- Spoken description for Unicode code point U+1F45D: "👝" POUCH --> + <string name="spoken_emoji_1F45D">Pouch</string> + <!-- Spoken description for Unicode code point U+1F45E: "👞" MANS SHOE --> + <string name="spoken_emoji_1F45E">Mans shoe</string> + <!-- Spoken description for Unicode code point U+1F45F: "👟" ATHLETIC SHOE --> + <string name="spoken_emoji_1F45F">Athletic shoe</string> + <!-- Spoken description for Unicode code point U+1F460: "👠" HIGH-HEELED SHOE --> + <string name="spoken_emoji_1F460">High-heeled shoe</string> + <!-- Spoken description for Unicode code point U+1F461: "👡" WOMANS SANDAL --> + <string name="spoken_emoji_1F461">Womans sandal</string> + <!-- Spoken description for Unicode code point U+1F462: "👢" WOMANS BOOTS --> + <string name="spoken_emoji_1F462">Womans boots</string> + <!-- Spoken description for Unicode code point U+1F463: "👣" FOOTPRINTS --> + <string name="spoken_emoji_1F463">Footprints</string> + <!-- Spoken description for Unicode code point U+1F464: "👤" BUST IN SILHOUETTE --> + <string name="spoken_emoji_1F464">Bust in silhouette</string> + <!-- Spoken description for Unicode code point U+1F465: "👥" BUSTS IN SILHOUETTE --> + <string name="spoken_emoji_1F465">Busts in silhouette</string> + <!-- Spoken description for Unicode code point U+1F466: "👦" BOY --> + <string name="spoken_emoji_1F466">Boy</string> + <!-- Spoken description for Unicode code point U+1F467: "👧" GIRL --> + <string name="spoken_emoji_1F467">Girl</string> + <!-- Spoken description for Unicode code point U+1F468: "👨" MAN --> + <string name="spoken_emoji_1F468">Man</string> + <!-- Spoken description for Unicode code point U+1F469: "👩" WOMAN --> + <string name="spoken_emoji_1F469">Woman</string> + <!-- Spoken description for Unicode code point U+1F46A: "👪" FAMILY --> + <string name="spoken_emoji_1F46A">Family</string> + <!-- Spoken description for Unicode code point U+1F46B: "👫" MAN AND WOMAN HOLDING HANDS --> + <string name="spoken_emoji_1F46B">Man and woman holding hands</string> + <!-- Spoken description for Unicode code point U+1F46C: "👬" TWO MEN HOLDING HANDS --> + <string name="spoken_emoji_1F46C">Two men holding hands</string> + <!-- Spoken description for Unicode code point U+1F46D: "👭" TWO WOMEN HOLDING HANDS --> + <string name="spoken_emoji_1F46D">Two women holding hands</string> + <!-- Spoken description for Unicode code point U+1F46E: "👮" POLICE OFFICER --> + <string name="spoken_emoji_1F46E">Police officer</string> + <!-- Spoken description for Unicode code point U+1F46F: "👯" WOMAN WITH BUNNY EARS --> + <string name="spoken_emoji_1F46F">Woman with bunny ears</string> + <!-- Spoken description for Unicode code point U+1F470: "👰" BRIDE WITH VEIL --> + <string name="spoken_emoji_1F470">Bride with veil</string> + <!-- Spoken description for Unicode code point U+1F471: "👱" PERSON WITH BLOND HAIR --> + <string name="spoken_emoji_1F471">Person with blond hair</string> + <!-- Spoken description for Unicode code point U+1F472: "👲" MAN WITH GUA PI MAO --> + <string name="spoken_emoji_1F472">Man with gua pi mao</string> + <!-- Spoken description for Unicode code point U+1F473: "👳" MAN WITH TURBAN --> + <string name="spoken_emoji_1F473">Man with turban</string> + <!-- Spoken description for Unicode code point U+1F474: "👴" OLDER MAN --> + <string name="spoken_emoji_1F474">Older man</string> + <!-- Spoken description for Unicode code point U+1F475: "👵" OLDER WOMAN --> + <string name="spoken_emoji_1F475">Older woman</string> + <!-- Spoken description for Unicode code point U+1F476: "👶" BABY --> + <string name="spoken_emoji_1F476">Baby</string> + <!-- Spoken description for Unicode code point U+1F477: "👷" CONSTRUCTION WORKER --> + <string name="spoken_emoji_1F477">Construction worker</string> + <!-- Spoken description for Unicode code point U+1F478: "👸" PRINCESS --> + <string name="spoken_emoji_1F478">Princess</string> + <!-- Spoken description for Unicode code point U+1F479: "👹" JAPANESE OGRE --> + <string name="spoken_emoji_1F479">Japanese ogre</string> + <!-- Spoken description for Unicode code point U+1F47A: "👺" JAPANESE GOBLIN --> + <string name="spoken_emoji_1F47A">Japanese goblin</string> + <!-- Spoken description for Unicode code point U+1F47B: "👻" GHOST --> + <string name="spoken_emoji_1F47B">Ghost</string> + <!-- Spoken description for Unicode code point U+1F47C: "👼" BABY ANGEL --> + <string name="spoken_emoji_1F47C">Baby angel</string> + <!-- Spoken description for Unicode code point U+1F47D: "👽" EXTRATERRESTRIAL ALIEN --> + <string name="spoken_emoji_1F47D">Extraterrestrial alien</string> + <!-- Spoken description for Unicode code point U+1F47E: "👾" ALIEN MONSTER --> + <string name="spoken_emoji_1F47E">Alien monster</string> + <!-- Spoken description for Unicode code point U+1F47F: "👿" IMP --> + <string name="spoken_emoji_1F47F">Imp</string> + <!-- Spoken description for Unicode code point U+1F480: "💀" SKULL --> + <string name="spoken_emoji_1F480">Skull</string> + <!-- Spoken description for Unicode code point U+1F481: "💁" INFORMATION DESK PERSON --> + <string name="spoken_emoji_1F481">Information desk person</string> + <!-- Spoken description for Unicode code point U+1F482: "💂" GUARDSMAN --> + <string name="spoken_emoji_1F482">Guardsman</string> + <!-- Spoken description for Unicode code point U+1F483: "💃" DANCER --> + <string name="spoken_emoji_1F483">Dancer</string> + <!-- Spoken description for Unicode code point U+1F484: "💄" LIPSTICK --> + <string name="spoken_emoji_1F484">Lipstick</string> + <!-- Spoken description for Unicode code point U+1F485: "💅" NAIL POLISH --> + <string name="spoken_emoji_1F485">Nail polish</string> + <!-- Spoken description for Unicode code point U+1F486: "💆" FACE MASSAGE --> + <string name="spoken_emoji_1F486">Face massage</string> + <!-- Spoken description for Unicode code point U+1F487: "💇" HAIRCUT --> + <string name="spoken_emoji_1F487">Haircut</string> + <!-- Spoken description for Unicode code point U+1F488: "💈" BARBER POLE --> + <string name="spoken_emoji_1F488">Barber pole</string> + <!-- Spoken description for Unicode code point U+1F489: "💉" SYRINGE --> + <string name="spoken_emoji_1F489">Syringe</string> + <!-- Spoken description for Unicode code point U+1F48A: "💊" PILL --> + <string name="spoken_emoji_1F48A">Pill</string> + <!-- Spoken description for Unicode code point U+1F48B: "💋" KISS MARK --> + <string name="spoken_emoji_1F48B">Kiss mark</string> + <!-- Spoken description for Unicode code point U+1F48C: "💌" LOVE LETTER --> + <string name="spoken_emoji_1F48C">Love letter</string> + <!-- Spoken description for Unicode code point U+1F48D: "💍" RING --> + <string name="spoken_emoji_1F48D">Ring</string> + <!-- Spoken description for Unicode code point U+1F48E: "💎" GEM STONE --> + <string name="spoken_emoji_1F48E">Gem stone</string> + <!-- Spoken description for Unicode code point U+1F48F: "💏" KISS --> + <string name="spoken_emoji_1F48F">Kiss</string> + <!-- Spoken description for Unicode code point U+1F490: "💐" BOUQUET --> + <string name="spoken_emoji_1F490">Bouquet</string> + <!-- Spoken description for Unicode code point U+1F491: "💑" COUPLE WITH HEART --> + <string name="spoken_emoji_1F491">Couple with heart</string> + <!-- Spoken description for Unicode code point U+1F492: "💒" WEDDING --> + <string name="spoken_emoji_1F492">Wedding</string> + <!-- Spoken description for Unicode code point U+1F493: "💓" BEATING HEART --> + <string name="spoken_emoji_1F493">Beating heart</string> + <!-- Spoken description for Unicode code point U+1F494: "💔" BROKEN HEART --> + <string name="spoken_emoji_1F494">Broken heart</string> + <!-- Spoken description for Unicode code point U+1F495: "💕" TWO HEARTS --> + <string name="spoken_emoji_1F495">Two hearts</string> + <!-- Spoken description for Unicode code point U+1F496: "💖" SPARKLING HEART --> + <string name="spoken_emoji_1F496">Sparkling heart</string> + <!-- Spoken description for Unicode code point U+1F497: "💗" GROWING HEART --> + <string name="spoken_emoji_1F497">Growing heart</string> + <!-- Spoken description for Unicode code point U+1F498: "💘" HEART WITH ARROW --> + <string name="spoken_emoji_1F498">Heart with arrow</string> + <!-- Spoken description for Unicode code point U+1F499: "💙" BLUE HEART --> + <string name="spoken_emoji_1F499">Blue heart</string> + <!-- Spoken description for Unicode code point U+1F49A: "💚" GREEN HEART --> + <string name="spoken_emoji_1F49A">Green heart</string> + <!-- Spoken description for Unicode code point U+1F49B: "💛" YELLOW HEART --> + <string name="spoken_emoji_1F49B">Yellow heart</string> + <!-- Spoken description for Unicode code point U+1F49C: "💜" PURPLE HEART --> + <string name="spoken_emoji_1F49C">Purple heart</string> + <!-- Spoken description for Unicode code point U+1F49D: "💝" HEART WITH RIBBON --> + <string name="spoken_emoji_1F49D">Heart with ribbon</string> + <!-- Spoken description for Unicode code point U+1F49E: "💞" REVOLVING HEARTS --> + <string name="spoken_emoji_1F49E">Revolving hearts</string> + <!-- Spoken description for Unicode code point U+1F49F: "💟" HEART DECORATION --> + <string name="spoken_emoji_1F49F">Heart decoration</string> + <!-- Spoken description for Unicode code point U+1F4A0: "💠" DIAMOND SHAPE WITH A DOT INSIDE --> + <string name="spoken_emoji_1F4A0">Diamond shape with a dot inside</string> + <!-- Spoken description for Unicode code point U+1F4A1: "💡" ELECTRIC LIGHT BULB --> + <string name="spoken_emoji_1F4A1">Electric light bulb</string> + <!-- Spoken description for Unicode code point U+1F4A2: "💢" ANGER SYMBOL --> + <string name="spoken_emoji_1F4A2">Anger symbol</string> + <!-- Spoken description for Unicode code point U+1F4A3: "💣" BOMB --> + <string name="spoken_emoji_1F4A3">Bomb</string> + <!-- Spoken description for Unicode code point U+1F4A4: "💤" SLEEPING SYMBOL --> + <string name="spoken_emoji_1F4A4">Sleeping symbol</string> + <!-- Spoken description for Unicode code point U+1F4A5: "💥" COLLISION SYMBOL --> + <string name="spoken_emoji_1F4A5">Collision symbol</string> + <!-- Spoken description for Unicode code point U+1F4A6: "💦" SPLASHING SWEAT SYMBOL --> + <string name="spoken_emoji_1F4A6">Splashing sweat symbol</string> + <!-- Spoken description for Unicode code point U+1F4A7: "💧" DROPLET --> + <string name="spoken_emoji_1F4A7">Droplet</string> + <!-- Spoken description for Unicode code point U+1F4A8: "💨" DASH SYMBOL --> + <string name="spoken_emoji_1F4A8">Dash symbol</string> + <!-- Spoken description for Unicode code point U+1F4A9: "💩" PILE OF POO --> + <string name="spoken_emoji_1F4A9">Pile of poo</string> + <!-- Spoken description for Unicode code point U+1F4AA: "💪" FLEXED BICEPS --> + <string name="spoken_emoji_1F4AA">Flexed biceps</string> + <!-- Spoken description for Unicode code point U+1F4AB: "💫" DIZZY SYMBOL --> + <string name="spoken_emoji_1F4AB">Dizzy symbol</string> + <!-- Spoken description for Unicode code point U+1F4AC: "💬" SPEECH BALLOON --> + <string name="spoken_emoji_1F4AC">Speech balloon</string> + <!-- Spoken description for Unicode code point U+1F4AD: "💭" THOUGHT BALLOON --> + <string name="spoken_emoji_1F4AD">Thought balloon</string> + <!-- Spoken description for Unicode code point U+1F4AE: "💮" WHITE FLOWER --> + <string name="spoken_emoji_1F4AE">White flower</string> + <!-- Spoken description for Unicode code point U+1F4AF: "💯" HUNDRED POINTS SYMBOL --> + <string name="spoken_emoji_1F4AF">Hundred points symbol</string> + <!-- Spoken description for Unicode code point U+1F4B0: "💰" MONEY BAG --> + <string name="spoken_emoji_1F4B0">Money bag</string> + <!-- Spoken description for Unicode code point U+1F4B1: "💱" CURRENCY EXCHANGE --> + <string name="spoken_emoji_1F4B1">Currency exchange</string> + <!-- Spoken description for Unicode code point U+1F4B2: "💲" HEAVY DOLLAR SIGN --> + <string name="spoken_emoji_1F4B2">Heavy dollar sign</string> + <!-- Spoken description for Unicode code point U+1F4B3: "💳" CREDIT CARD --> + <string name="spoken_emoji_1F4B3">Credit card</string> + <!-- Spoken description for Unicode code point U+1F4B4: "💴" BANKNOTE WITH YEN SIGN --> + <string name="spoken_emoji_1F4B4">Banknote with yen sign</string> + <!-- Spoken description for Unicode code point U+1F4B5: "💵" BANKNOTE WITH DOLLAR SIGN --> + <string name="spoken_emoji_1F4B5">Banknote with dollar sign</string> + <!-- Spoken description for Unicode code point U+1F4B6: "💶" BANKNOTE WITH EURO SIGN --> + <string name="spoken_emoji_1F4B6">Banknote with euro sign</string> + <!-- Spoken description for Unicode code point U+1F4B7: "💷" BANKNOTE WITH POUND SIGN --> + <string name="spoken_emoji_1F4B7">Banknote with pound sign</string> + <!-- Spoken description for Unicode code point U+1F4B8: "💸" MONEY WITH WINGS --> + <string name="spoken_emoji_1F4B8">Money with wings</string> + <!-- Spoken description for Unicode code point U+1F4B9: "💹" CHART WITH UPWARDS TREND AND YEN SIGN --> + <string name="spoken_emoji_1F4B9">Chart with upwards trend and yen sign</string> + <!-- Spoken description for Unicode code point U+1F4BA: "💺" SEAT --> + <string name="spoken_emoji_1F4BA">Seat</string> + <!-- Spoken description for Unicode code point U+1F4BB: "💻" PERSONAL COMPUTER --> + <string name="spoken_emoji_1F4BB">Personal computer</string> + <!-- Spoken description for Unicode code point U+1F4BC: "💼" BRIEFCASE --> + <string name="spoken_emoji_1F4BC">Briefcase</string> + <!-- Spoken description for Unicode code point U+1F4BD: "💽" MINIDISC --> + <string name="spoken_emoji_1F4BD">Minidisc</string> + <!-- Spoken description for Unicode code point U+1F4BE: "💾" FLOPPY DISK --> + <string name="spoken_emoji_1F4BE">Floppy disk</string> + <!-- Spoken description for Unicode code point U+1F4BF: "💿" OPTICAL DISC --> + <string name="spoken_emoji_1F4BF">Optical disc</string> + <!-- Spoken description for Unicode code point U+1F4C0: "📀" DVD --> + <string name="spoken_emoji_1F4C0">Dvd</string> + <!-- Spoken description for Unicode code point U+1F4C1: "📁" FILE FOLDER --> + <string name="spoken_emoji_1F4C1">File folder</string> + <!-- Spoken description for Unicode code point U+1F4C2: "📂" OPEN FILE FOLDER --> + <string name="spoken_emoji_1F4C2">Open file folder</string> + <!-- Spoken description for Unicode code point U+1F4C3: "📃" PAGE WITH CURL --> + <string name="spoken_emoji_1F4C3">Page with curl</string> + <!-- Spoken description for Unicode code point U+1F4C4: "📄" PAGE FACING UP --> + <string name="spoken_emoji_1F4C4">Page facing up</string> + <!-- Spoken description for Unicode code point U+1F4C5: "📅" CALENDAR --> + <string name="spoken_emoji_1F4C5">Calendar</string> + <!-- Spoken description for Unicode code point U+1F4C6: "📆" TEAR-OFF CALENDAR --> + <string name="spoken_emoji_1F4C6">Tear-off calendar</string> + <!-- Spoken description for Unicode code point U+1F4C7: "📇" CARD INDEX --> + <string name="spoken_emoji_1F4C7">Card index</string> + <!-- Spoken description for Unicode code point U+1F4C8: "📈" CHART WITH UPWARDS TREND --> + <string name="spoken_emoji_1F4C8">Chart with upwards trend</string> + <!-- Spoken description for Unicode code point U+1F4C9: "📉" CHART WITH DOWNWARDS TREND --> + <string name="spoken_emoji_1F4C9">Chart with downwards trend</string> + <!-- Spoken description for Unicode code point U+1F4CA: "📊" BAR CHART --> + <string name="spoken_emoji_1F4CA">Bar chart</string> + <!-- Spoken description for Unicode code point U+1F4CB: "📋" CLIPBOARD --> + <string name="spoken_emoji_1F4CB">Clipboard</string> + <!-- Spoken description for Unicode code point U+1F4CC: "📌" PUSHPIN --> + <string name="spoken_emoji_1F4CC">Pushpin</string> + <!-- Spoken description for Unicode code point U+1F4CD: "📍" ROUND PUSHPIN --> + <string name="spoken_emoji_1F4CD">Round pushpin</string> + <!-- Spoken description for Unicode code point U+1F4CE: "📎" PAPERCLIP --> + <string name="spoken_emoji_1F4CE">Paperclip</string> + <!-- Spoken description for Unicode code point U+1F4CF: "📏" STRAIGHT RULER --> + <string name="spoken_emoji_1F4CF">Straight ruler</string> + <!-- Spoken description for Unicode code point U+1F4D0: "📐" TRIANGULAR RULER --> + <string name="spoken_emoji_1F4D0">Triangular ruler</string> + <!-- Spoken description for Unicode code point U+1F4D1: "📑" BOOKMARK TABS --> + <string name="spoken_emoji_1F4D1">Bookmark tabs</string> + <!-- Spoken description for Unicode code point U+1F4D2: "📒" LEDGER --> + <string name="spoken_emoji_1F4D2">Ledger</string> + <!-- Spoken description for Unicode code point U+1F4D3: "📓" NOTEBOOK --> + <string name="spoken_emoji_1F4D3">Notebook</string> + <!-- Spoken description for Unicode code point U+1F4D4: "📔" NOTEBOOK WITH DECORATIVE COVER --> + <string name="spoken_emoji_1F4D4">Notebook with decorative cover</string> + <!-- Spoken description for Unicode code point U+1F4D5: "📕" CLOSED BOOK --> + <string name="spoken_emoji_1F4D5">Closed book</string> + <!-- Spoken description for Unicode code point U+1F4D6: "📖" OPEN BOOK --> + <string name="spoken_emoji_1F4D6">Open book</string> + <!-- Spoken description for Unicode code point U+1F4D7: "📗" GREEN BOOK --> + <string name="spoken_emoji_1F4D7">Green book</string> + <!-- Spoken description for Unicode code point U+1F4D8: "📘" BLUE BOOK --> + <string name="spoken_emoji_1F4D8">Blue book</string> + <!-- Spoken description for Unicode code point U+1F4D9: "📙" ORANGE BOOK --> + <string name="spoken_emoji_1F4D9">Orange book</string> + <!-- Spoken description for Unicode code point U+1F4DA: "📚" BOOKS --> + <string name="spoken_emoji_1F4DA">Books</string> + <!-- Spoken description for Unicode code point U+1F4DB: "📛" NAME BADGE --> + <string name="spoken_emoji_1F4DB">Name badge</string> + <!-- Spoken description for Unicode code point U+1F4DC: "📜" SCROLL --> + <string name="spoken_emoji_1F4DC">Scroll</string> + <!-- Spoken description for Unicode code point U+1F4DD: "📝" MEMO --> + <string name="spoken_emoji_1F4DD">Memo</string> + <!-- Spoken description for Unicode code point U+1F4DE: "📞" TELEPHONE RECEIVER --> + <string name="spoken_emoji_1F4DE">Telephone receiver</string> + <!-- Spoken description for Unicode code point U+1F4DF: "📟" PAGER --> + <string name="spoken_emoji_1F4DF">Pager</string> + <!-- Spoken description for Unicode code point U+1F4E0: "📠" FAX MACHINE --> + <string name="spoken_emoji_1F4E0">Fax machine</string> + <!-- Spoken description for Unicode code point U+1F4E1: "📡" SATELLITE ANTENNA --> + <string name="spoken_emoji_1F4E1">Satellite antenna</string> + <!-- Spoken description for Unicode code point U+1F4E2: "📢" PUBLIC ADDRESS LOUDSPEAKER --> + <string name="spoken_emoji_1F4E2">Public address loudspeaker</string> + <!-- Spoken description for Unicode code point U+1F4E3: "📣" CHEERING MEGAPHONE --> + <string name="spoken_emoji_1F4E3">Cheering megaphone</string> + <!-- Spoken description for Unicode code point U+1F4E4: "📤" OUTBOX TRAY --> + <string name="spoken_emoji_1F4E4">Outbox tray</string> + <!-- Spoken description for Unicode code point U+1F4E5: "📥" INBOX TRAY --> + <string name="spoken_emoji_1F4E5">Inbox tray</string> + <!-- Spoken description for Unicode code point U+1F4E6: "📦" PACKAGE --> + <string name="spoken_emoji_1F4E6">Package</string> + <!-- Spoken description for Unicode code point U+1F4E7: "📧" E-MAIL SYMBOL --> + <string name="spoken_emoji_1F4E7">E-mail symbol</string> + <!-- Spoken description for Unicode code point U+1F4E8: "📨" INCOMING ENVELOPE --> + <string name="spoken_emoji_1F4E8">Incoming envelope</string> + <!-- Spoken description for Unicode code point U+1F4E9: "📩" ENVELOPE WITH DOWNWARDS ARROW ABOVE --> + <string name="spoken_emoji_1F4E9">Envelope with downwards arrow above</string> + <!-- Spoken description for Unicode code point U+1F4EA: "📪" CLOSED MAILBOX WITH LOWERED FLAG --> + <string name="spoken_emoji_1F4EA">Closed mailbox with lowered flag</string> + <!-- Spoken description for Unicode code point U+1F4EB: "📫" CLOSED MAILBOX WITH RAISED FLAG --> + <string name="spoken_emoji_1F4EB">Closed mailbox with raised flag</string> + <!-- Spoken description for Unicode code point U+1F4EC: "📬" OPEN MAILBOX WITH RAISED FLAG --> + <string name="spoken_emoji_1F4EC">Open mailbox with raised flag</string> + <!-- Spoken description for Unicode code point U+1F4ED: "📭" OPEN MAILBOX WITH LOWERED FLAG --> + <string name="spoken_emoji_1F4ED">Open mailbox with lowered flag</string> + <!-- Spoken description for Unicode code point U+1F4EE: "📮" POSTBOX --> + <string name="spoken_emoji_1F4EE">Postbox</string> + <!-- Spoken description for Unicode code point U+1F4EF: "📯" POSTAL HORN --> + <string name="spoken_emoji_1F4EF">Postal horn</string> + <!-- Spoken description for Unicode code point U+1F4F0: "📰" NEWSPAPER --> + <string name="spoken_emoji_1F4F0">Newspaper</string> + <!-- Spoken description for Unicode code point U+1F4F1: "📱" MOBILE PHONE --> + <string name="spoken_emoji_1F4F1">Mobile phone</string> + <!-- Spoken description for Unicode code point U+1F4F2: "📲" MOBILE PHONE WITH RIGHTWARDS ARROW AT LEFT --> + <string name="spoken_emoji_1F4F2">Mobile phone with rightwards arrow at left</string> + <!-- Spoken description for Unicode code point U+1F4F3: "📳" VIBRATION MODE --> + <string name="spoken_emoji_1F4F3">Vibration mode</string> + <!-- Spoken description for Unicode code point U+1F4F4: "📴" MOBILE PHONE OFF --> + <string name="spoken_emoji_1F4F4">Mobile phone off</string> + <!-- Spoken description for Unicode code point U+1F4F5: "📵" NO MOBILE PHONES --> + <string name="spoken_emoji_1F4F5">No mobile phones</string> + <!-- Spoken description for Unicode code point U+1F4F6: "📶" ANTENNA WITH BARS --> + <string name="spoken_emoji_1F4F6">Antenna with bars</string> + <!-- Spoken description for Unicode code point U+1F4F7: "📷" CAMERA --> + <string name="spoken_emoji_1F4F7">Camera</string> + <!-- Spoken description for Unicode code point U+1F4F9: "📹" VIDEO CAMERA --> + <string name="spoken_emoji_1F4F9">Video camera</string> + <!-- Spoken description for Unicode code point U+1F4FA: "📺" TELEVISION --> + <string name="spoken_emoji_1F4FA">Television</string> + <!-- Spoken description for Unicode code point U+1F4FB: "📻" RADIO --> + <string name="spoken_emoji_1F4FB">Radio</string> + <!-- Spoken description for Unicode code point U+1F4FC: "📼" VIDEOCASSETTE --> + <string name="spoken_emoji_1F4FC">Videocassette</string> + <!-- Spoken description for Unicode code point U+1F500: "🔀" TWISTED RIGHTWARDS ARROWS --> + <string name="spoken_emoji_1F500">Twisted rightwards arrows</string> + <!-- Spoken description for Unicode code point U+1F501: "🔁" CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS --> + <string name="spoken_emoji_1F501">Clockwise rightwards and leftwards open circle arrows</string> + <!-- Spoken description for Unicode code point U+1F502: "🔂" CLOCKWISE RIGHTWARDS AND LEFTWARDS OPEN CIRCLE ARROWS WITH CIRCLED ONE OVERLAY --> + <string name="spoken_emoji_1F502">Clockwise rightwards and leftwards open circle arrows with circled one overlay</string> + <!-- Spoken description for Unicode code point U+1F503: "🔃" CLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS --> + <string name="spoken_emoji_1F503">Clockwise downwards and upwards open circle arrows</string> + <!-- Spoken description for Unicode code point U+1F504: "🔄" ANTICLOCKWISE DOWNWARDS AND UPWARDS OPEN CIRCLE ARROWS --> + <string name="spoken_emoji_1F504">Anticlockwise downwards and upwards open circle arrows</string> + <!-- Spoken description for Unicode code point U+1F505: "🔅" LOW BRIGHTNESS SYMBOL --> + <string name="spoken_emoji_1F505">Low brightness symbol</string> + <!-- Spoken description for Unicode code point U+1F506: "🔆" HIGH BRIGHTNESS SYMBOL --> + <string name="spoken_emoji_1F506">High brightness symbol</string> + <!-- Spoken description for Unicode code point U+1F507: "🔇" SPEAKER WITH CANCELLATION STROKE --> + <string name="spoken_emoji_1F507">Speaker with cancellation stroke</string> + <!-- Spoken description for Unicode code point U+1F508: "🔈" SPEAKER --> + <string name="spoken_emoji_1F508">Speaker</string> + <!-- Spoken description for Unicode code point U+1F509: "🔉" SPEAKER WITH ONE SOUND WAVE --> + <string name="spoken_emoji_1F509">Speaker with one sound wave</string> + <!-- Spoken description for Unicode code point U+1F50A: "🔊" SPEAKER WITH THREE SOUND WAVES --> + <string name="spoken_emoji_1F50A">Speaker with three sound waves</string> + <!-- Spoken description for Unicode code point U+1F50B: "🔋" BATTERY --> + <string name="spoken_emoji_1F50B">Battery</string> + <!-- Spoken description for Unicode code point U+1F50C: "🔌" ELECTRIC PLUG --> + <string name="spoken_emoji_1F50C">Electric plug</string> + <!-- Spoken description for Unicode code point U+1F50D: "🔍" LEFT-POINTING MAGNIFYING GLASS --> + <string name="spoken_emoji_1F50D">Left-pointing magnifying glass</string> + <!-- Spoken description for Unicode code point U+1F50E: "🔎" RIGHT-POINTING MAGNIFYING GLASS --> + <string name="spoken_emoji_1F50E">Right-pointing magnifying glass</string> + <!-- Spoken description for Unicode code point U+1F50F: "🔏" LOCK WITH INK PEN --> + <string name="spoken_emoji_1F50F">Lock with ink pen</string> + <!-- Spoken description for Unicode code point U+1F510: "🔐" CLOSED LOCK WITH KEY --> + <string name="spoken_emoji_1F510">Closed lock with key</string> + <!-- Spoken description for Unicode code point U+1F511: "🔑" KEY --> + <string name="spoken_emoji_1F511">Key</string> + <!-- Spoken description for Unicode code point U+1F512: "🔒" LOCK --> + <string name="spoken_emoji_1F512">Lock</string> + <!-- Spoken description for Unicode code point U+1F513: "🔓" OPEN LOCK --> + <string name="spoken_emoji_1F513">Open lock</string> + <!-- Spoken description for Unicode code point U+1F514: "🔔" BELL --> + <string name="spoken_emoji_1F514">Bell</string> + <!-- Spoken description for Unicode code point U+1F515: "🔕" BELL WITH CANCELLATION STROKE --> + <string name="spoken_emoji_1F515">Bell with cancellation stroke</string> + <!-- Spoken description for Unicode code point U+1F516: "🔖" BOOKMARK --> + <string name="spoken_emoji_1F516">Bookmark</string> + <!-- Spoken description for Unicode code point U+1F517: "🔗" LINK SYMBOL --> + <string name="spoken_emoji_1F517">Link symbol</string> + <!-- Spoken description for Unicode code point U+1F518: "🔘" RADIO BUTTON --> + <string name="spoken_emoji_1F518">Radio button</string> + <!-- Spoken description for Unicode code point U+1F519: "🔙" BACK WITH LEFTWARDS ARROW ABOVE --> + <string name="spoken_emoji_1F519">Back with leftwards arrow above</string> + <!-- Spoken description for Unicode code point U+1F51A: "🔚" END WITH LEFTWARDS ARROW ABOVE --> + <string name="spoken_emoji_1F51A">End with leftwards arrow above</string> + <!-- Spoken description for Unicode code point U+1F51B: "🔛" ON WITH EXCLAMATION MARK WITH LEFT RIGHT ARROW ABOVE --> + <string name="spoken_emoji_1F51B">On with exclamation mark with left right arrow above</string> + <!-- Spoken description for Unicode code point U+1F51C: "🔜" SOON WITH RIGHTWARDS ARROW ABOVE --> + <string name="spoken_emoji_1F51C">Soon with rightwards arrow above</string> + <!-- Spoken description for Unicode code point U+1F51D: "🔝" TOP WITH UPWARDS ARROW ABOVE --> + <string name="spoken_emoji_1F51D">Top with upwards arrow above</string> + <!-- Spoken description for Unicode code point U+1F51E: "🔞" NO ONE UNDER EIGHTEEN SYMBOL --> + <string name="spoken_emoji_1F51E">No one under eighteen symbol</string> + <!-- Spoken description for Unicode code point U+1F51F: "🔟" KEYCAP TEN --> + <string name="spoken_emoji_1F51F">Keycap ten</string> + <!-- Spoken description for Unicode code point U+1F520: "🔠" INPUT SYMBOL FOR LATIN CAPITAL LETTERS --> + <string name="spoken_emoji_1F520">Input symbol for latin capital letters</string> + <!-- Spoken description for Unicode code point U+1F521: "🔡" INPUT SYMBOL FOR LATIN SMALL LETTERS --> + <string name="spoken_emoji_1F521">Input symbol for latin small letters</string> + <!-- Spoken description for Unicode code point U+1F522: "🔢" INPUT SYMBOL FOR NUMBERS --> + <string name="spoken_emoji_1F522">Input symbol for numbers</string> + <!-- Spoken description for Unicode code point U+1F523: "🔣" INPUT SYMBOL FOR SYMBOLS --> + <string name="spoken_emoji_1F523">Input symbol for symbols</string> + <!-- Spoken description for Unicode code point U+1F524: "🔤" INPUT SYMBOL FOR LATIN LETTERS --> + <string name="spoken_emoji_1F524">Input symbol for latin letters</string> + <!-- Spoken description for Unicode code point U+1F525: "🔥" FIRE --> + <string name="spoken_emoji_1F525">Fire</string> + <!-- Spoken description for Unicode code point U+1F526: "🔦" ELECTRIC TORCH --> + <string name="spoken_emoji_1F526">Electric torch</string> + <!-- Spoken description for Unicode code point U+1F527: "🔧" WRENCH --> + <string name="spoken_emoji_1F527">Wrench</string> + <!-- Spoken description for Unicode code point U+1F528: "🔨" HAMMER --> + <string name="spoken_emoji_1F528">Hammer</string> + <!-- Spoken description for Unicode code point U+1F529: "🔩" NUT AND BOLT --> + <string name="spoken_emoji_1F529">Nut and bolt</string> + <!-- Spoken description for Unicode code point U+1F52A: "🔪" HOCHO --> + <string name="spoken_emoji_1F52A">Hocho</string> + <!-- Spoken description for Unicode code point U+1F52B: "🔫" PISTOL --> + <string name="spoken_emoji_1F52B">Pistol</string> + <!-- Spoken description for Unicode code point U+1F52C: "🔬" MICROSCOPE --> + <string name="spoken_emoji_1F52C">Microscope</string> + <!-- Spoken description for Unicode code point U+1F52D: "🔭" TELESCOPE --> + <string name="spoken_emoji_1F52D">Telescope</string> + <!-- Spoken description for Unicode code point U+1F52E: "🔮" CRYSTAL BALL --> + <string name="spoken_emoji_1F52E">Crystal ball</string> + <!-- Spoken description for Unicode code point U+1F52F: "🔯" SIX POINTED STAR WITH MIDDLE DOT --> + <string name="spoken_emoji_1F52F">Six pointed star with middle dot</string> + <!-- Spoken description for Unicode code point U+1F530: "🔰" JAPANESE SYMBOL FOR BEGINNER --> + <string name="spoken_emoji_1F530">Japanese symbol for beginner</string> + <!-- Spoken description for Unicode code point U+1F531: "🔱" TRIDENT EMBLEM --> + <string name="spoken_emoji_1F531">Trident emblem</string> + <!-- Spoken description for Unicode code point U+1F532: "🔲" BLACK SQUARE BUTTON --> + <string name="spoken_emoji_1F532">Black square button</string> + <!-- Spoken description for Unicode code point U+1F533: "🔳" WHITE SQUARE BUTTON --> + <string name="spoken_emoji_1F533">White square button</string> + <!-- Spoken description for Unicode code point U+1F534: "🔴" LARGE RED CIRCLE --> + <string name="spoken_emoji_1F534">Large red circle</string> + <!-- Spoken description for Unicode code point U+1F535: "🔵" LARGE BLUE CIRCLE --> + <string name="spoken_emoji_1F535">Large blue circle</string> + <!-- Spoken description for Unicode code point U+1F536: "🔶" LARGE ORANGE DIAMOND --> + <string name="spoken_emoji_1F536">Large orange diamond</string> + <!-- Spoken description for Unicode code point U+1F537: "🔷" LARGE BLUE DIAMOND --> + <string name="spoken_emoji_1F537">Large blue diamond</string> + <!-- Spoken description for Unicode code point U+1F538: "🔸" SMALL ORANGE DIAMOND --> + <string name="spoken_emoji_1F538">Small orange diamond</string> + <!-- Spoken description for Unicode code point U+1F539: "🔹" SMALL BLUE DIAMOND --> + <string name="spoken_emoji_1F539">Small blue diamond</string> + <!-- Spoken description for Unicode code point U+1F53A: "🔺" UP-POINTING RED TRIANGLE --> + <string name="spoken_emoji_1F53A">Up-pointing red triangle</string> + <!-- Spoken description for Unicode code point U+1F53B: "🔻" DOWN-POINTING RED TRIANGLE --> + <string name="spoken_emoji_1F53B">Down-pointing red triangle</string> + <!-- Spoken description for Unicode code point U+1F53C: "🔼" UP-POINTING SMALL RED TRIANGLE --> + <string name="spoken_emoji_1F53C">Up-pointing small red triangle</string> + <!-- Spoken description for Unicode code point U+1F53D: "🔽" DOWN-POINTING SMALL RED TRIANGLE --> + <string name="spoken_emoji_1F53D">Down-pointing small red triangle</string> + <!-- Spoken description for Unicode code point U+1F550: "🕐" CLOCK FACE ONE OCLOCK --> + <string name="spoken_emoji_1F550">Clock face one oclock</string> + <!-- Spoken description for Unicode code point U+1F551: "🕑" CLOCK FACE TWO OCLOCK --> + <string name="spoken_emoji_1F551">Clock face two oclock</string> + <!-- Spoken description for Unicode code point U+1F552: "🕒" CLOCK FACE THREE OCLOCK --> + <string name="spoken_emoji_1F552">Clock face three oclock</string> + <!-- Spoken description for Unicode code point U+1F553: "🕓" CLOCK FACE FOUR OCLOCK --> + <string name="spoken_emoji_1F553">Clock face four oclock</string> + <!-- Spoken description for Unicode code point U+1F554: "🕔" CLOCK FACE FIVE OCLOCK --> + <string name="spoken_emoji_1F554">Clock face five oclock</string> + <!-- Spoken description for Unicode code point U+1F555: "🕕" CLOCK FACE SIX OCLOCK --> + <string name="spoken_emoji_1F555">Clock face six oclock</string> + <!-- Spoken description for Unicode code point U+1F556: "🕖" CLOCK FACE SEVEN OCLOCK --> + <string name="spoken_emoji_1F556">Clock face seven oclock</string> + <!-- Spoken description for Unicode code point U+1F557: "🕗" CLOCK FACE EIGHT OCLOCK --> + <string name="spoken_emoji_1F557">Clock face eight oclock</string> + <!-- Spoken description for Unicode code point U+1F558: "🕘" CLOCK FACE NINE OCLOCK --> + <string name="spoken_emoji_1F558">Clock face nine oclock</string> + <!-- Spoken description for Unicode code point U+1F559: "🕙" CLOCK FACE TEN OCLOCK --> + <string name="spoken_emoji_1F559">Clock face ten oclock</string> + <!-- Spoken description for Unicode code point U+1F55A: "🕚" CLOCK FACE ELEVEN OCLOCK --> + <string name="spoken_emoji_1F55A">Clock face eleven oclock</string> + <!-- Spoken description for Unicode code point U+1F55B: "🕛" CLOCK FACE TWELVE OCLOCK --> + <string name="spoken_emoji_1F55B">Clock face twelve oclock</string> + <!-- Spoken description for Unicode code point U+1F55C: "🕜" CLOCK FACE ONE-THIRTY --> + <string name="spoken_emoji_1F55C">Clock face one-thirty</string> + <!-- Spoken description for Unicode code point U+1F55D: "🕝" CLOCK FACE TWO-THIRTY --> + <string name="spoken_emoji_1F55D">Clock face two-thirty</string> + <!-- Spoken description for Unicode code point U+1F55E: "🕞" CLOCK FACE THREE-THIRTY --> + <string name="spoken_emoji_1F55E">Clock face three-thirty</string> + <!-- Spoken description for Unicode code point U+1F55F: "🕟" CLOCK FACE FOUR-THIRTY --> + <string name="spoken_emoji_1F55F">Clock face four-thirty</string> + <!-- Spoken description for Unicode code point U+1F560: "🕠" CLOCK FACE FIVE-THIRTY --> + <string name="spoken_emoji_1F560">Clock face five-thirty</string> + <!-- Spoken description for Unicode code point U+1F561: "🕡" CLOCK FACE SIX-THIRTY --> + <string name="spoken_emoji_1F561">Clock face six-thirty</string> + <!-- Spoken description for Unicode code point U+1F562: "🕢" CLOCK FACE SEVEN-THIRTY --> + <string name="spoken_emoji_1F562">Clock face seven-thirty</string> + <!-- Spoken description for Unicode code point U+1F563: "🕣" CLOCK FACE EIGHT-THIRTY --> + <string name="spoken_emoji_1F563">Clock face eight-thirty</string> + <!-- Spoken description for Unicode code point U+1F564: "🕤" CLOCK FACE NINE-THIRTY --> + <string name="spoken_emoji_1F564">Clock face nine-thirty</string> + <!-- Spoken description for Unicode code point U+1F565: "🕥" CLOCK FACE TEN-THIRTY --> + <string name="spoken_emoji_1F565">Clock face ten-thirty</string> + <!-- Spoken description for Unicode code point U+1F566: "🕦" CLOCK FACE ELEVEN-THIRTY --> + <string name="spoken_emoji_1F566">Clock face eleven-thirty</string> + <!-- Spoken description for Unicode code point U+1F567: "🕧" CLOCK FACE TWELVE-THIRTY --> + <string name="spoken_emoji_1F567">Clock face twelve-thirty</string> + <!-- Spoken description for Unicode code point U+1F5FB: "🗻" MOUNT FUJI --> + <string name="spoken_emoji_1F5FB">Mount fuji</string> + <!-- Spoken description for Unicode code point U+1F5FC: "🗼" TOKYO TOWER --> + <string name="spoken_emoji_1F5FC">Tokyo tower</string> + <!-- Spoken description for Unicode code point U+1F5FD: "🗽" STATUE OF LIBERTY --> + <string name="spoken_emoji_1F5FD">Statue of liberty</string> + <!-- Spoken description for Unicode code point U+1F5FE: "🗾" SILHOUETTE OF JAPAN --> + <string name="spoken_emoji_1F5FE">Silhouette of japan</string> + <!-- Spoken description for Unicode code point U+1F5FF: "🗿" MOYAI --> + <string name="spoken_emoji_1F5FF">Moyai</string> + <!-- Spoken description for Unicode code point U+1F600: "😀" GRINNING FACE --> + <string name="spoken_emoji_1F600">Grinning face</string> + <!-- Spoken description for Unicode code point U+1F601: "😁" GRINNING FACE WITH SMILING EYES --> + <string name="spoken_emoji_1F601">Grinning face with smiling eyes</string> + <!-- Spoken description for Unicode code point U+1F602: "😂" FACE WITH TEARS OF JOY --> + <string name="spoken_emoji_1F602">Face with tears of joy</string> + <!-- Spoken description for Unicode code point U+1F603: "😃" SMILING FACE WITH OPEN MOUTH --> + <string name="spoken_emoji_1F603">Smiling face with open mouth</string> + <!-- Spoken description for Unicode code point U+1F604: "😄" SMILING FACE WITH OPEN MOUTH AND SMILING EYES --> + <string name="spoken_emoji_1F604">Smiling face with open mouth and smiling eyes</string> + <!-- Spoken description for Unicode code point U+1F605: "😅" SMILING FACE WITH OPEN MOUTH AND COLD SWEAT --> + <string name="spoken_emoji_1F605">Smiling face with open mouth and cold sweat</string> + <!-- Spoken description for Unicode code point U+1F606: "😆" SMILING FACE WITH OPEN MOUTH AND TIGHTLY-CLOSED EYES --> + <string name="spoken_emoji_1F606">Smiling face with open mouth and tightly-closed eyes</string> + <!-- Spoken description for Unicode code point U+1F607: "😇" SMILING FACE WITH HALO --> + <string name="spoken_emoji_1F607">Smiling face with halo</string> + <!-- Spoken description for Unicode code point U+1F608: "😈" SMILING FACE WITH HORNS --> + <string name="spoken_emoji_1F608">Smiling face with horns</string> + <!-- Spoken description for Unicode code point U+1F609: "😉" WINKING FACE --> + <string name="spoken_emoji_1F609">Winking face</string> + <!-- Spoken description for Unicode code point U+1F60A: "😊" SMILING FACE WITH SMILING EYES --> + <string name="spoken_emoji_1F60A">Smiling face with smiling eyes</string> + <!-- Spoken description for Unicode code point U+1F60B: "😋" FACE SAVOURING DELICIOUS FOOD --> + <string name="spoken_emoji_1F60B">Face savouring delicious food</string> + <!-- Spoken description for Unicode code point U+1F60C: "😌" RELIEVED FACE --> + <string name="spoken_emoji_1F60C">Relieved face</string> + <!-- Spoken description for Unicode code point U+1F60D: "😍" SMILING FACE WITH HEART-SHAPED EYES --> + <string name="spoken_emoji_1F60D">Smiling face with heart-shaped eyes</string> + <!-- Spoken description for Unicode code point U+1F60E: "😎" SMILING FACE WITH SUNGLASSES --> + <string name="spoken_emoji_1F60E">Smiling face with sunglasses</string> + <!-- Spoken description for Unicode code point U+1F60F: "😏" SMIRKING FACE --> + <string name="spoken_emoji_1F60F">Smirking face</string> + <!-- Spoken description for Unicode code point U+1F610: "😐" NEUTRAL FACE --> + <string name="spoken_emoji_1F610">Neutral face</string> + <!-- Spoken description for Unicode code point U+1F611: "😑" EXPRESSIONLESS FACE --> + <string name="spoken_emoji_1F611">Expressionless face</string> + <!-- Spoken description for Unicode code point U+1F612: "😒" UNAMUSED FACE --> + <string name="spoken_emoji_1F612">Unamused face</string> + <!-- Spoken description for Unicode code point U+1F613: "😓" FACE WITH COLD SWEAT --> + <string name="spoken_emoji_1F613">Face with cold sweat</string> + <!-- Spoken description for Unicode code point U+1F614: "😔" PENSIVE FACE --> + <string name="spoken_emoji_1F614">Pensive face</string> + <!-- Spoken description for Unicode code point U+1F615: "😕" CONFUSED FACE --> + <string name="spoken_emoji_1F615">Confused face</string> + <!-- Spoken description for Unicode code point U+1F616: "😖" CONFOUNDED FACE --> + <string name="spoken_emoji_1F616">Confounded face</string> + <!-- Spoken description for Unicode code point U+1F617: "😗" KISSING FACE --> + <string name="spoken_emoji_1F617">Kissing face</string> + <!-- Spoken description for Unicode code point U+1F618: "😘" FACE THROWING A KISS --> + <string name="spoken_emoji_1F618">Face throwing a kiss</string> + <!-- Spoken description for Unicode code point U+1F619: "😙" KISSING FACE WITH SMILING EYES --> + <string name="spoken_emoji_1F619">Kissing face with smiling eyes</string> + <!-- Spoken description for Unicode code point U+1F61A: "😚" KISSING FACE WITH CLOSED EYES --> + <string name="spoken_emoji_1F61A">Kissing face with closed eyes</string> + <!-- Spoken description for Unicode code point U+1F61B: "😛" FACE WITH STUCK-OUT TONGUE --> + <string name="spoken_emoji_1F61B">Face with stuck-out tongue</string> + <!-- Spoken description for Unicode code point U+1F61C: "😜" FACE WITH STUCK-OUT TONGUE AND WINKING EYE --> + <string name="spoken_emoji_1F61C">Face with stuck-out tongue and winking eye</string> + <!-- Spoken description for Unicode code point U+1F61D: "😝" FACE WITH STUCK-OUT TONGUE AND TIGHTLY-CLOSED EYES --> + <string name="spoken_emoji_1F61D">Face with stuck-out tongue and tightly-closed eyes</string> + <!-- Spoken description for Unicode code point U+1F61E: "😞" DISAPPOINTED FACE --> + <string name="spoken_emoji_1F61E">Disappointed face</string> + <!-- Spoken description for Unicode code point U+1F61F: "😟" WORRIED FACE --> + <string name="spoken_emoji_1F61F">Worried face</string> + <!-- Spoken description for Unicode code point U+1F620: "😠" ANGRY FACE --> + <string name="spoken_emoji_1F620">Angry face</string> + <!-- Spoken description for Unicode code point U+1F621: "😡" POUTING FACE --> + <string name="spoken_emoji_1F621">Pouting face</string> + <!-- Spoken description for Unicode code point U+1F622: "😢" CRYING FACE --> + <string name="spoken_emoji_1F622">Crying face</string> + <!-- Spoken description for Unicode code point U+1F623: "😣" PERSEVERING FACE --> + <string name="spoken_emoji_1F623">Persevering face</string> + <!-- Spoken description for Unicode code point U+1F624: "😤" FACE WITH LOOK OF TRIUMPH --> + <string name="spoken_emoji_1F624">Face with look of triumph</string> + <!-- Spoken description for Unicode code point U+1F625: "😥" DISAPPOINTED BUT RELIEVED FACE --> + <string name="spoken_emoji_1F625">Disappointed but relieved face</string> + <!-- Spoken description for Unicode code point U+1F626: "😦" FROWNING FACE WITH OPEN MOUTH --> + <string name="spoken_emoji_1F626">Frowning face with open mouth</string> + <!-- Spoken description for Unicode code point U+1F627: "😧" ANGUISHED FACE --> + <string name="spoken_emoji_1F627">Anguished face</string> + <!-- Spoken description for Unicode code point U+1F628: "😨" FEARFUL FACE --> + <string name="spoken_emoji_1F628">Fearful face</string> + <!-- Spoken description for Unicode code point U+1F629: "😩" WEARY FACE --> + <string name="spoken_emoji_1F629">Weary face</string> + <!-- Spoken description for Unicode code point U+1F62A: "😪" SLEEPY FACE --> + <string name="spoken_emoji_1F62A">Sleepy face</string> + <!-- Spoken description for Unicode code point U+1F62B: "😫" TIRED FACE --> + <string name="spoken_emoji_1F62B">Tired face</string> + <!-- Spoken description for Unicode code point U+1F62C: "😬" GRIMACING FACE --> + <string name="spoken_emoji_1F62C">Grimacing face</string> + <!-- Spoken description for Unicode code point U+1F62D: "😭" LOUDLY CRYING FACE --> + <string name="spoken_emoji_1F62D">Loudly crying face</string> + <!-- Spoken description for Unicode code point U+1F62E: "😮" FACE WITH OPEN MOUTH --> + <string name="spoken_emoji_1F62E">Face with open mouth</string> + <!-- Spoken description for Unicode code point U+1F62F: "😯" HUSHED FACE --> + <string name="spoken_emoji_1F62F">Hushed face</string> + <!-- Spoken description for Unicode code point U+1F630: "😰" FACE WITH OPEN MOUTH AND COLD SWEAT --> + <string name="spoken_emoji_1F630">Face with open mouth and cold sweat</string> + <!-- Spoken description for Unicode code point U+1F631: "😱" FACE SCREAMING IN FEAR --> + <string name="spoken_emoji_1F631">Face screaming in fear</string> + <!-- Spoken description for Unicode code point U+1F632: "😲" ASTONISHED FACE --> + <string name="spoken_emoji_1F632">Astonished face</string> + <!-- Spoken description for Unicode code point U+1F633: "😳" FLUSHED FACE --> + <string name="spoken_emoji_1F633">Flushed face</string> + <!-- Spoken description for Unicode code point U+1F634: "😴" SLEEPING FACE --> + <string name="spoken_emoji_1F634">Sleeping face</string> + <!-- Spoken description for Unicode code point U+1F635: "😵" DIZZY FACE --> + <string name="spoken_emoji_1F635">Dizzy face</string> + <!-- Spoken description for Unicode code point U+1F636: "😶" FACE WITHOUT MOUTH --> + <string name="spoken_emoji_1F636">Face without mouth</string> + <!-- Spoken description for Unicode code point U+1F637: "😷" FACE WITH MEDICAL MASK --> + <string name="spoken_emoji_1F637">Face with medical mask</string> + <!-- Spoken description for Unicode code point U+1F638: "😸" GRINNING CAT FACE WITH SMILING EYES --> + <string name="spoken_emoji_1F638">Grinning cat face with smiling eyes</string> + <!-- Spoken description for Unicode code point U+1F639: "😹" CAT FACE WITH TEARS OF JOY --> + <string name="spoken_emoji_1F639">Cat face with tears of joy</string> + <!-- Spoken description for Unicode code point U+1F63A: "😺" SMILING CAT FACE WITH OPEN MOUTH --> + <string name="spoken_emoji_1F63A">Smiling cat face with open mouth</string> + <!-- Spoken description for Unicode code point U+1F63B: "😻" SMILING CAT FACE WITH HEART-SHAPED EYES --> + <string name="spoken_emoji_1F63B">Smiling cat face with heart-shaped eyes</string> + <!-- Spoken description for Unicode code point U+1F63C: "😼" CAT FACE WITH WRY SMILE --> + <string name="spoken_emoji_1F63C">Cat face with wry smile</string> + <!-- Spoken description for Unicode code point U+1F63D: "😽" KISSING CAT FACE WITH CLOSED EYES --> + <string name="spoken_emoji_1F63D">Kissing cat face with closed eyes</string> + <!-- Spoken description for Unicode code point U+1F63E: "😾" POUTING CAT FACE --> + <string name="spoken_emoji_1F63E">Pouting cat face</string> + <!-- Spoken description for Unicode code point U+1F63F: "😿" CRYING CAT FACE --> + <string name="spoken_emoji_1F63F">Crying cat face</string> + <!-- Spoken description for Unicode code point U+1F640: "🙀" WEARY CAT FACE --> + <string name="spoken_emoji_1F640">Weary cat face</string> + <!-- Spoken description for Unicode code point U+1F645: "🙅" FACE WITH NO GOOD GESTURE --> + <string name="spoken_emoji_1F645">Face with no good gesture</string> + <!-- Spoken description for Unicode code point U+1F646: "🙆" FACE WITH OK GESTURE --> + <string name="spoken_emoji_1F646">Face with ok gesture</string> + <!-- Spoken description for Unicode code point U+1F647: "🙇" PERSON BOWING DEEPLY --> + <string name="spoken_emoji_1F647">Person bowing deeply</string> + <!-- Spoken description for Unicode code point U+1F648: "🙈" SEE-NO-EVIL MONKEY --> + <string name="spoken_emoji_1F648">See-no-evil monkey</string> + <!-- Spoken description for Unicode code point U+1F649: "🙉" HEAR-NO-EVIL MONKEY --> + <string name="spoken_emoji_1F649">Hear-no-evil monkey</string> + <!-- Spoken description for Unicode code point U+1F64A: "🙊" SPEAK-NO-EVIL MONKEY --> + <string name="spoken_emoji_1F64A">Speak-no-evil monkey</string> + <!-- Spoken description for Unicode code point U+1F64B: "🙋" HAPPY PERSON RAISING ONE HAND --> + <string name="spoken_emoji_1F64B">Happy person raising one hand</string> + <!-- Spoken description for Unicode code point U+1F64C: "🙌" PERSON RAISING BOTH HANDS IN CELEBRATION --> + <string name="spoken_emoji_1F64C">Person raising both hands in celebration</string> + <!-- Spoken description for Unicode code point U+1F64D: "🙍" PERSON FROWNING --> + <string name="spoken_emoji_1F64D">Person frowning</string> + <!-- Spoken description for Unicode code point U+1F64E: "🙎" PERSON WITH POUTING FACE --> + <string name="spoken_emoji_1F64E">Person with pouting face</string> + <!-- Spoken description for Unicode code point U+1F64F: "🙏" PERSON WITH FOLDED HANDS --> + <string name="spoken_emoji_1F64F">Person with folded hands</string> + <!-- Spoken description for Unicode code point U+1F680: "🚀" ROCKET --> + <string name="spoken_emoji_1F680">Rocket</string> + <!-- Spoken description for Unicode code point U+1F681: "🚁" HELICOPTER --> + <string name="spoken_emoji_1F681">Helicopter</string> + <!-- Spoken description for Unicode code point U+1F682: "🚂" STEAM LOCOMOTIVE --> + <string name="spoken_emoji_1F682">Steam locomotive</string> + <!-- Spoken description for Unicode code point U+1F683: "🚃" RAILWAY CAR --> + <string name="spoken_emoji_1F683">Railway car</string> + <!-- Spoken description for Unicode code point U+1F684: "🚄" HIGH-SPEED TRAIN --> + <string name="spoken_emoji_1F684">High-speed train</string> + <!-- Spoken description for Unicode code point U+1F685: "🚅" HIGH-SPEED TRAIN WITH BULLET NOSE --> + <string name="spoken_emoji_1F685">High-speed train with bullet nose</string> + <!-- Spoken description for Unicode code point U+1F686: "🚆" TRAIN --> + <string name="spoken_emoji_1F686">Train</string> + <!-- Spoken description for Unicode code point U+1F687: "🚇" METRO --> + <string name="spoken_emoji_1F687">Metro</string> + <!-- Spoken description for Unicode code point U+1F688: "🚈" LIGHT RAIL --> + <string name="spoken_emoji_1F688">Light rail</string> + <!-- Spoken description for Unicode code point U+1F689: "🚉" STATION --> + <string name="spoken_emoji_1F689">Station</string> + <!-- Spoken description for Unicode code point U+1F68A: "🚊" TRAM --> + <string name="spoken_emoji_1F68A">Tram</string> + <!-- Spoken description for Unicode code point U+1F68B: "🚋" TRAM CAR --> + <string name="spoken_emoji_1F68B">Tram car</string> + <!-- Spoken description for Unicode code point U+1F68C: "🚌" BUS --> + <string name="spoken_emoji_1F68C">Bus</string> + <!-- Spoken description for Unicode code point U+1F68D: "🚍" ONCOMING BUS --> + <string name="spoken_emoji_1F68D">Oncoming bus</string> + <!-- Spoken description for Unicode code point U+1F68E: "🚎" TROLLEYBUS --> + <string name="spoken_emoji_1F68E">Trolleybus</string> + <!-- Spoken description for Unicode code point U+1F68F: "🚏" BUS STOP --> + <string name="spoken_emoji_1F68F">Bus stop</string> + <!-- Spoken description for Unicode code point U+1F690: "🚐" MINIBUS --> + <string name="spoken_emoji_1F690">Minibus</string> + <!-- Spoken description for Unicode code point U+1F691: "🚑" AMBULANCE --> + <string name="spoken_emoji_1F691">Ambulance</string> + <!-- Spoken description for Unicode code point U+1F692: "🚒" FIRE ENGINE --> + <string name="spoken_emoji_1F692">Fire engine</string> + <!-- Spoken description for Unicode code point U+1F693: "🚓" POLICE CAR --> + <string name="spoken_emoji_1F693">Police car</string> + <!-- Spoken description for Unicode code point U+1F694: "🚔" ONCOMING POLICE CAR --> + <string name="spoken_emoji_1F694">Oncoming police car</string> + <!-- Spoken description for Unicode code point U+1F695: "🚕" TAXI --> + <string name="spoken_emoji_1F695">Taxi</string> + <!-- Spoken description for Unicode code point U+1F696: "🚖" ONCOMING TAXI --> + <string name="spoken_emoji_1F696">Oncoming taxi</string> + <!-- Spoken description for Unicode code point U+1F697: "🚗" AUTOMOBILE --> + <string name="spoken_emoji_1F697">Automobile</string> + <!-- Spoken description for Unicode code point U+1F698: "🚘" ONCOMING AUTOMOBILE --> + <string name="spoken_emoji_1F698">Oncoming automobile</string> + <!-- Spoken description for Unicode code point U+1F699: "🚙" RECREATIONAL VEHICLE --> + <string name="spoken_emoji_1F699">Recreational vehicle</string> + <!-- Spoken description for Unicode code point U+1F69A: "🚚" DELIVERY TRUCK --> + <string name="spoken_emoji_1F69A">Delivery truck</string> + <!-- Spoken description for Unicode code point U+1F69B: "🚛" ARTICULATED LORRY --> + <string name="spoken_emoji_1F69B">Articulated lorry</string> + <!-- Spoken description for Unicode code point U+1F69C: "🚜" TRACTOR --> + <string name="spoken_emoji_1F69C">Tractor</string> + <!-- Spoken description for Unicode code point U+1F69D: "🚝" MONORAIL --> + <string name="spoken_emoji_1F69D">Monorail</string> + <!-- Spoken description for Unicode code point U+1F69E: "🚞" MOUNTAIN RAILWAY --> + <string name="spoken_emoji_1F69E">Mountain railway</string> + <!-- Spoken description for Unicode code point U+1F69F: "🚟" SUSPENSION RAILWAY --> + <string name="spoken_emoji_1F69F">Suspension railway</string> + <!-- Spoken description for Unicode code point U+1F6A0: "🚠" MOUNTAIN CABLEWAY --> + <string name="spoken_emoji_1F6A0">Mountain cableway</string> + <!-- Spoken description for Unicode code point U+1F6A1: "🚡" AERIAL TRAMWAY --> + <string name="spoken_emoji_1F6A1">Aerial tramway</string> + <!-- Spoken description for Unicode code point U+1F6A2: "🚢" SHIP --> + <string name="spoken_emoji_1F6A2">Ship</string> + <!-- Spoken description for Unicode code point U+1F6A3: "🚣" ROWBOAT --> + <string name="spoken_emoji_1F6A3">Rowboat</string> + <!-- Spoken description for Unicode code point U+1F6A4: "🚤" SPEEDBOAT --> + <string name="spoken_emoji_1F6A4">Speedboat</string> + <!-- Spoken description for Unicode code point U+1F6A5: "🚥" HORIZONTAL TRAFFIC LIGHT --> + <string name="spoken_emoji_1F6A5">Horizontal traffic light</string> + <!-- Spoken description for Unicode code point U+1F6A6: "🚦" VERTICAL TRAFFIC LIGHT --> + <string name="spoken_emoji_1F6A6">Vertical traffic light</string> + <!-- Spoken description for Unicode code point U+1F6A7: "🚧" CONSTRUCTION SIGN --> + <string name="spoken_emoji_1F6A7">Construction sign</string> + <!-- Spoken description for Unicode code point U+1F6A8: "🚨" POLICE CARS REVOLVING LIGHT --> + <string name="spoken_emoji_1F6A8">Police cars revolving light</string> + <!-- Spoken description for Unicode code point U+1F6A9: "🚩" TRIANGULAR FLAG ON POST --> + <string name="spoken_emoji_1F6A9">Triangular flag on post</string> + <!-- Spoken description for Unicode code point U+1F6AA: "🚪" DOOR --> + <string name="spoken_emoji_1F6AA">Door</string> + <!-- Spoken description for Unicode code point U+1F6AB: "🚫" NO ENTRY SIGN --> + <string name="spoken_emoji_1F6AB">No entry sign</string> + <!-- Spoken description for Unicode code point U+1F6AC: "🚬" SMOKING SYMBOL --> + <string name="spoken_emoji_1F6AC">Smoking symbol</string> + <!-- Spoken description for Unicode code point U+1F6AD: "🚭" NO SMOKING SYMBOL --> + <string name="spoken_emoji_1F6AD">No smoking symbol</string> + <!-- Spoken description for Unicode code point U+1F6AE: "🚮" PUT LITTER IN ITS PLACE SYMBOL --> + <string name="spoken_emoji_1F6AE">Put litter in its place symbol</string> + <!-- Spoken description for Unicode code point U+1F6AF: "🚯" DO NOT LITTER SYMBOL --> + <string name="spoken_emoji_1F6AF">Do not litter symbol</string> + <!-- Spoken description for Unicode code point U+1F6B0: "🚰" POTABLE WATER SYMBOL --> + <string name="spoken_emoji_1F6B0">Potable water symbol</string> + <!-- Spoken description for Unicode code point U+1F6B1: "🚱" NON-POTABLE WATER SYMBOL --> + <string name="spoken_emoji_1F6B1">Non-potable water symbol</string> + <!-- Spoken description for Unicode code point U+1F6B2: "🚲" BICYCLE --> + <string name="spoken_emoji_1F6B2">Bicycle</string> + <!-- Spoken description for Unicode code point U+1F6B3: "🚳" NO BICYCLES --> + <string name="spoken_emoji_1F6B3">No bicycles</string> + <!-- Spoken description for Unicode code point U+1F6B4: "🚴" BICYCLIST --> + <string name="spoken_emoji_1F6B4">Bicyclist</string> + <!-- Spoken description for Unicode code point U+1F6B5: "🚵" MOUNTAIN BICYCLIST --> + <string name="spoken_emoji_1F6B5">Mountain bicyclist</string> + <!-- Spoken description for Unicode code point U+1F6B6: "🚶" PEDESTRIAN --> + <string name="spoken_emoji_1F6B6">Pedestrian</string> + <!-- Spoken description for Unicode code point U+1F6B7: "🚷" NO PEDESTRIANS --> + <string name="spoken_emoji_1F6B7">No pedestrians</string> + <!-- Spoken description for Unicode code point U+1F6B8: "🚸" CHILDREN CROSSING --> + <string name="spoken_emoji_1F6B8">Children crossing</string> + <!-- Spoken description for Unicode code point U+1F6B9: "🚹" MENS SYMBOL --> + <string name="spoken_emoji_1F6B9">Mens symbol</string> + <!-- Spoken description for Unicode code point U+1F6BA: "🚺" WOMENS SYMBOL --> + <string name="spoken_emoji_1F6BA">Womens symbol</string> + <!-- Spoken description for Unicode code point U+1F6BB: "🚻" RESTROOM --> + <string name="spoken_emoji_1F6BB">Restroom</string> + <!-- Spoken description for Unicode code point U+1F6BC: "🚼" BABY SYMBOL --> + <string name="spoken_emoji_1F6BC">Baby symbol</string> + <!-- Spoken description for Unicode code point U+1F6BD: "🚽" TOILET --> + <string name="spoken_emoji_1F6BD">Toilet</string> + <!-- Spoken description for Unicode code point U+1F6BE: "🚾" WATER CLOSET --> + <string name="spoken_emoji_1F6BE">Water closet</string> + <!-- Spoken description for Unicode code point U+1F6BF: "🚿" SHOWER --> + <string name="spoken_emoji_1F6BF">Shower</string> + <!-- Spoken description for Unicode code point U+1F6C0: "🛀" BATH --> + <string name="spoken_emoji_1F6C0">Bath</string> + <!-- Spoken description for Unicode code point U+1F6C1: "🛁" BATHTUB --> + <string name="spoken_emoji_1F6C1">Bathtub</string> + <!-- Spoken description for Unicode code point U+1F6C2: "🛂" PASSPORT CONTROL --> + <string name="spoken_emoji_1F6C2">Passport control</string> + <!-- Spoken description for Unicode code point U+1F6C3: "🛃" CUSTOMS --> + <string name="spoken_emoji_1F6C3">Customs</string> + <!-- Spoken description for Unicode code point U+1F6C4: "🛄" BAGGAGE CLAIM --> + <string name="spoken_emoji_1F6C4">Baggage claim</string> + <!-- Spoken description for Unicode code point U+1F6C5: "🛅" LEFT LUGGAGE --> + <string name="spoken_emoji_1F6C5">Left luggage</string> +</resources> diff --git a/java/res/values/strings-letter-descriptions.xml b/java/res/values/strings-letter-descriptions.xml new file mode 100644 index 000000000..297b6bed2 --- /dev/null +++ b/java/res/values/strings-letter-descriptions.xml @@ -0,0 +1,384 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<!-- + These accented letters (spoken_accented_letter_*) are unsupported by TTS. + These symbols (spoken_symbol_*) are also unsupported by TTS. + TODO: Remove these string resources when TTS/TalkBack support these letters. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Spoken description for Unicode code point U+00AA: "ª" FEMININE ORDINAL INDICATOR --> + <string name="spoken_accented_letter_00AA">Feminine ordinal indicator</string> + <!-- Spoken description for Unicode code point U+00B5: "µ" MICRO SIGN --> + <string name="spoken_accented_letter_00B5">Micro sign</string> + <!-- Spoken description for Unicode code point U+00BA: "º" MASCULINE ORDINAL INDICATOR --> + <string name="spoken_accented_letter_00BA">Masculine ordinal indicator</string> + <!-- Spoken description for Unicode code point U+00DF: "ß" LATIN SMALL LETTER SHARP S --> + <string name="spoken_accented_letter_00DF">Sharp S</string> + <!-- Spoken description for Unicode code point U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE --> + <string name="spoken_accented_letter_00E0">A, grave</string> + <!-- Spoken description for Unicode code point U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE --> + <string name="spoken_accented_letter_00E1">A, acute</string> + <!-- Spoken description for Unicode code point U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_00E2">A, circumflex</string> + <!-- Spoken description for Unicode code point U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE --> + <string name="spoken_accented_letter_00E3">A, tilde</string> + <!-- Spoken description for Unicode code point U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS --> + <string name="spoken_accented_letter_00E4">A, diaeresis</string> + <!-- Spoken description for Unicode code point U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE --> + <string name="spoken_accented_letter_00E5">A, ring above</string> + <!-- Spoken description for Unicode code point U+00E6: "æ" LATIN SMALL LETTER AE --> + <string name="spoken_accented_letter_00E6">A, E, ligature</string> + <!-- Spoken description for Unicode code point U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA --> + <string name="spoken_accented_letter_00E7">C, cedilla</string> + <!-- Spoken description for Unicode code point U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE --> + <string name="spoken_accented_letter_00E8">E, grave</string> + <!-- Spoken description for Unicode code point U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE --> + <string name="spoken_accented_letter_00E9">E, acute</string> + <!-- Spoken description for Unicode code point U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_00EA">E, circumflex</string> + <!-- Spoken description for Unicode code point U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS --> + <string name="spoken_accented_letter_00EB">E, diaeresis</string> + <!-- Spoken description for Unicode code point U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE --> + <string name="spoken_accented_letter_00EC">I, grave</string> + <!-- Spoken description for Unicode code point U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE --> + <string name="spoken_accented_letter_00ED">I, acute</string> + <!-- Spoken description for Unicode code point U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_00EE">I, circumflex</string> + <!-- Spoken description for Unicode code point U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS --> + <string name="spoken_accented_letter_00EF">I, diaeresis</string> + <!-- Spoken description for Unicode code point U+00F0: "ð" LATIN SMALL LETTER ETH --> + <string name="spoken_accented_letter_00F0">Eth</string> + <!-- Spoken description for Unicode code point U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> + <string name="spoken_accented_letter_00F1">N, tilde</string> + <!-- Spoken description for Unicode code point U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE --> + <string name="spoken_accented_letter_00F2">O, grave</string> + <!-- Spoken description for Unicode code point U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE --> + <string name="spoken_accented_letter_00F3">O, acute</string> + <!-- Spoken description for Unicode code point U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_00F4">O, circumflex</string> + <!-- Spoken description for Unicode code point U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE --> + <string name="spoken_accented_letter_00F5">O, tilde</string> + <!-- Spoken description for Unicode code point U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS --> + <string name="spoken_accented_letter_00F6">O, diaeresis</string> + <!-- Spoken description for Unicode code point U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE --> + <string name="spoken_accented_letter_00F8">O, stroke</string> + <!-- Spoken description for Unicode code point U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE --> + <string name="spoken_accented_letter_00F9">U, grave</string> + <!-- Spoken description for Unicode code point U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE --> + <string name="spoken_accented_letter_00FA">U, acute</string> + <!-- Spoken description for Unicode code point U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_00FB">U, circumflex</string> + <!-- Spoken description for Unicode code point U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS --> + <string name="spoken_accented_letter_00FC">U, diaeresis</string> + <!-- Spoken description for Unicode code point U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE --> + <string name="spoken_accented_letter_00FD">Y, acute</string> + <!-- Spoken description for Unicode code point U+00FE: "þ" LATIN SMALL LETTER THORN --> + <string name="spoken_accented_letter_00FE">Thorn</string> + <!-- Spoken description for Unicode code point U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS --> + <string name="spoken_accented_letter_00FF">Y, diaeresis</string> + <!-- Spoken description for Unicode code point U+0101: "ā" LATIN SMALL LETTER A WITH MACRON --> + <string name="spoken_accented_letter_0101">A, macron</string> + <!-- Spoken description for Unicode code point U+0103: "ă" LATIN SMALL LETTER A WITH BREVE --> + <string name="spoken_accented_letter_0103">A, breve</string> + <!-- Spoken description for Unicode code point U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK --> + <string name="spoken_accented_letter_0105">A, ogonek</string> + <!-- Spoken description for Unicode code point U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE --> + <string name="spoken_accented_letter_0107">C, acute</string> + <!-- Spoken description for Unicode code point U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_0109">C, circumflex</string> + <!-- Spoken description for Unicode code point U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE --> + <string name="spoken_accented_letter_010B">C, dot above</string> + <!-- Spoken description for Unicode code point U+010D: "č" LATIN SMALL LETTER C WITH CARON --> + <string name="spoken_accented_letter_010D">C, caron</string> + <!-- Spoken description for Unicode code point U+010F: "ď" LATIN SMALL LETTER D WITH CARON --> + <string name="spoken_accented_letter_010F">D, caron</string> + <!-- Spoken description for Unicode code point U+0111: "đ" LATIN SMALL LETTER D WITH STROKE --> + <string name="spoken_accented_letter_0111">D, stroke</string> + <!-- Spoken description for Unicode code point U+0113: "ē" LATIN SMALL LETTER E WITH MACRON --> + <string name="spoken_accented_letter_0113">E, macron</string> + <!-- Spoken description for Unicode code point U+0115: "ĕ" LATIN SMALL LETTER E WITH BREVE --> + <string name="spoken_accented_letter_0115">E, breve</string> + <!-- Spoken description for Unicode code point U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE --> + <string name="spoken_accented_letter_0117">E, dot above</string> + <!-- Spoken description for Unicode code point U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK --> + <string name="spoken_accented_letter_0119">E, ogonek</string> + <!-- Spoken description for Unicode code point U+011B: "ě" LATIN SMALL LETTER E WITH CARON --> + <string name="spoken_accented_letter_011B">E, caron</string> + <!-- Spoken description for Unicode code point U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_011D">G, circumflex</string> + <!-- Spoken description for Unicode code point U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE --> + <string name="spoken_accented_letter_011F">G, breve</string> + <!-- Spoken description for Unicode code point U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE --> + <string name="spoken_accented_letter_0121">G, dot above</string> + <!-- Spoken description for Unicode code point U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA --> + <string name="spoken_accented_letter_0123">G, cedilla</string> + <!-- Spoken description for Unicode code point U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_0125">H, circumflex</string> + <!-- Spoken description for Unicode code point U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE --> + <string name="spoken_accented_letter_0127">H, stroke</string> + <!-- Spoken description for Unicode code point U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE --> + <string name="spoken_accented_letter_0129">I, tilde</string> + <!-- Spoken description for Unicode code point U+012B: "ī" LATIN SMALL LETTER I WITH MACRON --> + <string name="spoken_accented_letter_012B">I, macron</string> + <!-- Spoken description for Unicode code point U+012D: "ĭ" LATIN SMALL LETTER I WITH BREVE --> + <string name="spoken_accented_letter_012D">I, breve</string> + <!-- Spoken description for Unicode code point U+012F: "į" LATIN SMALL LETTER I WITH OGONEK --> + <string name="spoken_accented_letter_012F">I, ogonek</string> + <!-- Spoken description for Unicode code point U+0131: "ı" LATIN SMALL LETTER DOTLESS I --> + <string name="spoken_accented_letter_0131">Dotless I</string> + <!-- Spoken description for Unicode code point U+0133: "ij" LATIN SMALL LIGATURE IJ --> + <string name="spoken_accented_letter_0133">I, J, ligature</string> + <!-- Spoken description for Unicode code point U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_0135">J, circumflex</string> + <!-- Spoken description for Unicode code point U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA --> + <string name="spoken_accented_letter_0137">K, cedilla</string> + <!-- Spoken description for Unicode code point U+0138: "ĸ" LATIN SMALL LETTER KRA --> + <string name="spoken_accented_letter_0138">Kra</string> + <!-- Spoken description for Unicode code point U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE --> + <string name="spoken_accented_letter_013A">L, acute</string> + <!-- Spoken description for Unicode code point U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA --> + <string name="spoken_accented_letter_013C">L, cedilla</string> + <!-- Spoken description for Unicode code point U+013E: "ľ" LATIN SMALL LETTER L WITH CARON --> + <string name="spoken_accented_letter_013E">L, caron</string> + <!-- Spoken description for Unicode code point U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT --> + <string name="spoken_accented_letter_0140">L, middle dot</string> + <!-- Spoken description for Unicode code point U+0142: "ł" LATIN SMALL LETTER L WITH STROKE --> + <string name="spoken_accented_letter_0142">L, stroke</string> + <!-- Spoken description for Unicode code point U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE --> + <string name="spoken_accented_letter_0144">N, acute</string> + <!-- Spoken description for Unicode code point U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA --> + <string name="spoken_accented_letter_0146">N, cedilla</string> + <!-- Spoken description for Unicode code point U+0148: "ň" LATIN SMALL LETTER N WITH CARON --> + <string name="spoken_accented_letter_0148">N, caron</string> + <!-- Spoken description for Unicode code point U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE --> + <string name="spoken_accented_letter_0149">N, preceded by apostrophe</string> + <!-- Spoken description for Unicode code point U+014B: "ŋ" LATIN SMALL LETTER ENG --> + <string name="spoken_accented_letter_014B">Eng</string> + <!-- Spoken description for Unicode code point U+014D: "ō" LATIN SMALL LETTER O WITH MACRON --> + <string name="spoken_accented_letter_014D">O, macron</string> + <!-- Spoken description for Unicode code point U+014F: "ŏ" LATIN SMALL LETTER O WITH BREVE --> + <string name="spoken_accented_letter_014F">O, breve</string> + <!-- Spoken description for Unicode code point U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE --> + <string name="spoken_accented_letter_0151">O, double acute</string> + <!-- Spoken description for Unicode code point U+0153: "œ" LATIN SMALL LIGATURE OE --> + <string name="spoken_accented_letter_0153">O, E, ligature</string> + <!-- Spoken description for Unicode code point U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE --> + <string name="spoken_accented_letter_0155">R, acute</string> + <!-- Spoken description for Unicode code point U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA --> + <string name="spoken_accented_letter_0157">R, cedilla</string> + <!-- Spoken description for Unicode code point U+0159: "ř" LATIN SMALL LETTER R WITH CARON --> + <string name="spoken_accented_letter_0159">R, caron</string> + <!-- Spoken description for Unicode code point U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE --> + <string name="spoken_accented_letter_015B">S, acute</string> + <!-- Spoken description for Unicode code point U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_015D">S, circumflex</string> + <!-- Spoken description for Unicode code point U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA --> + <string name="spoken_accented_letter_015F">S, cedilla</string> + <!-- Spoken description for Unicode code point U+0161: "š" LATIN SMALL LETTER S WITH CARON --> + <string name="spoken_accented_letter_0161">S, caron</string> + <!-- Spoken description for Unicode code point U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA --> + <string name="spoken_accented_letter_0163">T, cedilla</string> + <!-- Spoken description for Unicode code point U+0165: "ť" LATIN SMALL LETTER T WITH CARON --> + <string name="spoken_accented_letter_0165">T, caron</string> + <!-- Spoken description for Unicode code point U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE --> + <string name="spoken_accented_letter_0167">T, stroke</string> + <!-- Spoken description for Unicode code point U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE --> + <string name="spoken_accented_letter_0169">U, tilde</string> + <!-- Spoken description for Unicode code point U+016B: "ū" LATIN SMALL LETTER U WITH MACRON --> + <string name="spoken_accented_letter_016B">U, macron</string> + <!-- Spoken description for Unicode code point U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE --> + <string name="spoken_accented_letter_016D">U, breve</string> + <!-- Spoken description for Unicode code point U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE --> + <string name="spoken_accented_letter_016F">U, ring above</string> + <!-- Spoken description for Unicode code point U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE --> + <string name="spoken_accented_letter_0171">U, double acute</string> + <!-- Spoken description for Unicode code point U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK --> + <string name="spoken_accented_letter_0173">U, ogonek</string> + <!-- Spoken description for Unicode code point U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_0175">W, circumflex</string> + <!-- Spoken description for Unicode code point U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX --> + <string name="spoken_accented_letter_0177">Y, circumflex</string> + <!-- Spoken description for Unicode code point U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE --> + <string name="spoken_accented_letter_017A">Z, acute</string> + <!-- Spoken description for Unicode code point U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE --> + <string name="spoken_accented_letter_017C">Z, dot above</string> + <!-- Spoken description for Unicode code point U+017E: "ž" LATIN SMALL LETTER Z WITH CARON --> + <string name="spoken_accented_letter_017E">Z, caron</string> + <!-- Spoken description for Unicode code point U+017F: "ſ" LATIN SMALL LETTER LONG S --> + <string name="spoken_accented_letter_017F">Long S</string> + <!-- Spoken description for Unicode code point U+01A1: "ơ" LATIN SMALL LETTER O WITH HORN --> + <string name="spoken_accented_letter_01A1">O, horn</string> + <!-- Spoken description for Unicode code point U+01B0: "ư" LATIN SMALL LETTER U WITH HORN --> + <string name="spoken_accented_letter_01B0">U, horn</string> + <!-- Spoken description for Unicode code point U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW --> + <string name="spoken_accented_letter_0219">S, comma below</string> + <!-- Spoken description for Unicode code point U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW --> + <string name="spoken_accented_letter_021B">T, comma below</string> + <!-- Spoken description for Unicode code point U+0259: "ə" LATIN SMALL LETTER SCHWA --> + <string name="spoken_accented_letter_0259">Schwa</string> + <!-- Spoken description for Unicode code point U+1EA1: "ạ" LATIN SMALL LETTER A WITH DOT BELOW --> + <string name="spoken_accented_letter_1EA1">A, dot below</string> + <!-- Spoken description for Unicode code point U+1EA3: "ả" LATIN SMALL LETTER A WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1EA3">A, hook above</string> + <!-- Spoken description for Unicode code point U+1EA5: "ấ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND ACUTE --> + <string name="spoken_accented_letter_1EA5">A, circumflex and acute</string> + <!-- Spoken description for Unicode code point U+1EA7: "ầ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND GRAVE --> + <string name="spoken_accented_letter_1EA7">A, circumflex and grave</string> + <!-- Spoken description for Unicode code point U+1EA9: "ẩ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE --> + <string name="spoken_accented_letter_1EA9">A, circumflex and hook above</string> + <!-- Spoken description for Unicode code point U+1EAB: "ẫ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND TILDE --> + <string name="spoken_accented_letter_1EAB">A, circumflex and tilde</string> + <!-- Spoken description for Unicode code point U+1EAD: "ậ" LATIN SMALL LETTER A WITH CIRCUMFLEX AND DOT BELOW --> + <string name="spoken_accented_letter_1EAD">A, circumflex and dot below</string> + <!-- Spoken description for Unicode code point U+1EAF: "ắ" LATIN SMALL LETTER A WITH BREVE AND ACUTE --> + <string name="spoken_accented_letter_1EAF">A, breve and acute</string> + <!-- Spoken description for Unicode code point U+1EB1: "ằ" LATIN SMALL LETTER A WITH BREVE AND GRAVE --> + <string name="spoken_accented_letter_1EB1">A, breve and grave</string> + <!-- Spoken description for Unicode code point U+1EB3: "ẳ" LATIN SMALL LETTER A WITH BREVE AND HOOK ABOVE --> + <string name="spoken_accented_letter_1EB3">A, breve and hook above</string> + <!-- Spoken description for Unicode code point U+1EB5: "ẵ" LATIN SMALL LETTER A WITH BREVE AND TILDE --> + <string name="spoken_accented_letter_1EB5">A, breve and tilde</string> + <!-- Spoken description for Unicode code point U+1EB7: "ặ" LATIN SMALL LETTER A WITH BREVE AND DOT BELOW --> + <string name="spoken_accented_letter_1EB7">A, breve and dot below</string> + <!-- Spoken description for Unicode code point U+1EB9: "ẹ" LATIN SMALL LETTER E WITH DOT BELOW --> + <string name="spoken_accented_letter_1EB9">E, dot below</string> + <!-- Spoken description for Unicode code point U+1EBB: "ẻ" LATIN SMALL LETTER E WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1EBB">E, hook above</string> + <!-- Spoken description for Unicode code point U+1EBD: "ẽ" LATIN SMALL LETTER E WITH TILDE --> + <string name="spoken_accented_letter_1EBD">E, tilde</string> + <!-- Spoken description for Unicode code point U+1EBF: "ế" LATIN SMALL LETTER E WITH CIRCUMFLEX AND ACUTE --> + <string name="spoken_accented_letter_1EBF">E, circumflex and acute</string> + <!-- Spoken description for Unicode code point U+1EC1: "ề" LATIN SMALL LETTER E WITH CIRCUMFLEX AND GRAVE --> + <string name="spoken_accented_letter_1EC1">E, circumflex and grave</string> + <!-- Spoken description for Unicode code point U+1EC3: "ể" LATIN SMALL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE --> + <string name="spoken_accented_letter_1EC3">E, circumflex and hook above</string> + <!-- Spoken description for Unicode code point U+1EC5: "ễ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND TILDE --> + <string name="spoken_accented_letter_1EC5">E, circumflex and tilde</string> + <!-- Spoken description for Unicode code point U+1EC7: "ệ" LATIN SMALL LETTER E WITH CIRCUMFLEX AND DOT BELOW --> + <string name="spoken_accented_letter_1EC7">E, circumflex and dot below</string> + <!-- Spoken description for Unicode code point U+1EC9: "ỉ" LATIN SMALL LETTER I WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1EC9">I, hook above</string> + <!-- Spoken description for Unicode code point U+1ECB: "ị" LATIN SMALL LETTER I WITH DOT BELOW --> + <string name="spoken_accented_letter_1ECB">I, dot below</string> + <!-- Spoken description for Unicode code point U+1ECD: "ọ" LATIN SMALL LETTER O WITH DOT BELOW --> + <string name="spoken_accented_letter_1ECD">O, dot below</string> + <!-- Spoken description for Unicode code point U+1ECF: "ỏ" LATIN SMALL LETTER O WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1ECF">O, hook above</string> + <!-- Spoken description for Unicode code point U+1ED1: "ố" LATIN SMALL LETTER O WITH CIRCUMFLEX AND ACUTE --> + <string name="spoken_accented_letter_1ED1">O, circumflex and acute</string> + <!-- Spoken description for Unicode code point U+1ED3: "ồ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND GRAVE --> + <string name="spoken_accented_letter_1ED3">O, circumflex and grave</string> + <!-- Spoken description for Unicode code point U+1ED5: "ổ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE --> + <string name="spoken_accented_letter_1ED5">O, circumflex and hook above</string> + <!-- Spoken description for Unicode code point U+1ED7: "ỗ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND TILDE --> + <string name="spoken_accented_letter_1ED7">O, circumflex and tilde</string> + <!-- Spoken description for Unicode code point U+1ED9: "ộ" LATIN SMALL LETTER O WITH CIRCUMFLEX AND DOT BELOW --> + <string name="spoken_accented_letter_1ED9">O, circumflex and dot below</string> + <!-- Spoken description for Unicode code point U+1EDB: "ớ" LATIN SMALL LETTER O WITH HORN AND ACUTE --> + <string name="spoken_accented_letter_1EDB">O, horn and acute</string> + <!-- Spoken description for Unicode code point U+1EDD: "ờ" LATIN SMALL LETTER O WITH HORN AND GRAVE --> + <string name="spoken_accented_letter_1EDD">O, horn and grave</string> + <!-- Spoken description for Unicode code point U+1EDF: "ở" LATIN SMALL LETTER O WITH HORN AND HOOK ABOVE --> + <string name="spoken_accented_letter_1EDF">O, horn and hook above</string> + <!-- Spoken description for Unicode code point U+1EE1: "ỡ" LATIN SMALL LETTER O WITH HORN AND TILDE --> + <string name="spoken_accented_letter_1EE1">O, horn and tilde</string> + <!-- Spoken description for Unicode code point U+1EE3: "ợ" LATIN SMALL LETTER O WITH HORN AND DOT BELOW --> + <string name="spoken_accented_letter_1EE3">O, horn and dot below</string> + <!-- Spoken description for Unicode code point U+1EE5: "ụ" LATIN SMALL LETTER U WITH DOT BELOW --> + <string name="spoken_accented_letter_1EE5">U, dot below</string> + <!-- Spoken description for Unicode code point U+1EE7: "ủ" LATIN SMALL LETTER U WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1EE7">U, hook above</string> + <!-- Spoken description for Unicode code point U+1EE9: "ứ" LATIN SMALL LETTER U WITH HORN AND ACUTE --> + <string name="spoken_accented_letter_1EE9">U, horn and acute</string> + <!-- Spoken description for Unicode code point U+1EEB: "ừ" LATIN SMALL LETTER U WITH HORN AND GRAVE --> + <string name="spoken_accented_letter_1EEB">U, horn and grave</string> + <!-- Spoken description for Unicode code point U+1EED: "ử" LATIN SMALL LETTER U WITH HORN AND HOOK ABOVE --> + <string name="spoken_accented_letter_1EED">U, horn and hook above</string> + <!-- Spoken description for Unicode code point U+1EEF: "ữ" LATIN SMALL LETTER U WITH HORN AND TILDE --> + <string name="spoken_accented_letter_1EEF">U, horn and tilde</string> + <!-- Spoken description for Unicode code point U+1EF1: "ự" LATIN SMALL LETTER U WITH HORN AND DOT BELOW --> + <string name="spoken_accented_letter_1EF1">U, horn and dot below</string> + <!-- Spoken description for Unicode code point U+1EF3: "ỳ" LATIN SMALL LETTER Y WITH GRAVE --> + <string name="spoken_accented_letter_1EF3">Y, grave</string> + <!-- Spoken description for Unicode code point U+1EF5: "ỵ" LATIN SMALL LETTER Y WITH DOT BELOW --> + <string name="spoken_accented_letter_1EF5">Y, dot below</string> + <!-- Spoken description for Unicode code point U+1EF7: "ỷ" LATIN SMALL LETTER Y WITH HOOK ABOVE --> + <string name="spoken_accented_letter_1EF7">Y, hook above</string> + <!-- Spoken description for Unicode code point U+1EF9: "ỹ" LATIN SMALL LETTER Y WITH TILDE --> + <string name="spoken_accented_letter_1EF9">Y, tilde</string> + <!-- Spoken description for Unicode code point U+00A1: "¡" INVERTED EXCLAMATION MARK --> + <string name="spoken_symbol_00A1">Inverted exclamation mark</string> + <!-- Spoken description for Unicode code point U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <string name="spoken_symbol_00AB">Left-pointing double angle quotation mark</string> + <!-- Spoken description for Unicode code point U+00B7: "·" MIDDLE DOT --> + <string name="spoken_symbol_00B7">Middle dot</string> + <!-- Spoken description for Unicode code point U+00B9: "¹" SUPERSCRIPT ONE --> + <string name="spoken_symbol_00B9">Superscript one</string> + <!-- Spoken description for Unicode code point U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <string name="spoken_symbol_00BB">Right-pointing double angle quotation mark</string> + <!-- Spoken description for Unicode code point U+00BF: "¿" INVERTED QUESTION MARK --> + <string name="spoken_symbol_00BF">Inverted question mark</string> + <!-- Spoken description for Unicode code point U+2018: "‘" LEFT SINGLE QUOTATION MARK --> + <string name="spoken_symbol_2018">Left single quotation mark</string> + <!-- Spoken description for Unicode code point U+2019: "’" RIGHT SINGLE QUOTATION MARK --> + <string name="spoken_symbol_2019">Right single quotation mark</string> + <!-- Spoken description for Unicode code point U+201A: "‚" SINGLE LOW-9 QUOTATION MARK --> + <string name="spoken_symbol_201A">Single low-9 quotation mark</string> + <!-- Spoken description for Unicode code point U+201C: "“" LEFT DOUBLE QUOTATION MARK --> + <string name="spoken_symbol_201C">Left double quotation mark</string> + <!-- Spoken description for Unicode code point U+201D: "”" RIGHT DOUBLE QUOTATION MARK --> + <string name="spoken_symbol_201D">Right double quotation mark</string> + <!-- Spoken description for Unicode code point U+2020: "†" DAGGER --> + <string name="spoken_symbol_2020">Dagger</string> + <!-- Spoken description for Unicode code point U+2021: "‡" DOUBLE DAGGER --> + <string name="spoken_symbol_2021">Double dagger</string> + <!-- Spoken description for Unicode code point U+2030: "‰" PER MILLE SIGN --> + <string name="spoken_symbol_2030">Per mille sign</string> + <!-- Spoken description for Unicode code point U+2032: "′" PRIME --> + <string name="spoken_symbol_2032">Prime</string> + <!-- Spoken description for Unicode code point U+2033: "″" DOUBLE PRIME --> + <string name="spoken_symbol_2033">Double prime</string> + <!-- Spoken description for Unicode code point U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK --> + <string name="spoken_symbol_2039">Single left-pointing angle quotation mark</string> + <!-- Spoken description for Unicode code point U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK --> + <string name="spoken_symbol_203A">Single right-pointing angle quotation mark</string> + <!-- Spoken description for Unicode code point U+2074: "⁴" SUPERSCRIPT FOUR --> + <string name="spoken_symbol_2074">Superscript four</string> + <!-- Spoken description for Unicode code point U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N --> + <string name="spoken_symbol_207F">Superscript latin small letter n</string> + <!-- Spoken description for Unicode code point U+20B1: "₱" PESO SIGN --> + <string name="spoken_symbol_20B1">Peso sign</string> + <!-- Spoken description for Unicode code point U+2105: "℅" CARE OF --> + <string name="spoken_symbol_2105">Care of</string> + <!-- Spoken description for Unicode code point U+2192: "→" RIGHTWARDS ARROW --> + <string name="spoken_symbol_2192">Rightwards arrow</string> + <!-- Spoken description for Unicode code point U+2193: "↓" DOWNWARDS ARROW --> + <string name="spoken_symbol_2193">Downwards arrow</string> + <!-- Spoken description for Unicode code point U+2205: "∅" EMPTY SET --> + <string name="spoken_symbol_2205">Empty set</string> + <!-- Spoken description for Unicode code point U+2206: "∆" INCREMENT --> + <string name="spoken_symbol_2206">Increment</string> + <!-- Spoken description for Unicode code point U+2264: "≤" LESS-THAN OR EQUAL TO --> + <string name="spoken_symbol_2264">Less-than or equal to</string> + <!-- Spoken description for Unicode code point U+2265: "≥" GREATER-THAN OR EQUAL TO --> + <string name="spoken_symbol_2265">Greater-than or equal to</string> + <!-- Spoken description for Unicode code point U+2605: "★" BLACK STAR --> + <string name="spoken_symbol_2605">Black star</string> +</resources> diff --git a/java/res/values/urls.xml b/java/res/values/strings-production.xml index a8e9ad7d3..8064b9949 100644 --- a/java/res/values/urls.xml +++ b/java/res/values/strings-production.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2012, The Android Open Source Project +** 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. @@ -17,6 +17,8 @@ ** limitations under the License. */ --> + <resources> - <string name="research_logger_upload_url" translatable="false"></string> + <!-- Description for option to enable sending usage statistics --> + <string name="enable_metrics_logging_summary" translatable="false"></string> </resources> diff --git a/java/res/values/strings-talkback-descriptions.xml b/java/res/values/strings-talkback-descriptions.xml index 9c1e652b0..80c7bdbf0 100644 --- a/java/res/values/strings-talkback-descriptions.xml +++ b/java/res/values/strings-talkback-descriptions.xml @@ -32,13 +32,17 @@ <string name="spoken_auto_correct_obscured"><xliff:g id="KEY_NAME" example="Space">%1$s</xliff:g> performs auto-correction</string> <!-- Spoken description for unknown keyboard keys. --> - <string name="spoken_description_unknown">Key code %d</string> + <string name="spoken_description_unknown">Unknown character</string> <!-- Spoken description for the "Shift" keyboard key when "Shift" is off. --> <string name="spoken_description_shift">Shift</string> + <!-- Spoken description for the "Shift" keyboard key in symbols mode. --> + <string name="spoken_description_symbols_shift">More symbols</string> <!-- Spoken description for the "Shift" keyboard key when "Shift" is on. --> - <string name="spoken_description_shift_shifted">Shift on (tap to disable)</string> + <string name="spoken_description_shift_shifted">Shift</string> + <!-- Spoken description for the "Shift" keyboard key in 2nd symbols (a.k.a. symbols shift) mode. --> + <string name="spoken_description_symbols_shift_shifted">Symbols</string> <!-- Spoken description for the "Shift" keyboard key when "Caps lock" is on. --> - <string name="spoken_description_caps_lock">Caps lock on (tap to disable)</string> + <string name="spoken_description_caps_lock">Shift</string> <!-- Spoken description for the "Delete" keyboard key. --> <string name="spoken_description_delete">Delete</string> <!-- Spoken description for the "To Symbol" keyboard key. --> @@ -74,11 +78,10 @@ <string name="spoken_description_shiftmode_on">Shift enabled</string> <!-- Spoken feedback after turning "Caps lock" mode on. --> <string name="spoken_description_shiftmode_locked">Caps lock enabled</string> - <!-- Spoken feedback after turning "Shift" mode off. --> - <string name="spoken_description_shiftmode_off">Shift disabled</string> - <!-- Spoken feedback after changing to the symbols keyboard. --> <string name="spoken_description_mode_symbol">Symbols mode</string> + <!-- Spoken feedback after changing to the 2nd symbols (a.k.a. symbols shift) keyboard. --> + <string name="spoken_description_mode_symbol_shift">More symbols mode</string> <!-- Spoken feedback after changing to the alphanumeric keyboard. --> <string name="spoken_description_mode_alpha">Letters mode</string> <!-- Spoken feedback after changing to the phone dialer keyboard. --> @@ -123,4 +126,27 @@ <string name="spoken_descrption_emoji_category_symbols">Symbols</string> <!-- Description of the emoji category icon of Emoticons. --> <string name="spoken_descrption_emoji_category_emoticons">Emoticons</string> + + <!-- Description of an upper case letter of LOWER_LETTER. --> + <string name="spoken_description_upper_case">Capital <xliff:g id="LOWER_LETTER" example="A, E, ligature">%s</xliff:g></string> + <!-- Spoken description for Unicode code point U+0049: "I" LATIN CAPITAL LETTER I + Note that depending on locale, the lower-case of this letter is U+0069 or U+0131. --> + <string name="spoken_letter_0049">Capital I</string> + <!-- Spoken description for Unicode code point U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE + Note that depending on locale, the lower-case of this letter is U+0069 or U+0131. --> + <string name="spoken_letter_0130">Capital I, dot above</string> + <!-- Spoken description for unknown symbol code point. --> + <string name="spoken_symbol_unknown">Unknown symbol</string> + <!-- Spoken description for unknown emoji code point. --> + <string name="spoken_emoji_unknown">Unknown emoji</string> + + <!-- Spoken descriptions when opening a more keys keyboard that has alternative characters. --> + <string name="spoken_open_more_keys_keyboard">Alternative characters are available</string> + <!-- Spoken descriptions when closing a more keys keyboard that has alternative characters. --> + <string name="spoken_close_more_keys_keyboard">Alternative characters are dismissed</string> + + <!-- Spoken descriptions when opening a more suggestions panel that has alternative suggested words. --> + <string name="spoken_open_more_suggestions">Alternative suggestions are available</string> + <!-- Spoken descriptions when closing a more suggestions panel that has alternative suggested words. --> + <string name="spoken_close_more_suggestions">Alternative suggestions are dismissed</string> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 9f93055e2..884911565 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -21,9 +21,6 @@ <!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] --> <string name="english_ime_input_options">Input options</string> - <!-- Title for Latin keyboard research log dialog, which contains special commands for users that contribute data for research. [CHAR LIMIT=33] --> - <string name="english_ime_research_log">Research Log Commands</string> - <!-- Title for the spell checker option to turn on/off contact names lookup [CHAR LIMIT=25] --> <string name="use_contacts_for_spellchecking_option_title">Look up contact names</string> @@ -90,6 +87,9 @@ <!-- Option name for enabling the use by the keyboards of sent/received messages, e-mail and typing history to improve suggestion accuracy [CHAR LIMIT=25] --> <string name="use_personalized_dicts">Personalized suggestions</string> + <!-- Option to enable sending usage statistics --> + <string name="enable_metrics_logging">"Improve <xliff:g id="APPLICATION_NAME" example="Android Keyboard">%s</xliff:g>"</string> + <!-- Option name for enabling or disabling the double-space period feature that lets double tap on spacebar insert a period followed by a space [CHAR LIMIT=30] --> <string name="use_double_space_period">Double-space period</string> <!-- Description for option enabling or disabling the double-space period feature that lets double tap on spacebar insert a period followed by a space [CHAR LIMIT=65] --> @@ -166,68 +166,8 @@ <!-- Title for input language selection screen --> <string name="language_selection_title">Input languages</string> - <!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_do_not_log_this_session" translatable="false">Suspend logging</string> - <!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_enable_session_logging" translatable="false">Enable logging</string> - <!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_notify_session_log_deleting" translatable="false">Deleting session log</string> - <!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_notify_logging_suspended" translatable="false">Logging temporarily suspended. To disable permanently, go to Android Keyboard Settings</string> - <!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_notify_session_log_not_deleted" translatable="false">Session log NOT deleted</string> - <!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_notify_session_logging_enabled" translatable="false">Session logging enabled</string> - - <!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_include_history_label" translatable="false">Include session history</string> - <!-- Text for checkbox option to include user account name in feedback for research purposes [CHAR LIMIT=50] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_include_account_name_label" translatable="false">Include account name</string> - <!-- Text for checkbox option to include a recording in feedback for research purposes [CHAR LIMIT=50] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_include_recording_label" translatable="false">Include recorded demonstration</string> - <!-- Dialog button choice to send research feedback [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_send" translatable="false">Send</string> - <!-- Dialog button choice to cancel sending research feedback [CHAR LIMIT=35] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_cancel" translatable="false">Cancel</string> - <!-- Temporary notification to provide user with instructions about stopping a recording - - operation[CHAR LIMIT=100] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_demonstration_instructions" translatable="false">Please demonstrate the issue you are writing about.\n\nWhen finished, select the \"Bug?\" button again."</string> <!-- Title of a preference to send feedback. [CHAR LIMIT=30]--> <string name="send_feedback">Send feedback</string> - <!-- Temporary notification of recording failure [CHAR LIMIT=100] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_feedback_recording_failure" translatable="false">Recording cancelled due to timeout</string> - <!-- Toast notification to ask user to quit the research feedback dialog to perform this operation [CHAR LIMIT=100] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_please_exit_feedback_form" translatable="false">Please exit the feedback dialog to access the research log menu</string> - - <!-- Title of dialog shown at start informing users about contributing research usage data--> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_splash_title" translatable="false">Warning</string> - - <!-- Toast message informing users that logging has been disabled --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_logging_disabled" translatable="false">Logging Disabled</string> - - <!-- Name for the research uploading service to be displayed to users. [CHAR LIMIT=50] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_log_uploader_name" translatable="false">Research Uploader Service</string> - - <!-- Name for the research replaying service to be displayed to users. [CHAR LIMIT=50] --> - <!-- TODO: remove translatable=false attribute once text is stable --> - <string name="research_log_replayer_name" translatable="false">Research Replayer Service</string> <!-- Preference for input language selection --> <string name="select_language">Input languages</string> @@ -238,11 +178,6 @@ <!-- Inform the user that a particular language has an available dictionary --> <string name="has_dictionary">Dictionary available</string> - <!-- Preferences item for enabling to send user statistics for development only diagnostics --> - <string name="prefs_enable_log">Enable user feedback</string> - <!-- Description for enabling to send user statistics for development only diagnostics --> - <string name="prefs_description_log">Help improve this input method editor by automatically sending usage statistics and crash reports</string> - <!-- Title of the item to change the keyboard theme [CHAR LIMIT=20]--> <string name="keyboard_layout">Keyboard theme</string> @@ -390,8 +325,6 @@ mobile devices. [CHAR LIMIT=25] --> <!-- Toast text to describe the same input style already exists [CHAR LIMIT=64]--> <string name="custom_input_style_already_exists">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME" example="English (Dvorak)">%s</xliff:g>"</string> - <!-- Title of an option for usability study mode --> - <string name="prefs_usability_study_mode">Usability study mode</string> <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> <!-- Title of the settings for keypress vibration duration [CHAR LIMIT=35] --> @@ -416,14 +349,8 @@ mobile devices. [CHAR LIMIT=25] --> <string name="read_external_dictionary_confirm_install_message">Really install this file for <xliff:g id="LANGUAGE_NAME" example="English">%s</xliff:g>?</string> <!-- Title for an error dialog that contains the details of the error in the body [CHAR LIMIT=80] --> <string name="error">There was an error</string> - <!-- Title of the settings for dumpping contacts dictionary file [CHAR LIMIT=35] --> - <string name="prefs_dump_contacts_dict">Dump contacts dictionary</string> - <!-- Title of the settings for dumpping personal dictionary file [CHAR LIMIT=35] --> - <string name="prefs_dump_user_dict">Dump personal dictionary</string> - <!-- Title of the settings for dumpping user history dictionary file [CHAR LIMIT=35] --> - <string name="prefs_dump_user_history_dict">Dump user history dictionary</string> - <!-- Title of the settings for dumpping personalization dictionary file [CHAR LIMIT=35] --> - <string name="prefs_dump_personalization_dict">Dump personalization dictionary</string> + <!-- Title of the settings group for dumpping dictionary files that have been created on the device [CHAR LIMIT=35] --> + <string name="prefs_dump_dynamic_dicts" translatable="false">Dump dictionary</string> <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] --> <string name="button_default">Default</string> diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml index eb6cdd975..02a93ca82 100644 --- a/java/res/values/themes-common.xml +++ b/java/res/values/themes-common.xml @@ -109,23 +109,13 @@ <style name="KeyPreviewTextView" /> <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, for instance delete button, need themed {@link KeyboardView} attributes. --> - <style - name="EmojiPalettesView" - parent="KeyboardView" - > - <item name="emojiTabLabelColor">@color/emoji_tab_label_color_holo</item> - </style> + <style name="EmojiPalettesView" /> <style name="MoreKeysKeyboard" /> <style name="MoreKeysKeyboardView" parent="MainKeyboardView" /> <style name="MoreKeysKeyboardContainer" /> - <style name="SuggestionStripView"> - <item name="suggestionsCountInStrip">@integer/config_suggestions_count_in_strip</item> - <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> - <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> - <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> - </style> + <style name="SuggestionStripView" /> <style name="SuggestionWord"> <item name="android:minWidth">@dimen/config_suggestion_min_width</item> <item name="android:textSize">@dimen/config_suggestion_text_size</item> @@ -134,9 +124,10 @@ <item name="android:paddingTop">0dp</item> <item name="android:paddingRight">@dimen/config_suggestion_text_horizontal_padding</item> <item name="android:paddingBottom">0dp</item> - <!-- Provide a haptic feedback by ourselves based on the keyboard settings. - We just need to ignore the system's haptic feedback settings. --> + <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. + We just need to ignore the system's audio and haptic feedback settings. --> <item name="android:hapticFeedbackEnabled">false</item> + <item name="android:soundEffectsEnabled">false</item> <item name="android:focusable">false</item> <item name="android:clickable">false</item> <item name="android:singleLine">true</item> diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 720eda9ce..319b4aeed 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -48,16 +48,19 @@ > <item name="android:background">@drawable/keyboard_background_holo</item> <item name="keyBackground">@drawable/btn_keyboard_key_ics</item> + <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_ics</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_ics</item> <item name="keyTypeface">bold</item> <item name="keyTextColor">@color/key_text_color_holo</item> <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item> + <item name="functionalTextColor">@color/key_text_color_holo</item> <item name="keyHintLetterColor">@color/key_hint_letter_color_holo</item> <item name="keyHintLabelColor">@color/key_hint_label_color_holo</item> <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_holo</item> <item name="keyPreviewTextColor">@color/key_text_color_holo</item> - <item name="keyTextShadowColor">@color/key_text_shadow_color_holo</item> - <item name="keyTextShadowRadius">0.0</item> + <!-- A negative value to disable key text shadow layer. --> + <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.ICS" @@ -68,11 +71,9 @@ <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_ics</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item> - <item name="autoCorrectionSpacebarLedEnabled">false</item> - <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item> + <item name="languageOnSpacebarTextShadowRadius">1.0</item> <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> - <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_ics</item> </style> <style name="KeyPreviewTextView.ICS" @@ -84,10 +85,20 @@ for instance delete button, need themed {@link KeyboardView} attributes. --> <style name="EmojiPalettesView.ICS" - parent="KeyboardView.ICS" + parent="MainKeyboardView.ICS" > - <item name="keyBackgroundEmojiFunctional">@drawable/btn_keyboard_key_functional_ics</item> - <item name="emojiTabLabelColor">@color/emoji_tab_label_color_holo</item> + <item name="categoryIndicatorEnabled">true</item> + <item name="categoryIndicatorDrawable">@drawable/emoji_category_tab_selected_ics</item> + <item name="categoryIndicatorBackground">@drawable/emoji_category_tab_unselected_holo_dark</item> + <item name="categoryPageIndicatorColor">@color/highlight_color_ics</item> + <item name="categoryPageIndicatorBackground">@color/emoji_tab_page_indicator_background_holo</item> + <item name="iconEmojiRecentsTab">@drawable/ic_emoji_recents_holo_dark</item> + <item name="iconEmojiCategory1Tab">@drawable/ic_emoji_people_holo_dark</item> + <item name="iconEmojiCategory2Tab">@drawable/ic_emoji_objects_holo_dark</item> + <item name="iconEmojiCategory3Tab">@drawable/ic_emoji_nature_holo_dark</item> + <item name="iconEmojiCategory4Tab">@drawable/ic_emoji_places_holo_dark</item> + <item name="iconEmojiCategory5Tab">@drawable/ic_emoji_symbols_holo_dark</item> + <item name="iconEmojiCategory6Tab">@drawable/ic_emoji_emoticons_holo_dark</item> </style> <style name="MoreKeysKeyboard.ICS" @@ -109,8 +120,12 @@ </style> <style name="SuggestionStripView.ICS" - parent="SuggestionStripView" + parent="KeyboardView.ICS" > + <item name="suggestionsCountInStrip">@integer/config_suggestions_count_in_strip</item> + <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> + <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> + <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> <item name="android:background">@drawable/keyboard_suggest_strip_holo</item> <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item> <item name="colorValidTypedWord">@color/typed_word_color_ics</item> diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml index 830527171..208723dd3 100644 --- a/java/res/values/themes-klp.xml +++ b/java/res/values/themes-klp.xml @@ -48,16 +48,19 @@ > <item name="android:background">@drawable/keyboard_background_holo</item> <item name="keyBackground">@drawable/btn_keyboard_key_klp</item> + <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_klp</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_klp</item> <item name="keyTypeface">bold</item> <item name="keyTextColor">@color/key_text_color_holo</item> <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item> + <item name="functionalTextColor">@color/key_text_color_holo</item> <item name="keyHintLetterColor">@color/key_hint_letter_color_holo</item> <item name="keyHintLabelColor">@color/key_hint_label_color_holo</item> <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_holo</item> <item name="keyPreviewTextColor">@color/key_text_color_holo</item> - <item name="keyTextShadowColor">@color/key_text_shadow_color_holo</item> - <item name="keyTextShadowRadius">0.0</item> + <!-- A negative value to disable key text shadow layer. --> + <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.KLP" @@ -68,11 +71,9 @@ <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_klp</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_klp</item> - <item name="autoCorrectionSpacebarLedEnabled">false</item> - <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item> + <item name="languageOnSpacebarTextShadowRadius">1.0</item> <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> - <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_klp</item> </style> <style name="KeyPreviewTextView.KLP" @@ -84,10 +85,20 @@ for instance delete button, need themed {@link KeyboardView} attributes. --> <style name="EmojiPalettesView.KLP" - parent="KeyboardView.KLP" + parent="MainKeyboardView.KLP" > - <item name="keyBackgroundEmojiFunctional">@drawable/btn_keyboard_key_functional_klp</item> - <item name="emojiTabLabelColor">@color/emoji_tab_label_color_holo</item> + <item name="categoryIndicatorEnabled">true</item> + <item name="categoryIndicatorDrawable">@drawable/emoji_category_tab_selected_klp</item> + <item name="categoryIndicatorBackground">@drawable/emoji_category_tab_unselected_holo_dark</item> + <item name="categoryPageIndicatorColor">@color/highlight_color_klp</item> + <item name="categoryPageIndicatorBackground">@color/emoji_tab_page_indicator_background_holo</item> + <item name="iconEmojiRecentsTab">@drawable/ic_emoji_recents_holo_dark</item> + <item name="iconEmojiCategory1Tab">@drawable/ic_emoji_people_holo_dark</item> + <item name="iconEmojiCategory2Tab">@drawable/ic_emoji_objects_holo_dark</item> + <item name="iconEmojiCategory3Tab">@drawable/ic_emoji_nature_holo_dark</item> + <item name="iconEmojiCategory4Tab">@drawable/ic_emoji_places_holo_dark</item> + <item name="iconEmojiCategory5Tab">@drawable/ic_emoji_symbols_holo_dark</item> + <item name="iconEmojiCategory6Tab">@drawable/ic_emoji_emoticons_holo_dark</item> </style> <style name="MoreKeysKeyboard.KLP" @@ -109,8 +120,12 @@ </style> <style name="SuggestionStripView.KLP" - parent="SuggestionStripView" + parent="KeyboardView.KLP" > + <item name="suggestionsCountInStrip">@integer/config_suggestions_count_in_strip</item> + <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> + <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> + <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> <item name="android:background">@drawable/keyboard_suggest_strip_holo</item> <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item> <item name="colorValidTypedWord">@color/typed_word_color_klp</item> diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml new file mode 100644 index 000000000..e9a295c67 --- /dev/null +++ b/java/res/values/themes-lxx-dark.xml @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="KeyboardTheme.LXX_Dark" parent="KeyboardIcons.LXX_Dark"> + <item name="keyboardStyle">@style/Keyboard.LXX_Dark</item> + <item name="keyboardViewStyle">@style/KeyboardView.LXX_Dark</item> + <item name="mainKeyboardViewStyle">@style/MainKeyboardView.LXX_Dark</item> + <item name="keyPreviewTextViewStyle">@style/KeyPreviewTextView.LXX_Dark</item> + <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.LXX_Dark</item> + <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.LXX_Dark</item> + <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.LXX_Dark</item> + <item name="suggestionStripViewStyle">@style/SuggestionStripView.LXX_Dark</item> + <item name="suggestionWordStyle">@style/SuggestionWord.LXX_Dark</item> + </style> + <style + name="Keyboard.LXX_Dark" + parent="Keyboard" + > + <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> + <item name="themeId">3</item> + <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> + <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> + <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> + <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + </style> + <style + name="KeyboardView.LXX_Dark" + parent="KeyboardView" + > + <item name="android:background">@color/keyboard_background_lxx_dark</item> + <item name="keyBackground">@drawable/btn_keyboard_key_lxx_dark</item> + <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_lxx_dark</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_lxx_dark</item> + <item name="spacebarIconWidthRatio">0.9</item> + <item name="keyTypeface">normal</item> + <item name="keyTextColor">@color/key_text_color_lxx_dark</item> + <item name="keyTextInactivatedColor">@color/key_text_inactive_color_lxx_dark</item> + <item name="functionalTextColor">@color/key_hint_letter_color_lxx_dark</item> + <item name="keyHintLetterColor">@color/key_hint_letter_color_lxx_dark</item> + <item name="keyHintLabelColor">@color/key_text_inactive_color_lxx_dark</item> + <item name="keyShiftedLetterHintInactivatedColor">@color/key_text_inactive_color_lxx_dark</item> + <item name="keyShiftedLetterHintActivatedColor">@color/key_text_color_lxx_dark</item> + <item name="keyPreviewTextColor">@color/key_text_color_lxx_dark</item> + <!-- A negative value to disable key text shadow layer. --> + <item name="keyTextShadowRadius">-1.0</item> + </style> + <style + name="MainKeyboardView.LXX_Dark" + parent="KeyboardView.LXX_Dark" + > + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> + <item name="gestureFloatingPreviewTextColor">@color/highlight_color_lxx_dark</item> + <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_lxx_dark</item> + <item name="gestureTrailColor">@color/highlight_color_lxx_dark</item> + <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_lxx_dark</item> + <item name="languageOnSpacebarTextColor">@color/key_text_inactive_color_lxx_dark</item> + <!-- A negative value to disable text shadow layer. --> + <item name="languageOnSpacebarTextShadowRadius">-1.0</item> + </style> + <style + name="KeyPreviewTextView.LXX_Dark" + parent="KeyPreviewTextView" + > + <item name="android:background">@drawable/keyboard_key_feedback_lxx_dark</item> + </style> + <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, + for instance delete button, need themed {@link KeyboardView} attributes. --> + <style + name="EmojiPalettesView.LXX_Dark" + parent="MainKeyboardView.LXX_Dark" + > + <item name="categoryIndicatorEnabled">false</item> + <item name="categoryPageIndicatorColor">@color/highlight_color_lxx_dark</item> + <item name="categoryPageIndicatorBackground">@color/emoji_tab_page_indicator_background_lxx_dark</item> + <!-- TODO: Update those icons to LXX_Dark theme. --> + <item name="iconEmojiRecentsTab">@drawable/ic_emoji_recents_holo_dark</item> + <item name="iconEmojiCategory1Tab">@drawable/ic_emoji_people_holo_dark</item> + <item name="iconEmojiCategory2Tab">@drawable/ic_emoji_objects_holo_dark</item> + <item name="iconEmojiCategory3Tab">@drawable/ic_emoji_nature_holo_dark</item> + <item name="iconEmojiCategory4Tab">@drawable/ic_emoji_places_holo_dark</item> + <item name="iconEmojiCategory5Tab">@drawable/ic_emoji_symbols_holo_dark</item> + <item name="iconEmojiCategory6Tab">@drawable/ic_emoji_emoticons_holo_dark</item> + </style> + <style + name="MoreKeysKeyboard.LXX_Dark" + parent="Keyboard.LXX_Dark" + > + <item name="keyboardTopPadding">0%p</item> + <item name="keyboardBottomPadding">0%p</item> + <item name="horizontalGap">0%p</item> + <item name="touchPositionCorrectionData">@null</item> + </style> + <style + name="MoreKeysKeyboardView.LXX_Dark" + parent="KeyboardView.LXX_Dark" + > + <item name="android:background">@drawable/keyboard_popup_panel_background_lxx_dark</item> + <!-- Reuse KLP key background --> + <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item> + <item name="keyTypeface">normal</item> + <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> + </style> + <style + name="SuggestionStripView.LXX_Dark" + parent="KeyboardView.LXX_Dark" + > + <item name="suggestionsCountInStrip">@integer/config_suggestions_count_in_strip</item> + <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> + <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> + <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> + <item name="android:background">@color/suggestions_strip_background_lxx_dark</item> + <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item> + <item name="colorValidTypedWord">@color/typed_word_color_lxx_dark</item> + <item name="colorTypedWord">@color/typed_word_color_lxx_dark</item> + <item name="colorAutoCorrect">@color/highlight_color_lxx_dark</item> + <item name="colorSuggested">@color/suggested_word_color_lxx_dark</item> + <item name="alphaObsoleted">70%</item> + </style> + <style + name="SuggestionWord.LXX_Dark" + parent="SuggestionWord" + > + <item name="android:background">@drawable/btn_suggestion_lxx_dark</item> + <item name="android:textColor">@color/highlight_color_lxx_dark</item> + </style> +</resources> diff --git a/java/res/xml-sw600dp/key_shortcut.xml b/java/res/xml-sw600dp/key_comma.xml index d24e81f73..67199e237 100644 --- a/java/res/xml-sw600dp/key_shortcut.xml +++ b/java/res/xml-sw600dp/key_comma.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2012, The Android Open Source Project +** 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. @@ -21,33 +21,27 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > + <!-- The table comma key which may have settings as popup key. --> + <!-- Kept as a separate file for cleaner overriding by an overlay. --> + <key-style + latin:styleName="baseTabletCommaKeyStyle" + latin:keySpec="!text/keyspec_tablet_comma" + latin:keyHintLabel="!text/keyhintlabel_tablet_comma" + latin:keyLabelFlags="hasPopupHint" + latin:parentStyle="hasShiftedLetterHintStyle" /> <switch> <case - latin:supportsSwitchingToShortcutIme="true" - latin:clobberSettingsKey="false" - > - <Key - latin:keyStyle="shortcutKeyStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_settings" /> - </case> - <case - latin:supportsSwitchingToShortcutIme="true" latin:clobberSettingsKey="true" > <Key - latin:keyStyle="shortcutKeyStyle" /> + latin:moreKeys="!text/morekeys_tablet_comma" + latin:keyStyle="baseTabletCommaKeyStyle" /> </case> - <case - latin:supportsSwitchingToShortcutIme="false" - latin:clobberSettingsKey="false" - > - <Key - latin:keyStyle="settingsKeyStyle" /> - </case> - <!-- supportsSwitchingToShortcutIme="false" clobberSettingsKey="true" --> + <!-- clobberSettingsKey="false" --> <default> - <Spacer /> + <Key + latin:moreKeys="!text/morekeys_tablet_comma,!text/keyspec_settings" + latin:keyStyle="baseTabletCommaKeyStyle" /> </default> </switch> </merge> diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml-sw600dp/key_period.xml index 23172cf18..d2909d82d 100644 --- a/java/res/xml-sw600dp/keys_comma_period.xml +++ b/java/res/xml-sw600dp/key_period.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** 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. @@ -21,13 +21,8 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="!text/keyspec_tablet_comma" - latin:keyHintLabel="!text/keyhintlabel_tablet_comma" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/morekeys_tablet_comma" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> + <!-- The table period key which may have different label depending on locale --> + <!-- Kept as a separate file for cleaner overriding by an overlay. --> <switch> <case latin:languageCode="hi" diff --git a/java/res/layout/emoji_keyboard_tab_label.xml b/java/res/xml-sw600dp/key_settings.xml index 62c552dd8..45915e948 100644 --- a/java/res/layout/emoji_keyboard_tab_label.xml +++ b/java/res/xml-sw600dp/key_settings.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** 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. @@ -18,9 +18,19 @@ */ --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="0dip" - android:layout_weight="1.0" - android:layout_height="wrap_content" - android:gravity="center" -/> +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:clobberSettingsKey="false" + > + <Key + latin:keyStyle="settingsKeyStyle" /> + </case> + <!-- clobberSettingsKey="true" --> + <default> + <Spacer /> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index 3d5556fe5..7de276901 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -35,6 +35,9 @@ latin:keyLabelFlags="hasShiftedLetterHint" /> </default> </switch> + <!-- Base key style for the key which may have settings key as more keys. --> + <include + latin:keyboardLayout="@xml/key_styles_settings" /> <!-- Functional key styles --> <!-- Base style for shift key. A single space is used for dummy label in moreKeys. --> <key-style @@ -76,9 +79,10 @@ latin:backgroundType="functional" /> <include latin:keyboardLayout="@xml/key_styles_enter" /> + <!-- TODO: Currently there is no way to specify icon alignment per theme. --> <key-style latin:styleName="spaceKeyStyle" - latin:keySpec=" |!code/key_space" + latin:keySpec="!icon/space_key|!code/key_space" latin:keyActionFlags="noKeyPreview|enableLongPress" /> <!-- U+200C: ZERO WIDTH NON-JOINER U+200D: ZERO WIDTH JOINER --> @@ -140,7 +144,7 @@ </switch> <key-style latin:styleName="baseForLayoutSwitchKeyStyle" - latin:keyLabelFlags="preserveCase" + latin:keyLabelFlags="preserveCase|followFunctionalTextColor" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> <key-style diff --git a/java/res/xml-sw600dp/key_styles_enter.xml b/java/res/xml-sw600dp/key_styles_enter.xml index 0699e4527..740bf3543 100644 --- a/java/res/xml-sw600dp/key_styles_enter.xml +++ b/java/res/xml-sw600dp/key_styles_enter.xml @@ -100,9 +100,9 @@ <key-style latin:styleName="defaultEnterKeyStyle" latin:keySpec="!icon/enter_key|!code/key_enter" - latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" latin:keyActionFlags="noKeyPreview" - latin:backgroundType="functional" + latin:backgroundType="action" latin:parentStyle="navigateMoreKeysStyle" /> <switch> <!-- Shift + Enter in textMultiLine field. --> @@ -117,10 +117,29 @@ </case> <case latin:imeAction="actionGo" + latin:isIconDefined="go_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/go_key|!code/key_enter" + latin:backgroundType="action" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionGo" > <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_go_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionNext" + latin:isIconDefined="next_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/next_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -130,6 +149,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_next_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionPrevious" + latin:isIconDefined="previous_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/previous_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -139,6 +167,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_previous_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionDone" + latin:isIconDefined="done_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/done_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -148,6 +185,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_done_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionSend" + latin:isIconDefined="send_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/send_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -157,11 +203,11 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_send_key|!code/key_enter" - latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> <case latin:imeAction="actionSearch" + latin:isIconDefined="search_key" > <key-style latin:styleName="enterKeyStyle" @@ -170,13 +216,20 @@ latin:parentStyle="defaultEnterKeyStyle" /> </case> <case + latin:imeAction="actionSearch" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!text/label_search_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case latin:imeAction="actionCustomLabel" > <key-style latin:styleName="enterKeyStyle" latin:keySpec="dummy_label|!code/key_enter" latin:keyLabelFlags="fromCustomActionLabel" - latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> <!-- imeAction is either actionNone or actionUnspecified. --> diff --git a/java/res/xml-sw600dp/row_dvorak4.xml b/java/res/xml-sw600dp/row_dvorak4.xml index 2ba6a491b..ab2b5603d 100644 --- a/java/res/xml-sw600dp/row_dvorak4.xml +++ b/java/res/xml-sw600dp/row_dvorak4.xml @@ -29,20 +29,20 @@ latin:keyStyle="toSymbolKeyStyle" latin:keyWidth="10.0%p" /> <include - latin:keyboardLayout="@xml/key_shortcut" /> - <include - latin:keyboardLayout="@xml/key_f1" /> + latin:keyboardLayout="@xml/key_settings" /> + <Key + latin:keySpec="_" + latin:keyHintLabel="-" + latin:moreKeys="-" + latin:keyStyle="hasShiftedLetterHintStyle" /> <include latin:keyXPos="28.0%p" latin:keyboardLayout="@xml/key_space_5kw" latin:backgroundType="normal" /> <include + latin:keyboardLayout="@xml/key_f1" /> + <include latin:keyboardLayout="@xml/key_question_exclamation" /> - <Key - latin:keySpec="-" - latin:keyHintLabel="_" - latin:moreKeys="_" - latin:keyStyle="hasShiftedLetterHintStyle" /> <include latin:keyboardLayout="@xml/key_f2" /> </Row> diff --git a/java/res/xml-sw600dp/row_pcqwerty5.xml b/java/res/xml-sw600dp/row_pcqwerty5.xml index 52b581ae6..ac07f11c2 100644 --- a/java/res/xml-sw600dp/row_pcqwerty5.xml +++ b/java/res/xml-sw600dp/row_pcqwerty5.xml @@ -26,7 +26,7 @@ > <include latin:keyWidth="9.0%p" - latin:keyboardLayout="@xml/key_shortcut" /> + latin:keyboardLayout="@xml/key_settings" /> <switch> <case latin:languageSwitchKeyEnabled="true" diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml index 7969dd8a5..0eb86f2d4 100644 --- a/java/res/xml-sw600dp/row_qwerty4.xml +++ b/java/res/xml-sw600dp/row_qwerty4.xml @@ -29,15 +29,18 @@ latin:keyStyle="toSymbolKeyStyle" latin:keyWidth="10.0%p" /> <include - latin:keyboardLayout="@xml/key_shortcut" /> - <include - latin:keyboardLayout="@xml/key_f1" /> + latin:keyboardLayout="@xml/key_comma" /> + <Key + latin:keySpec="_" /> + <!-- Space key. --> <include latin:keyXPos="28.0%p" latin:keyboardLayout="@xml/key_space_5kw" latin:backgroundType="normal" /> <include - latin:keyboardLayout="@xml/keys_comma_period" /> + latin:keyboardLayout="@xml/key_f1" /> + <include + latin:keyboardLayout="@xml/key_period" /> <include latin:keyboardLayout="@xml/key_f2" /> </Row> diff --git a/java/res/xml-sw600dp/rows_marathi.xml b/java/res/xml-sw600dp/rows_marathi.xml new file mode 100644 index 000000000..51dc7a2b9 --- /dev/null +++ b/java/res/xml-sw600dp/rows_marathi.xml @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi2" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi3" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-v16/keystyle_devanagari_sign_anusvara.xml b/java/res/xml-v16/keystyle_devanagari_sign_anusvara.xml index 71439d68c..405aebc50 100644 --- a/java/res/xml-v16/keystyle_devanagari_sign_anusvara.xml +++ b/java/res/xml-v16/keystyle_devanagari_sign_anusvara.xml @@ -36,6 +36,15 @@ latin:styleName="moreKeysDevanagariSignAnusvara" latin:moreKeys="ः,ँ,़" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA + U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU --> + <key-style + latin:styleName="moreKeysDevanagariSignAnusvara" + latin:moreKeys="ः,ँ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariSignAnusvara" /> diff --git a/java/res/xml-v16/keystyle_devanagari_sign_virama.xml b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml index 0c3a29b16..73248e4c2 100644 --- a/java/res/xml-v16/keystyle_devanagari_sign_virama.xml +++ b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml @@ -34,6 +34,14 @@ latin:styleName="moreKeysDevanagariSignVirama" latin:moreKeys="्" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0905: "अ" DEVANAGARI LETTER A --> + <key-style + latin:styleName="moreKeysDevanagariSignVirama" + latin:moreKeys="अ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariSignVirama" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml index 5bb0351ec..cd07999b5 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml @@ -43,6 +43,14 @@ latin:styleName="moreKeysDevanagariVowelSignAa" latin:moreKeys="ा,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0906: "आ" DEVANAGARI LETTER AA --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAa" + latin:moreKeys="आ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignAa" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml index 8edf6eb1c..75a49b19d 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml @@ -43,6 +43,14 @@ latin:moreKeys="ै,%" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0910: "ऐ" DEVANAGARI LETTER AI --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAi" + latin:moreKeys="ऐ,%" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA --> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml index 212e058d1..26f1aca5a 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml @@ -42,6 +42,14 @@ latin:styleName="moreKeysDevanagariVowelSignAu" latin:moreKeys="ौ,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0914: "औ" DEVANAGARI LETTER AU --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAu" + latin:moreKeys="औ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignAu" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_e.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_e.xml index ef2c3f14b..ec056a380 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_e.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_e.xml @@ -34,9 +34,23 @@ latin:styleName="moreKeysDevanagariVowelSignCandraE" latin:moreKeys="ॅ" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090D: "ऍ" DEVANAGARI LETTER CANDRA E --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignCandraE" + latin:moreKeys="ऍ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignCandraE" /> </default> </switch> + <!-- U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E --> + <key-style + latin:styleName="baseKeyDevanagariVowelSignCandraE" + latin:parentStyle="moreKeysDevanagariVowelSignCandraE" + latin:keySpec="ॅ" + latin:keyLabelFlags="fontNormal" /> </merge> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_o.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_o.xml index ac01d37b7..fb4d4eb17 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_o.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_candra_o.xml @@ -34,6 +34,14 @@ latin:styleName="moreKeysDevanagariVowelSignCandraO" latin:moreKeys="ॉ" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0911: "ऑ" DEVANAGARI LETTER CANDRA O --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignCandraO" + latin:moreKeys="ऑ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignCandraO" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml index 77d6eb5e0..965bccbd2 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml @@ -43,6 +43,14 @@ latin:moreKeys="े" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090F: "ए" DEVANAGARI LETTER SHORT E --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignE" + latin:moreKeys="ए" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml index d79447be5..ec71c4dcd 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml @@ -42,6 +42,14 @@ latin:styleName="moreKeysDevanagariVowelSignI" latin:moreKeys="ि" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0907: "इ" DEVANAGARI LETTER I --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignI" + latin:moreKeys="इ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignI" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml index 0e10f3172..9a9f9158c 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml @@ -42,6 +42,14 @@ latin:styleName="moreKeysDevanagariVowelSignIi" latin:moreKeys="ी,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0908: "ई" DEVANAGARI LETTER II --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignIi" + latin:moreKeys="ई,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignIi" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml index 47ca906ce..77389c22d 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml @@ -44,6 +44,14 @@ latin:styleName="moreKeysDevanagariVowelSignO" latin:moreKeys="ो" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0913: "ओ" DEVANAGARI LETTER O --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignO" + latin:moreKeys="ओ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignO" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml index 694e4abe7..e2167bf24 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml @@ -43,6 +43,14 @@ latin:styleName="moreKeysDevanagariVowelSignU" latin:moreKeys="ु" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0909: "उ" DEVANAGARI LETTER U --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignU" + latin:moreKeys="उ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignU" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml index f17489e3a..745236816 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml @@ -43,6 +43,14 @@ latin:styleName="moreKeysDevanagariVowelSignUu" latin:moreKeys="ू,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090A: "ऊ" DEVANAGARI LETTER UU --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignUu" + latin:moreKeys="ऊ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignUu" /> diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_vocalic_r.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_vocalic_r.xml index 27098466d..9c930d348 100644 --- a/java/res/xml-v16/keystyle_devanagari_vowel_sign_vocalic_r.xml +++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_vocalic_r.xml @@ -44,6 +44,16 @@ latin:moreKeys="ऋ,ृ" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0931: "ऱ" DEVANAGARI LETTER RRA + U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R + U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignVocalicR" + latin:moreKeys="ऱ,ऋ,ृ" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+0913: "ओ" DEVANAGARI LETTER O --> diff --git a/java/res/xml/keys_comma_period_symbols.xml b/java/res/xml/kbd_marathi.xml index 843595c27..4328cd6d3 100644 --- a/java/res/xml/keys_comma_period_symbols.xml +++ b/java/res/xml/kbd_marathi.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2013, The Android Open Source Project +** 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. @@ -18,13 +18,9 @@ */ --> -<merge +<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="!text/keyspec_comma" /> - <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> - <Key - latin:keySpec="." - latin:moreKeys="…" /> -</merge> + <include + latin:keyboardLayout="@xml/rows_marathi" /> +</Keyboard> diff --git a/java/res/xml/key_f1.xml b/java/res/xml/key_f1.xml index c96ddcac1..7bd7385a1 100644 --- a/java/res/xml/key_f1.xml +++ b/java/res/xml/key_f1.xml @@ -27,37 +27,20 @@ > <Key latin:keySpec="/" - latin:keyStyle="f1MoreKeysStyle" /> + latin:keyStyle="settingsMoreKeysStyle" /> </case> <case latin:mode="email" > <Key latin:keySpec="\@" - latin:keyStyle="f1MoreKeysStyle" /> + latin:keyStyle="settingsMoreKeysStyle" /> </case> - <case - latin:supportsSwitchingToShortcutIme="false" - > - <Key - latin:keySpec="!text/keyspec_comma" - latin:keyLabelFlags="hasPopupHint" - latin:keyStyle="f1MoreKeysStyle" /> - </case> - <!-- latin:supportsSwitchingToShortcutIme="true" --> - <case - latin:hasShortcutKey="true" - > - <Key - latin:keyStyle="shortcutKeyStyle" /> - </case> - <!-- latin:hasShortcutKey="false" --> <default> <Key latin:keySpec="!text/keyspec_comma" latin:keyLabelFlags="hasPopupHint" - latin:additionalMoreKeys="!text/keyspec_shortcut" - latin:keyStyle="f1MoreKeysStyle" /> + latin:keyStyle="settingsMoreKeysStyle" /> </default> </switch> </merge> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index 78e030132..8b3bb2e5c 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -35,9 +35,9 @@ latin:keyLabelFlags="hasShiftedLetterHint" /> </default> </switch> - <!-- Base key style for the key which may have settings or tab key as popup key. --> + <!-- Base key style for the key which may have settings key as more keys. --> <include - latin:keyboardLayout="@xml/key_styles_f1" /> + latin:keyboardLayout="@xml/key_styles_settings" /> <!-- Functional key styles --> <!-- Base style for shift key. A single space is used for dummy label in moreKeys. --> <key-style @@ -82,12 +82,13 @@ latin:styleName="emojiKeyStyle" latin:keySpec="!icon/emoji_key|!code/key_emoji" latin:keyActionFlags="noKeyPreview" - latin:backgroundType="functional" /> + latin:backgroundType="action" /> <include latin:keyboardLayout="@xml/key_styles_enter" /> + <!-- TODO: Currently there is no way to specify icon alignment per theme. --> <key-style latin:styleName="spaceKeyStyle" - latin:keySpec=" |!code/key_space" + latin:keySpec="!icon/space_key|!code/key_space" latin:keyActionFlags="noKeyPreview|enableLongPress" /> <!-- U+200C: ZERO WIDTH NON-JOINER U+200D: ZERO WIDTH JOINER --> @@ -103,7 +104,7 @@ latin:keyIconDisabled="!icon/shortcut_key_disabled" latin:keyActionFlags="noKeyPreview|altCodeWhileTyping" latin:altCode="!code/key_space" - latin:parentStyle="f1MoreKeysStyle" /> + latin:parentStyle="settingsMoreKeysStyle" /> <key-style latin:styleName="settingsKeyStyle" latin:keySpec="!icon/settings_key|!code/key_settings" @@ -128,7 +129,7 @@ latin:keyIconPreview="!icon/tab_key_preview" /> <key-style latin:styleName="baseForLayoutSwitchKeyStyle" - latin:keyLabelFlags="preserveCase" + latin:keyLabelFlags="preserveCase|followFunctionalTextColor" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> <key-style diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml index acb27abb1..50530e1bf 100644 --- a/java/res/xml/key_styles_enter.xml +++ b/java/res/xml/key_styles_enter.xml @@ -255,9 +255,9 @@ <!-- Enter key style --> <key-style latin:styleName="defaultEnterKeyStyle" - latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" latin:keyActionFlags="noKeyPreview" - latin:backgroundType="functional" + latin:backgroundType="action" latin:parentStyle="navigateMoreKeysStyle" /> <key-style latin:styleName="shiftEnterKeyStyle" @@ -284,10 +284,29 @@ </case> <case latin:imeAction="actionGo" + latin:isIconDefined="go_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/go_key|!code/key_enter" + latin:backgroundType="action" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionGo" > <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_go_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionNext" + latin:isIconDefined="next_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/next_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -297,6 +316,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_next_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionPrevious" + latin:isIconDefined="previous_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/previous_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -306,6 +334,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_previous_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionDone" + latin:isIconDefined="done_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/done_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -315,6 +352,15 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_done_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case + latin:imeAction="actionSend" + latin:isIconDefined="send_key" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!icon/send_key|!code/key_enter" latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> @@ -324,11 +370,11 @@ <key-style latin:styleName="enterKeyStyle" latin:keySpec="!text/label_send_key|!code/key_enter" - latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> <case latin:imeAction="actionSearch" + latin:isIconDefined="search_key" > <key-style latin:styleName="enterKeyStyle" @@ -337,13 +383,20 @@ latin:parentStyle="defaultEnterKeyStyle" /> </case> <case + latin:imeAction="actionSearch" + > + <key-style + latin:styleName="enterKeyStyle" + latin:keySpec="!text/label_search_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <case latin:imeAction="actionCustomLabel" > <key-style latin:styleName="enterKeyStyle" latin:keySpec="dummy_label|!code/key_enter" latin:keyLabelFlags="fromCustomActionLabel" - latin:backgroundType="action" latin:parentStyle="defaultEnterKeyStyle" /> </case> <!-- imeAction is either actionNone or actionUnspecified. --> diff --git a/java/res/xml/keys_less_greater.xml b/java/res/xml/key_styles_less_greater.xml index 778de02a1..db4c7984b 100644 --- a/java/res/xml/keys_less_greater.xml +++ b/java/res/xml/key_styles_less_greater.xml @@ -21,25 +21,31 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > + <!-- The less and greater keys' style which may have different label depending on locale. --> + <!-- Kept as a separate file for cleaner overriding by an overlay. --> <switch> <case latin:languageCode="fa" > - <Key + <key-style + latin:styleName="lessKeyStyle" latin:keySpec="!text/keyspec_left_double_angle_quote" latin:backgroundType="functional" latin:moreKeys="!text/morekeys_less_than" /> - <Key + <key-style + latin:styleName="greaterKeyStyle" latin:keySpec="!text/keyspec_right_double_angle_quote" latin:backgroundType="functional" latin:moreKeys="!text/morekeys_greater_than" /> </case> <default> - <Key + <key-style + latin:styleName="lessKeyStyle" latin:keySpec="!text/keyspec_less_than" latin:backgroundType="functional" latin:moreKeys="!text/morekeys_less_than" /> - <Key + <key-style + latin:styleName="greaterKeyStyle" latin:keySpec="!text/keyspec_greater_than" latin:backgroundType="functional" latin:moreKeys="!text/morekeys_greater_than" /> diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml index 5c108cf58..8a76fe397 100644 --- a/java/res/xml/key_styles_number.xml +++ b/java/res/xml/key_styles_number.xml @@ -30,7 +30,7 @@ latin:parentStyle="numKeyBaseStyle" /> <key-style latin:styleName="numModeKeyStyle" - latin:keyLabelFlags="fontNormal|followKeyLetterRatio" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio|followFunctionalTextColor" latin:parentStyle="numKeyBaseStyle" /> <key-style latin:styleName="numFunctionalKeyStyle" @@ -120,6 +120,7 @@ <key-style latin:styleName="numSpaceKeyStyle" latin:keySpec="!icon/space_key_for_number_layout|!code/key_space" + latin:keyLabelFlags="alignButtom" latin:keyActionFlags="enableLongPress" latin:parentStyle="numKeyBaseStyle" /> <!-- Override defaultEnterKeyStyle in key_styles_enter.xml --> diff --git a/java/res/xml/key_styles_f1.xml b/java/res/xml/key_styles_settings.xml index 8a07827fa..956b40235 100644 --- a/java/res/xml/key_styles_f1.xml +++ b/java/res/xml/key_styles_settings.xml @@ -21,20 +21,20 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- Base key style for the key which may have settings or tab key as popup key. --> + <!-- Base key style for the key which may have settings key as more keys. --> <!-- Kept as a separate file for cleaner overriding by an overlay. --> <switch> <case latin:clobberSettingsKey="true" > <key-style - latin:styleName="f1MoreKeysStyle" + latin:styleName="settingsMoreKeysStyle" latin:backgroundType="functional" /> </case> <!-- clobberSettingsKey="false" --> <default> <key-style - latin:styleName="f1MoreKeysStyle" + latin:styleName="settingsMoreKeysStyle" latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/keyspec_settings" latin:backgroundType="functional" /> diff --git a/java/res/xml/keyboard_layout_set_marathi.xml b/java/res/xml/keyboard_layout_set_marathi.xml new file mode 100644 index 000000000..e5c68e7ce --- /dev/null +++ b/java/res/xml/keyboard_layout_set_marathi.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<KeyboardLayoutSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_marathi" + latin:enableProximityCharsCorrection="true" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneSymbols" + latin:elementKeyboard="@xml/kbd_phone_symbols" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardLayoutSet> diff --git a/java/res/xml/keystyle_devanagari_sign_anusvara.xml b/java/res/xml/keystyle_devanagari_sign_anusvara.xml index 6dc9b7e3a..a7e421d17 100644 --- a/java/res/xml/keystyle_devanagari_sign_anusvara.xml +++ b/java/res/xml/keystyle_devanagari_sign_anusvara.xml @@ -37,6 +37,16 @@ latin:styleName="moreKeysDevanagariSignAnusvara" latin:moreKeys="◌ः|ः,◌ँ|ँ,◌़|़" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+25CC: "◌" DOTTED CIRCLE + U+0903: "ः" DEVANAGARI SIGN VISARGA + U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU --> + <key-style + latin:styleName="moreKeysDevanagariSignAnusvara" + latin:moreKeys="◌ः|ः,◌ँ|ँ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariSignAnusvara" /> diff --git a/java/res/xml/keystyle_devanagari_sign_virama.xml b/java/res/xml/keystyle_devanagari_sign_virama.xml index 96506e2fc..58dd42a2a 100644 --- a/java/res/xml/keystyle_devanagari_sign_virama.xml +++ b/java/res/xml/keystyle_devanagari_sign_virama.xml @@ -35,6 +35,14 @@ latin:styleName="moreKeysDevanagariSignVirama" latin:moreKeys="◌्|्" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0905: "अ" DEVANAGARI LETTER A --> + <key-style + latin:styleName="moreKeysDevanagariSignVirama" + latin:moreKeys="अ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariSignVirama" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml b/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml index 4b876505a..1a60ca22f 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml @@ -45,6 +45,14 @@ latin:styleName="moreKeysDevanagariVowelSignAa" latin:moreKeys="◌ा|ा,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0906: "आ" DEVANAGARI LETTER AA --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAa" + latin:moreKeys="आ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignAa" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml b/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml index 050a7ce0e..e6b64e56c 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml @@ -45,6 +45,14 @@ latin:moreKeys="◌ै|ै,%" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0910: "ऐ" DEVANAGARI LETTER AI --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAi" + latin:moreKeys="ऐ,%" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA --> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_au.xml b/java/res/xml/keystyle_devanagari_vowel_sign_au.xml index 49e67da38..3e129ec87 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_au.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_au.xml @@ -44,6 +44,14 @@ latin:styleName="moreKeysDevanagariVowelSignAu" latin:moreKeys="◌ौ|ौ,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0914: "औ" DEVANAGARI LETTER AU --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignAu" + latin:moreKeys="औ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignAu" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_candra_e.xml b/java/res/xml/keystyle_devanagari_vowel_sign_candra_e.xml index 86f68d355..b7d0908e1 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_candra_e.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_candra_e.xml @@ -35,9 +35,24 @@ latin:styleName="moreKeysDevanagariVowelSignCandraE" latin:moreKeys="◌ॅ|ॅ" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090D: "ऍ" DEVANAGARI LETTER CANDRA E --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignCandraE" + latin:moreKeys="ऍ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignCandraE" /> </default> </switch> + <!-- U+25CC: "◌" DOTTED CIRCLE + U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E --> + <key-style + latin:styleName="baseKeyDevanagariVowelSignCandraE" + latin:parentStyle="moreKeysDevanagariVowelSignCandraE" + latin:keySpec="◌ॅ|ॅ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> </merge> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_candra_o.xml b/java/res/xml/keystyle_devanagari_vowel_sign_candra_o.xml index fd711e049..861dd1a3d 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_candra_o.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_candra_o.xml @@ -35,6 +35,14 @@ latin:styleName="moreKeysDevanagariVowelSignCandraO" latin:moreKeys="◌ॉ|ॉ" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0911: "ऑ" DEVANAGARI LETTER CANDRA O --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignCandraO" + latin:moreKeys="ऑ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignCandraO" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_e.xml b/java/res/xml/keystyle_devanagari_vowel_sign_e.xml index 88f6a74f4..95d85a91d 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_e.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_e.xml @@ -45,6 +45,14 @@ latin:moreKeys="◌े|े" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090F: "ए" DEVANAGARI LETTER SHORT E --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignE" + latin:moreKeys="ए" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+25CC: "◌" DOTTED CIRCLE @@ -59,6 +67,8 @@ latin:styleName="moreKeysDevanagariVowelSignE" /> </default> </switch> + <!-- U+25CC: "◌" DOTTED CIRCLE + U+0947: "े" DEVANAGARI VOWEL SIGN E --> <key-style latin:styleName="baseKeyDevanagariVowelSignE" latin:parentStyle="moreKeysDevanagariVowelSignE" diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_i.xml b/java/res/xml/keystyle_devanagari_vowel_sign_i.xml index a84fdb4a9..5817be1c6 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_i.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_i.xml @@ -44,6 +44,14 @@ latin:styleName="moreKeysDevanagariVowelSignI" latin:moreKeys="◌ि|ि" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0907: "इ" DEVANAGARI LETTER I --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignI" + latin:moreKeys="इ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignI" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml b/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml index 6f6eb0f15..a7863c65f 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml @@ -44,6 +44,14 @@ latin:styleName="moreKeysDevanagariVowelSignIi" latin:moreKeys="◌ी|ी,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0908: "ई" DEVANAGARI LETTER II --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignIi" + latin:moreKeys="ई,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignIi" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_o.xml b/java/res/xml/keystyle_devanagari_vowel_sign_o.xml index 68b176a43..3dc874e1a 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_o.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_o.xml @@ -46,6 +46,14 @@ latin:styleName="moreKeysDevanagariVowelSignO" latin:moreKeys="◌ो|ो" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0913: "ओ" DEVANAGARI LETTER O --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignO" + latin:moreKeys="ओ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignO" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_u.xml b/java/res/xml/keystyle_devanagari_vowel_sign_u.xml index 7c058b174..226c11a06 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_u.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_u.xml @@ -45,6 +45,14 @@ latin:styleName="moreKeysDevanagariVowelSignU" latin:moreKeys="◌ु|ु" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0909: "उ" DEVANAGARI LETTER U --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignU" + latin:moreKeys="उ" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignU" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml b/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml index 73ab63c89..7a9f47d77 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml @@ -45,6 +45,14 @@ latin:styleName="moreKeysDevanagariVowelSignUu" latin:moreKeys="◌ू|ू,%" /> </case> + <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+090A: "ऊ" DEVANAGARI LETTER UU --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignUu" + latin:moreKeys="ऊ,%" /> + </case> <default> <key-style latin:styleName="moreKeysDevanagariVowelSignUu" /> diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_vocalic_r.xml b/java/res/xml/keystyle_devanagari_vowel_sign_vocalic_r.xml index 29b083eb3..56b739642 100644 --- a/java/res/xml/keystyle_devanagari_vowel_sign_vocalic_r.xml +++ b/java/res/xml/keystyle_devanagari_vowel_sign_vocalic_r.xml @@ -46,6 +46,17 @@ latin:moreKeys="ऋ,◌ृ|ृ" /> </case> <case + latin:keyboardLayoutSet="marathi" + > + <!-- U+0931: "ऱ" DEVANAGARI LETTER RRA + U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R + U+25CC: "◌" DOTTED CIRCLE + U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R --> + <key-style + latin:styleName="moreKeysDevanagariVowelSignVocalicR" + latin:moreKeys="ऱ,ऋ,◌ृ|ृ" /> + </case> + <case latin:keyboardLayoutSet="nepali_traditional" > <!-- U+0913: "ओ" DEVANAGARI LETTER O --> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 28eceb8db..777a13d42 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -68,6 +68,7 @@ lv: Latvian/qwerty mk: Macedonian/south_slavic mn_MN: Mongolian (Mongolia)/mongolian + (mr_IN: Marathi (India)/marathi) # This is a preliminary keyboard layout. ms_MY: Malay (Malaysia)/qwerty (my_MM: Myanmar (Myanmar)/myanmar) # This is a preliminary keyboard layout. nb: Norwegian Bokmål/nordic @@ -331,6 +332,7 @@ /> <!-- TODO: This hindi_compact keyboard is a preliminary layout. This isn't based on the final specification. --> + <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic_compact" android:subtypeId="0xe49c89a1" @@ -339,6 +341,7 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi_compact,EmojiCapable" android:isAsciiCapable="false" /> + --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x35b7526a" @@ -477,6 +480,18 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable" android:isAsciiCapable="false" /> + <!-- TODO: This marathi keyboard is a preliminary layout. + This isn't based on the final specification. --> + <!-- + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0x747b9f03" + android:imeSubtypeLocale="mr_IN" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=marathi,EmojiCapable" + android:isAsciiCapable="false" + /> + --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x84c87c61" @@ -487,14 +502,16 @@ /> <!-- TODO: This Myanmar keyboard is a preliminary layout. This isn't based on the final specification. --> + <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xea266ea4" android:imeSubtypeLocale="my_MM" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable,CombiningRules=MyanmarReordering" android:isAsciiCapable="false" /> + --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3f12ee14" diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 7d86dbd5d..0e9c16190 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -95,6 +95,12 @@ android:summary="@string/use_personalized_dicts_summary" android:persistent="true" android:defaultValue="true" /> + <!-- title will be set programmatically to embed application name --> + <CheckBoxPreference + android:key="pref_enable_metrics_logging" + android:summary="@string/enable_metrics_logging_summary" + android:persistent="true" + android:defaultValue="true" /> </PreferenceCategory> <PreferenceCategory android:title="@string/gesture_typing_category" @@ -153,17 +159,17 @@ android:defaultValue="true" /> <CheckBoxPreference android:key="pref_include_other_imes_in_language_switch_list" + android:dependency="pref_show_language_switch_key" android:title="@string/include_other_imes_in_language_switch_list" android:summary="@string/include_other_imes_in_language_switch_list_summary" android:persistent="true" android:defaultValue="false" /> <ListPreference - android:key="pref_keyboard_layout_20110916" + android:key="pref_keyboard_theme" android:title="@string/keyboard_color_scheme" android:persistent="true" android:entryValues="@array/keyboard_theme_ids" - android:entries="@array/keyboard_theme_names" - android:defaultValue="@string/config_default_keyboard_theme_id" /> + android:entries="@array/keyboard_theme_names" /> <PreferenceScreen android:fragment="com.android.inputmethod.latin.settings.AdditionalSubtypeSettings" android:key="custom_input_styles" diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml index c333b0751..0bcc5cbe4 100644 --- a/java/res/xml/prefs_for_debug.xml +++ b/java/res/xml/prefs_for_debug.xml @@ -21,12 +21,6 @@ android:key="english_ime_debug_settings" > <CheckBoxPreference - android:key="enable_logging" - android:title="@string/prefs_enable_log" - android:summary="@string/prefs_description_log" - android:persistent="true" - android:defaultValue="false" /> - <CheckBoxPreference android:key="debug_mode" android:title="@string/prefs_debug_mode" android:persistent="true" @@ -37,11 +31,6 @@ android:persistent="true" android:defaultValue="false" /> <CheckBoxPreference - android:key="usability_study_mode" - android:title="@string/prefs_usability_study_mode" - android:persistent="true" - android:defaultValue="false" /> - <CheckBoxPreference android:key="pref_sliding_key_input_preview" android:title="@string/sliding_key_input_preview" android:summary="@string/sliding_key_input_preview_summary" @@ -72,16 +61,8 @@ <PreferenceScreen android:key="read_external_dictionary" android:title="@string/prefs_read_external_dictionary" /> - <PreferenceScreen - android:key="dump_contacts_dict" - android:title="@string/prefs_dump_contacts_dict" /> - <PreferenceScreen - android:key="dump_user_dict" - android:title="@string/prefs_dump_user_dict" /> - <PreferenceScreen - android:key="dump_user_history_dict" - android:title="@string/prefs_dump_user_history_dict" /> - <PreferenceScreen - android:key="dump_personalization_dict" - android:title="@string/prefs_dump_personalization_dict" /> + <PreferenceCategory + android:key="pref_key_dump_dictionaries" + android:title="@string/prefs_dump_dynamic_dicts"> + </PreferenceCategory> </PreferenceScreen> diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml index 91462cb9c..e7a3ee736 100644 --- a/java/res/xml/row_dvorak4.xml +++ b/java/res/xml/row_dvorak4.xml @@ -30,8 +30,7 @@ <Key latin:keySpec="q" latin:backgroundType="normal" - latin:additionalMoreKeys="!text/keyspec_shortcut" - latin:keyStyle="f1MoreKeysStyle" /> + latin:keyStyle="settingsMoreKeysStyle" /> <include latin:keyXPos="25%p" latin:keyboardLayout="@xml/key_space_5kw" /> diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml index 3782763a8..32c5389cc 100644 --- a/java/res/xml/row_pcqwerty5.xml +++ b/java/res/xml/row_pcqwerty5.xml @@ -26,13 +26,6 @@ > <switch> <case - latin:hasShortcutKey="true" - > - <Key - latin:keyStyle="shortcutKeyStyle" - latin:keyWidth="11.538%p" /> - </case> - <case latin:clobberSettingsKey="false" > <Key diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml index 09f6b628c..2be03bd6d 100644 --- a/java/res/xml/row_symbols4.xml +++ b/java/res/xml/row_symbols4.xml @@ -20,11 +20,15 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <Key - latin:keySpec="_" /> + latin:keySpec="!text/keyspec_comma" /> <Key - latin:keySpec="/" /> + latin:keySpec="_" /> <include latin:keyboardLayout="@xml/key_space_symbols" /> - <include - latin:keyboardLayout="@xml/keys_comma_period_symbols" /> + <Key + latin:keySpec="/" /> + <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> + <Key + latin:keySpec="." + latin:moreKeys="…" /> </merge> diff --git a/java/res/xml/row_symbols_shift4.xml b/java/res/xml/row_symbols_shift4.xml index f75575bc6..4fc63c2c5 100644 --- a/java/res/xml/row_symbols_shift4.xml +++ b/java/res/xml/row_symbols_shift4.xml @@ -18,9 +18,18 @@ */ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - - <include latin:keyboardLayout="@xml/keys_less_greater" /> + <include + latin:keyboardLayout="@xml/key_styles_less_greater" /> + <Key + latin:keySpec="!text/keyspec_comma" /> + <Key + latin:keyStyle="lessKeyStyle" /> <include latin:keyboardLayout="@xml/key_space_symbols" /> - <include latin:keyboardLayout="@xml/keys_comma_period_symbols" /> + <Key + latin:keyStyle="greaterKeyStyle" /> + <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> + <Key + latin:keySpec="." + latin:moreKeys="…" /> </merge> diff --git a/java/res/xml/rowkeys_marathi1.xml b/java/res/xml/rowkeys_marathi1.xml new file mode 100644 index 000000000..810e71e3c --- /dev/null +++ b/java/res/xml/rowkeys_marathi1.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" /> + <!-- U+0967: "१" DEVANAGARI DIGIT ONE --> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignAu" + latin:keyHintLabel="1" + latin:additionalMoreKeys="१,1" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" /> + <!-- U+0968: "२" DEVANAGARI DIGIT TWO --> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignAi" + latin:keyHintLabel="2" + latin:additionalMoreKeys="२,2" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_aa" /> + <!-- U+0969: "३" DEVANAGARI DIGIT THREE --> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignAa" + latin:keyHintLabel="3" + latin:additionalMoreKeys="३,3" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ii" /> + <!-- U+096A: "४" DEVANAGARI DIGIT FOUR --> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignIi" + latin:keyHintLabel="4" + latin:additionalMoreKeys="४,4" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_uu" /> + <!-- U+096B: "५" DEVANAGARI DIGIT FIVE --> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignUu" + latin:keyHintLabel="5" + latin:additionalMoreKeys="५,5" /> + <!-- U+092C: "ब" DEVANAGARI LETTER BA + U+092D: "भ" DEVANAGARI LETTER BHA + U+096C: "६" DEVANAGARI DIGIT SIX --> + <Key + latin:keySpec="ब" + latin:moreKeys="भ,%" + latin:keyHintLabel="6" + latin:additionalMoreKeys="६,6" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0939: "ह" DEVANAGARI LETTER HA + U+096D: "७" DEVANAGARI DIGIT SEVEN --> + <Key + latin:keySpec="ह" + latin:keyHintLabel="7" + latin:additionalMoreKeys="७,7" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0917: "ग" DEVANAGARI LETTER GA + U+0918: "घ" DEVANAGARI LETTER GHA + U+096E: "८" DEVANAGARI DIGIT EIGHT --> + <Key + latin:keySpec="ग" + latin:moreKeys="घ,%" + latin:keyHintLabel="8" + latin:additionalMoreKeys="८,8" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0926: "द" DEVANAGARI LETTER DA + U+0927: "ध" DEVANAGARI LETTER DHA + U+096F: "९" DEVANAGARI DIGIT NINE --> + <Key + latin:keySpec="द" + latin:moreKeys="ध,%" + latin:keyHintLabel="9" + latin:additionalMoreKeys="९,9" + latin:keyLabelFlags="fontNormal" /> + <!-- U+091C: "ज" DEVANAGARI LETTER JA + U+091D: "झ" DEVANAGARI LETTER JHA + U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER JHA --> + <Key + latin:keySpec="ज" + latin:moreKeys="झ,ज्ञ,%" + latin:keyHintLabel="0" + latin:additionalMoreKeys="०,0" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0921: "ड" DEVANAGARI LETTER DDA + U+0922: "ढ" DEVANAGARI LETTER DDHA --> + <Key + latin:keySpec="ड" + latin:moreKeys="ढ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml/rowkeys_marathi2.xml b/java/res/xml/rowkeys_marathi2.xml new file mode 100644 index 000000000..f95091529 --- /dev/null +++ b/java/res/xml/rowkeys_marathi2.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_o" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignO" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignE" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" /> + <Key + latin:keyStyle="baseKeyDevanagariSignVirama" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_i" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignI" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_u" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignU" /> + <!-- U+092A: "प" DEVANAGARI LETTER PA + U+092B: "फ" DEVANAGARI LETTER PHA --> + <Key + latin:keySpec="प" + latin:moreKeys="फ" + latin:keyLabelFlags="fontNormal" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_vocalic_r" /> + <!-- U+0930: "र" DEVANAGARI LETTER RA --> + <Key + latin:keySpec="र" + latin:keyStyle="moreKeysDevanagariVowelSignVocalicR" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0915: "क" DEVANAGARI LETTER KA + U+0916: "ख" DEVANAGARI LETTER KHA --> + <Key + latin:keySpec="क" + latin:moreKeys="ख" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0924: "त" DEVANAGARI LETTER TA + U+0925: "थ" DEVANAGARI LETTER THA + U+0924/U+094D/U+0930: "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA --> + <Key + latin:keySpec="त" + latin:moreKeys="थ,त्र" + latin:keyLabelFlags="fontNormal" /> + <!-- U+091A: "च" DEVANAGARI LETTER CA + U+091B: "छ" DEVANAGARI LETTER CHA --> + <Key + latin:keySpec="च" + latin:moreKeys="छ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+091F: "ट" DEVANAGARI LETTER TTA + U+0920: "ठ" DEVANAGARI LETTER TTHA --> + <Key + latin:keySpec="ट" + latin:moreKeys="ठ" + latin:keyLabelFlags="fontNormal" /> +</merge> diff --git a/java/res/xml/rowkeys_marathi3.xml b/java/res/xml/rowkeys_marathi3.xml new file mode 100644 index 000000000..17fc5ac77 --- /dev/null +++ b/java/res/xml/rowkeys_marathi3.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_candra_o" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignCandraO" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_candra_e" /> + <Key + latin:keyStyle="baseKeyDevanagariVowelSignCandraE" /> + <!-- Because the font rendering system prior to API version 16 can't automatically + render dotted circle for incomplete combining letter of some scripts, different + set of Key definitions are needed based on the API version. --> + <include + latin:keyboardLayout="@xml/keystyle_devanagari_sign_anusvara" /> + <Key + latin:keyStyle="baseKeyDevanagariSignAnusvara" /> + <!-- U+092E: "म" DEVANAGARI LETTER MA --> + <Key + latin:keySpec="म" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0928: "न" DEVANAGARI LETTER NA + U+0923: "ण" DEVANAGARI LETTER NNA + U+091E: "ञ" DEVANAGARI LETTER NYA + U+0919: "ङ" DEVANAGARI LETTER NGA --> + <Key + latin:keySpec="न" + latin:moreKeys="ण,ञ,ङ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0935: "व" DEVANAGARI LETTER VA --> + <Key + latin:keySpec="व" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0932: "ल" DEVANAGARI LETTER LA + U+0933: "ळ" DEVANAGARI LETTER LLA --> + <Key + latin:keySpec="ल" + latin:moreKeys="ळ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0938: "स" DEVANAGARI LETTER SA + U+0936: "श" DEVANAGARI LETTER SHA + U+0937: "ष" DEVANAGARI LETTER SSA + U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA --> + <Key + latin:keySpec="स" + latin:moreKeys="श,ष,श्र" + latin:keyLabelFlags="fontNormal" /> + <!-- U+092F: "य" DEVANAGARI LETTER YA --> + <Key + latin:keySpec="य" + latin:keyLabelFlags="fontNormal" /> + <!-- U+0915/U+094D/U+0937: "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA --> + <Key + latin:keySpec="क्ष" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> +</merge> diff --git a/java/res/xml/rows_marathi.xml b/java/res/xml/rows_marathi.xml new file mode 100644 index 000000000..42a036374 --- /dev/null +++ b/java/res/xml/rows_marathi.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi1" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi2" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_marathi3" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml index 859a1624a..d8d15080e 100644 --- a/java/res/xml/rows_number_normal.xml +++ b/java/res/xml/rows_number_normal.xml @@ -90,11 +90,6 @@ latin:keyWidth="fillRight" /> </Row> <Row> - <Key - latin:keyStyle="numSpaceKeyStyle" /> - <Key - latin:keySpec="0" - latin:keyStyle="numKeyStyle" /> <switch> <case latin:mode="date" @@ -125,6 +120,11 @@ </default> </switch> <Key + latin:keySpec="0" + latin:keyStyle="numKeyStyle" /> + <Key + latin:keyStyle="numSpaceKeyStyle" /> + <Key latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> </Row> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java new file mode 100644 index 000000000..967cafad0 --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityLongPressTimer.java @@ -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. + */ + +package com.android.inputmethod.accessibility; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.latin.R; + +// Handling long press timer to show a more keys keyboard. +final class AccessibilityLongPressTimer extends Handler { + public interface LongPressTimerCallback { + public void onLongPressed(Key key); + } + + private static final int MSG_LONG_PRESS = 1; + + private final LongPressTimerCallback mCallback; + private final long mConfigAccessibilityLongPressTimeout; + + public AccessibilityLongPressTimer(final LongPressTimerCallback callback, + final Context context) { + super(); + mCallback = callback; + mConfigAccessibilityLongPressTimeout = context.getResources().getInteger( + R.integer.config_accessibility_long_press_key_timeout); + } + + @Override + public void handleMessage(final Message msg) { + switch (msg.what) { + case MSG_LONG_PRESS: + cancelLongPress(); + mCallback.onLongPressed((Key)msg.obj); + return; + default: + super.handleMessage(msg); + return; + } + } + + public void startLongPress(final Key key) { + cancelLongPress(); + final Message longPressMessage = obtainMessage(MSG_LONG_PRESS, key); + sendMessageDelayed(longPressMessage, mConfigAccessibilityLongPressTimeout); + } + + public void cancelLongPress() { + removeMessages(MSG_LONG_PRESS); + } +} diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index bc094b117..2762a9f25 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -67,8 +67,6 @@ public final class AccessibilityUtils { // These only need to be initialized if the kill switch is off. sInstance.initInternal(context); - KeyCodeDescriptionMapper.init(); - AccessibleKeyboardViewProxy.init(context); } public static AccessibilityUtils getInstance() { @@ -115,7 +113,7 @@ public final class AccessibilityUtils { * @param event The event to check. * @return {@true} is the event is a touch exploration event */ - public boolean isTouchExplorationEvent(final MotionEvent event) { + public static boolean isTouchExplorationEvent(final MotionEvent event) { final int action = event.getAction(); return action == MotionEvent.ACTION_HOVER_ENTER || action == MotionEvent.ACTION_HOVER_EXIT diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java deleted file mode 100644 index 322127a12..000000000 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ /dev/null @@ -1,454 +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.accessibility; - -import android.content.Context; -import android.os.SystemClock; -import android.support.v4.view.AccessibilityDelegateCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.view.accessibility.AccessibilityEventCompat; -import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; -import android.util.SparseIntArray; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewParent; -import android.view.accessibility.AccessibilityEvent; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.KeyDetector; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; - -public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat { - private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy(); - - /** Map of keyboard modes to resource IDs. */ - private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray(); - - static { - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time); - KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url); - } - - private MainKeyboardView mView; - private Keyboard mKeyboard; - private AccessibilityEntityProvider mAccessibilityNodeProvider; - - private Key mLastHoverKey = null; - - /** - * Inset in pixels to look for keys when the user's finger exits the keyboard area. - */ - private int mEdgeSlop; - - /** The most recently set keyboard mode. */ - private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN; - private static final int KEYBOARD_IS_HIDDEN = -1; - - public static void init(final Context context) { - sInstance.initInternal(context); - } - - public static AccessibleKeyboardViewProxy getInstance() { - return sInstance; - } - - private AccessibleKeyboardViewProxy() { - // Not publicly instantiable. - } - - private void initInternal(final Context context) { - mEdgeSlop = context.getResources().getDimensionPixelSize( - R.dimen.config_accessibility_edge_slop); - } - - /** - * Sets the view wrapped by this proxy. - * - * @param view The view to wrap. - */ - public void setView(final MainKeyboardView view) { - if (view == null) { - // Ignore null views. - return; - } - mView = view; - - // Ensure that the view has an accessibility delegate. - ViewCompat.setAccessibilityDelegate(view, this); - - if (mAccessibilityNodeProvider == null) { - return; - } - mAccessibilityNodeProvider.setView(view); - - // Since this class is constructed lazily, we might not get a subsequent - // call to setKeyboard() and therefore need to call it now. - setKeyboard(view.getKeyboard()); - } - - /** - * Called when the keyboard layout changes. - * <p> - * <b>Note:</b> This method will be called even if accessibility is not - * enabled. - * @param keyboard The keyboard that is being set to the wrapping view. - */ - public void setKeyboard(final Keyboard keyboard) { - if (keyboard == null) { - return; - } - if (mAccessibilityNodeProvider != null) { - mAccessibilityNodeProvider.setKeyboard(keyboard); - } - final Keyboard lastKeyboard = mKeyboard; - final int lastKeyboardMode = mLastKeyboardMode; - mKeyboard = keyboard; - mLastKeyboardMode = keyboard.mId.mMode; - - // Since this method is called even when accessibility is off, make sure - // to check the state before announcing anything. - if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) { - return; - } - // Announce the language name only when the language is changed. - if (lastKeyboard == null || !lastKeyboard.mId.mSubtype.equals(keyboard.mId.mSubtype)) { - announceKeyboardLanguage(keyboard); - } - // Announce the mode only when the mode is changed. - if (lastKeyboardMode != keyboard.mId.mMode) { - announceKeyboardMode(keyboard); - } - } - - /** - * Called when the keyboard is hidden and accessibility is enabled. - */ - public void onHideWindow() { - if (mView == null) { - return; - } - announceKeyboardHidden(); - mLastKeyboardMode = KEYBOARD_IS_HIDDEN; - } - - /** - * Announces which language of keyboard is being displayed. - * - * @param keyboard The new keyboard. - */ - private void announceKeyboardLanguage(final Keyboard keyboard) { - final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( - keyboard.mId.mSubtype); - sendWindowStateChanged(languageText); - } - - /** - * Announces which type of keyboard is being displayed. - * If the keyboard type is unknown, no announcement is made. - * - * @param keyboard The new keyboard. - */ - private void announceKeyboardMode(final Keyboard keyboard) { - final int mode = keyboard.mId.mMode; - final Context context = mView.getContext(); - final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(mode); - if (modeTextResId == 0) { - return; - } - final String modeText = context.getString(modeTextResId); - final String text = context.getString(R.string.announce_keyboard_mode, modeText); - sendWindowStateChanged(text); - } - - /** - * Announces that the keyboard has been hidden. - */ - private void announceKeyboardHidden() { - final Context context = mView.getContext(); - final String text = context.getString(R.string.announce_keyboard_hidden); - - sendWindowStateChanged(text); - } - - /** - * Sends a window state change event with the specified text. - * - * @param text The text to send with the event. - */ - private void sendWindowStateChanged(final String text) { - final AccessibilityEvent stateChange = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - mView.onInitializeAccessibilityEvent(stateChange); - stateChange.getText().add(text); - stateChange.setContentDescription(null); - - final ViewParent parent = mView.getParent(); - if (parent != null) { - parent.requestSendAccessibilityEvent(mView, stateChange); - } - } - - /** - * Proxy method for View.getAccessibilityNodeProvider(). This method is called in SDK - * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual - * node hierarchy provider. - * - * @param host The host view for the provider. - * @return The accessibility node provider for the current keyboard. - */ - @Override - public AccessibilityEntityProvider getAccessibilityNodeProvider(final View host) { - if (mView == null) { - return null; - } - return getAccessibilityNodeProvider(); - } - - /** - * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. - * - * @param event The hover event. - * @param keyDetector The {@link KeyDetector} to determine on which key the <code>event</code> - * is hovering. - * @return {@code true} if the event is handled - */ - public boolean dispatchHoverEvent(final MotionEvent event, final KeyDetector keyDetector) { - if (mView == null) { - return false; - } - - final int x = (int) event.getX(); - final int y = (int) event.getY(); - final Key previousKey = mLastHoverKey; - final Key key; - - if (pointInView(x, y)) { - key = keyDetector.detectHitKey(x, y); - } else { - key = null; - } - mLastHoverKey = key; - - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_EXIT: - // Make sure we're not getting an EXIT event because the user slid - // off the keyboard area, then force a key press. - if (key != null) { - final long downTime = simulateKeyPress(key); - simulateKeyRelease(key, downTime); - } - //$FALL-THROUGH$ - case MotionEvent.ACTION_HOVER_ENTER: - return onHoverKey(key, event); - case MotionEvent.ACTION_HOVER_MOVE: - if (key != previousKey) { - return onTransitionKey(key, previousKey, event); - } - return onHoverKey(key, event); - } - return false; - } - - /** - * @return A lazily-instantiated node provider for this view proxy. - */ - private AccessibilityEntityProvider getAccessibilityNodeProvider() { - // Instantiate the provide only when requested. Since the system - // will call this method multiple times it is a good practice to - // cache the provider instance. - if (mAccessibilityNodeProvider == null) { - mAccessibilityNodeProvider = new AccessibilityEntityProvider(mView); - } - return mAccessibilityNodeProvider; - } - - /** - * Utility method to determine whether the given point, in local coordinates, is inside the - * view, where the area of the view is contracted by the edge slop factor. - * - * @param localX The local x-coordinate. - * @param localY The local y-coordinate. - */ - private boolean pointInView(final int localX, final int localY) { - return (localX >= mEdgeSlop) && (localY >= mEdgeSlop) - && (localX < (mView.getWidth() - mEdgeSlop)) - && (localY < (mView.getHeight() - mEdgeSlop)); - } - - /** - * Simulates a key press by injecting touch event into the keyboard view. - * This avoids the complexity of trackers and listeners within the keyboard. - * - * @param key The key to press. - */ - private long simulateKeyPress(final Key key) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final long downTime = SystemClock.uptimeMillis(); - final MotionEvent downEvent = MotionEvent.obtain( - downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0); - mView.onTouchEvent(downEvent); - downEvent.recycle(); - return downTime; - } - - /** - * Simulates a key release by injecting touch event into the keyboard view. - * This avoids the complexity of trackers and listeners within the keyboard. - * - * @param key The key to release. - */ - private void simulateKeyRelease(final Key key, final long downTime) { - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final MotionEvent upEvent = MotionEvent.obtain( - downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); - mView.onTouchEvent(upEvent); - upEvent.recycle(); - } - - /** - * Simulates a transition between two {@link Key}s by sending a HOVER_EXIT on the previous key, - * a HOVER_ENTER on the current key, and a HOVER_MOVE on the current key. - * - * @param currentKey The currently hovered key. - * @param previousKey The previously hovered key. - * @param event The event that triggered the transition. - * @return {@code true} if the event was handled. - */ - private boolean onTransitionKey(final Key currentKey, final Key previousKey, - final MotionEvent event) { - final int savedAction = event.getAction(); - event.setAction(MotionEvent.ACTION_HOVER_EXIT); - onHoverKey(previousKey, event); - event.setAction(MotionEvent.ACTION_HOVER_ENTER); - onHoverKey(currentKey, event); - event.setAction(MotionEvent.ACTION_HOVER_MOVE); - final boolean handled = onHoverKey(currentKey, event); - event.setAction(savedAction); - return handled; - } - - /** - * Handles a hover event on a key. If {@link Key} extended View, this would be analogous to - * calling View.onHoverEvent(MotionEvent). - * - * @param key The currently hovered key. - * @param event The hover event. - * @return {@code true} if the event was handled. - */ - private boolean onHoverKey(final Key key, final MotionEvent event) { - // Null keys can't receive events. - if (key == null) { - return false; - } - final AccessibilityEntityProvider provider = getAccessibilityNodeProvider(); - - switch (event.getAction()) { - case MotionEvent.ACTION_HOVER_ENTER: - provider.sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); - provider.performActionForKey( - key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS, null); - break; - case MotionEvent.ACTION_HOVER_EXIT: - provider.sendAccessibilityEventForKey( - key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); - break; - } - return true; - } - - /** - * Notifies the user of changes in the keyboard shift state. - */ - public void notifyShiftState() { - if (mView == null || mKeyboard == null) { - return; - } - - final KeyboardId keyboardId = mKeyboard.mId; - final int elementId = keyboardId.mElementId; - final Context context = mView.getContext(); - final CharSequence text; - - switch (elementId) { - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: - text = context.getText(R.string.spoken_description_shiftmode_locked); - break; - case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: - text = context.getText(R.string.spoken_description_shiftmode_on); - break; - default: - text = context.getText(R.string.spoken_description_shiftmode_off); - } - AccessibilityUtils.getInstance().announceForAccessibility(mView, text); - } - - /** - * Notifies the user of changes in the keyboard symbols state. - */ - public void notifySymbolsState() { - if (mView == null || mKeyboard == null) { - return; - } - - final KeyboardId keyboardId = mKeyboard.mId; - final int elementId = keyboardId.mElementId; - final Context context = mView.getContext(); - final int resId; - - switch (elementId) { - case KeyboardId.ELEMENT_ALPHABET: - case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: - case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: - resId = R.string.spoken_description_mode_alpha; - break; - case KeyboardId.ELEMENT_SYMBOLS: - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: - resId = R.string.spoken_description_mode_symbol; - break; - case KeyboardId.ELEMENT_PHONE: - resId = R.string.spoken_description_mode_phone; - break; - case KeyboardId.ELEMENT_PHONE_SYMBOLS: - resId = R.string.spoken_description_mode_phone_shift; - break; - default: - return; - } - - final String text = context.getString(resId); - AccessibilityUtils.getInstance().announceForAccessibility(mView, text); - } -} diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 2e6649bf2..7a3510ee1 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -17,6 +17,7 @@ package com.android.inputmethod.accessibility; import android.content.Context; +import android.content.res.Resources; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; @@ -27,37 +28,29 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.StringUtils; -import java.util.HashMap; +import java.util.Locale; -public final class KeyCodeDescriptionMapper { +final class KeyCodeDescriptionMapper { private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName(); + private static final String SPOKEN_LETTER_RESOURCE_NAME_FORMAT = "spoken_accented_letter_%04X"; + private static final String SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT = "spoken_symbol_%04X"; + private static final String SPOKEN_EMOJI_RESOURCE_NAME_FORMAT = "spoken_emoji_%04X"; // The resource ID of the string spoken for obscured keys private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot; - private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); - - // Map of key labels to spoken description resource IDs - private final HashMap<CharSequence, Integer> mKeyLabelMap = CollectionUtils.newHashMap(); - - // Sparse array of spoken description resource IDs indexed by key codes - private final SparseIntArray mKeyCodeMap; - - public static void init() { - sInstance.initInternal(); - } + private static final KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); public static KeyCodeDescriptionMapper getInstance() { return sInstance; } - private KeyCodeDescriptionMapper() { - mKeyCodeMap = new SparseIntArray(); - } + // Sparse array of spoken description resource IDs indexed by key codes + private final SparseIntArray mKeyCodeMap = new SparseIntArray(); - private void initInternal() { + private KeyCodeDescriptionMapper() { // Special non-character codes defined in Keyboard mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space); mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete); @@ -73,19 +66,20 @@ public final class KeyCodeDescriptionMapper { mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous); mKeyCodeMap.put(Constants.CODE_EMOJI, R.string.spoken_description_emoji); + // Because the upper-case and lower-case mappings of the following letters is depending on + // the locale, the upper case descriptions should be defined here. The lower case + // descriptions are handled in {@link #getSpokenLetterDescriptionId(Context,int)}. + // U+0049: "I" LATIN CAPITAL LETTER I + // U+0069: "i" LATIN SMALL LETTER I + // U+0130: "İ" LATIN CAPITAL LETTER I WITH DOT ABOVE + // U+0131: "ı" LATIN SMALL LETTER DOTLESS I + mKeyCodeMap.put(0x0049, R.string.spoken_letter_0049); + mKeyCodeMap.put(0x0130, R.string.spoken_letter_0130); } /** * Returns the localized description of the action performed by a specified * key based on the current keyboard state. - * <p> - * The order of precedence for key descriptions is: - * <ol> - * <li>Manually-defined based on the key label</li> - * <li>Automatic or manually-defined based on the key code</li> - * <li>Automatically based on the key label</li> - * <li>{code null} for keys with no label or key code defined</li> - * </p> * * @param context The package's context. * @param keyboard The keyboard on which the key resides. @@ -114,18 +108,26 @@ public final class KeyCodeDescriptionMapper { return getDescriptionForActionKey(context, keyboard, key); } - if (!TextUtils.isEmpty(key.getLabel())) { - final String label = key.getLabel().trim(); - - // First, attempt to map the label to a pre-defined description. - if (mKeyLabelMap.containsKey(label)) { - return context.getString(mKeyLabelMap.get(label)); - } + if (code == Constants.CODE_OUTPUT_TEXT) { + return key.getOutputText(); } // Just attempt to speak the description. - if (key.getCode() != Constants.CODE_UNSPECIFIED) { - return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); + if (code != Constants.CODE_UNSPECIFIED) { + // If the key description should be obscured, now is the time to do it. + final boolean isDefinedNonCtrl = Character.isDefined(code) + && !Character.isISOControl(code); + if (shouldObscure && isDefinedNonCtrl) { + return context.getString(OBSCURED_KEY_RES_ID); + } + final String description = getDescriptionForCodePoint(context, code); + if (description != null) { + return description; + } + if (!TextUtils.isEmpty(key.getLabel())) { + return key.getLabel(); + } + return context.getString(R.string.spoken_description_unknown); } return null; } @@ -139,7 +141,7 @@ public final class KeyCodeDescriptionMapper { * @param keyboard The keyboard on which the key resides. * @return a character sequence describing the action performed by pressing the key */ - private String getDescriptionForSwitchAlphaSymbol(final Context context, + private static String getDescriptionForSwitchAlphaSymbol(final Context context, final Keyboard keyboard) { final KeyboardId keyboardId = keyboard.mId; final int elementId = keyboardId.mElementId; @@ -177,7 +179,8 @@ public final class KeyCodeDescriptionMapper { * @param keyboard The keyboard on which the key resides. * @return A context-sensitive description of the "Shift" key. */ - private String getDescriptionForShiftKey(final Context context, final Keyboard keyboard) { + private static String getDescriptionForShiftKey(final Context context, + final Keyboard keyboard) { final KeyboardId keyboardId = keyboard.mId; final int elementId = keyboardId.mElementId; final int resId; @@ -189,9 +192,14 @@ public final class KeyCodeDescriptionMapper { break; case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: - case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: resId = R.string.spoken_description_shift_shifted; break; + case KeyboardId.ELEMENT_SYMBOLS: + resId = R.string.spoken_description_symbols_shift; + break; + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: + resId = R.string.spoken_description_symbols_shift_shifted; + break; default: resId = R.string.spoken_description_shift; } @@ -206,7 +214,7 @@ public final class KeyCodeDescriptionMapper { * @param key The key to describe. * @return Returns a context-sensitive description of the "Enter" action key. */ - private String getDescriptionForActionKey(final Context context, final Keyboard keyboard, + private static String getDescriptionForActionKey(final Context context, final Keyboard keyboard, final Key key) { final KeyboardId keyboardId = keyboard.mId; final int actionId = keyboardId.imeAction(); @@ -245,42 +253,91 @@ public final class KeyCodeDescriptionMapper { /** * Returns a localized character sequence describing what will happen when - * the specified key is pressed based on its key code. - * <p> - * The order of precedence for key code descriptions is: - * <ol> - * <li>Manually-defined shift-locked description</li> - * <li>Manually-defined shifted description</li> - * <li>Manually-defined normal description</li> - * <li>Automatic based on the character represented by the key code</li> - * <li>Fall-back for undefined or control characters</li> - * </ol> - * </p> + * the specified key is pressed based on its key code point. * * @param context The package's context. - * @param keyboard The keyboard on which the key resides. - * @param key The key from which to obtain a description. - * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured. - * @return a character sequence describing the action performed by pressing the key + * @param codePoint The code point from which to obtain a description. + * @return a character sequence describing the code point. */ - private String getDescriptionForKeyCode(final Context context, final Keyboard keyboard, - final Key key, final boolean shouldObscure) { - final int code = key.getCode(); - + public String getDescriptionForCodePoint(final Context context, final int codePoint) { // If the key description should be obscured, now is the time to do it. - final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code); - if (shouldObscure && isDefinedNonCtrl) { - return context.getString(OBSCURED_KEY_RES_ID); + final int index = mKeyCodeMap.indexOfKey(codePoint); + if (index >= 0) { + return context.getString(mKeyCodeMap.valueAt(index)); } - if (mKeyCodeMap.indexOfKey(code) >= 0) { - return context.getString(mKeyCodeMap.get(code)); + final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint); + if (accentedLetter != null) { + return accentedLetter; } - if (isDefinedNonCtrl) { - return Character.toString((char) code); + // Here, <code>code</code> may be a base (non-accented) letter. + final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint); + if (unsupportedSymbol != null) { + return unsupportedSymbol; } - if (!TextUtils.isEmpty(key.getLabel())) { - return key.getLabel(); + final String emojiDescription = getSpokenEmojiDescription(context, codePoint); + if (emojiDescription != null) { + return emojiDescription; + } + if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) { + return StringUtils.newSingleCodePointString(codePoint); + } + return null; + } + + // TODO: Remove this method once TTS supports those accented letters' verbalization. + private String getSpokenAccentedLetterDescription(final Context context, final int code) { + final boolean isUpperCase = Character.isUpperCase(code); + final int baseCode = isUpperCase ? Character.toLowerCase(code) : code; + final int baseIndex = mKeyCodeMap.indexOfKey(baseCode); + final int resId = (baseIndex >= 0) ? mKeyCodeMap.valueAt(baseIndex) + : getSpokenDescriptionId(context, baseCode, SPOKEN_LETTER_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + return isUpperCase ? context.getString(R.string.spoken_description_upper_case, spokenText) + : spokenText; + } + + // TODO: Remove this method once TTS supports those symbols' verbalization. + private String getSpokenSymbolDescription(final Context context, final int code) { + final int resId = getSpokenDescriptionId(context, code, SPOKEN_SYMBOL_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + if (!TextUtils.isEmpty(spokenText)) { + return spokenText; + } + // If a translated description is empty, fall back to unknown symbol description. + return context.getString(R.string.spoken_symbol_unknown); + } + + // TODO: Remove this method once TTS supports emoji verbalization. + private String getSpokenEmojiDescription(final Context context, final int code) { + final int resId = getSpokenDescriptionId(context, code, SPOKEN_EMOJI_RESOURCE_NAME_FORMAT); + if (resId == 0) { + return null; + } + final String spokenText = context.getString(resId); + if (!TextUtils.isEmpty(spokenText)) { + return spokenText; + } + // If a translated description is empty, fall back to unknown emoji description. + return context.getString(R.string.spoken_emoji_unknown); + } + + private int getSpokenDescriptionId(final Context context, final int code, + final String resourceNameFormat) { + final String resourceName = String.format(Locale.ROOT, resourceNameFormat, code); + final Resources resources = context.getResources(); + // Note that the resource package name may differ from the context package name. + final String resourcePackageName = resources.getResourcePackageName( + R.string.spoken_description_unknown); + final int resId = resources.getIdentifier(resourceName, "string", resourcePackageName); + if (resId != 0) { + mKeyCodeMap.append(code, resId); } - return context.getString(R.string.spoken_description_unknown, code); + return resId; } } diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java new file mode 100644 index 000000000..d67d9dc4b --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityDelegate.java @@ -0,0 +1,333 @@ +/* + * 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.accessibility; + +import android.content.Context; +import android.support.v4.view.AccessibilityDelegateCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.PointerTracker; + +/** + * This class represents a delegate that can be registered in a class that extends + * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance. + * + * To implement accessibility mode, the target keyboard view has to:<p> + * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view. + * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}. + * + * @param <KV> The keyboard view class type. + */ +public class KeyboardAccessibilityDelegate<KV extends KeyboardView> + extends AccessibilityDelegateCompat { + private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName(); + protected static final boolean DEBUG_HOVER = false; + + protected final KV mKeyboardView; + protected final KeyDetector mKeyDetector; + private Keyboard mKeyboard; + private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider; + private Key mLastHoverKey; + + public static final int HOVER_EVENT_POINTER_ID = 0; + + public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) { + super(); + mKeyboardView = keyboardView; + mKeyDetector = keyDetector; + + // Ensure that the view has an accessibility delegate. + ViewCompat.setAccessibilityDelegate(keyboardView, this); + } + + /** + * Called when the keyboard layout changes. + * <p> + * <b>Note:</b> This method will be called even if accessibility is not + * enabled. + * @param keyboard The keyboard that is being set to the wrapping view. + */ + public void setKeyboard(final Keyboard keyboard) { + if (keyboard == null) { + return; + } + if (mAccessibilityNodeProvider != null) { + mAccessibilityNodeProvider.setKeyboard(keyboard); + } + mKeyboard = keyboard; + } + + protected final Keyboard getKeyboard() { + return mKeyboard; + } + + protected final void setLastHoverKey(final Key key) { + mLastHoverKey = key; + } + + protected final Key getLastHoverKey() { + return mLastHoverKey; + } + + /** + * Sends a window state change event with the specified string resource id. + * + * @param resId The string resource id of the text to send with the event. + */ + protected void sendWindowStateChanged(final int resId) { + if (resId == 0) { + return; + } + final Context context = mKeyboardView.getContext(); + sendWindowStateChanged(context.getString(resId)); + } + + /** + * Sends a window state change event with the specified text. + * + * @param text The text to send with the event. + */ + protected void sendWindowStateChanged(final String text) { + final AccessibilityEvent stateChange = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + mKeyboardView.onInitializeAccessibilityEvent(stateChange); + stateChange.getText().add(text); + stateChange.setContentDescription(null); + + final ViewParent parent = mKeyboardView.getParent(); + if (parent != null) { + parent.requestSendAccessibilityEvent(mKeyboardView, stateChange); + } + } + + /** + * Delegate method for View.getAccessibilityNodeProvider(). This method is called in SDK + * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual + * node hierarchy provider. + * + * @param host The host view for the provider. + * @return The accessibility node provider for the current keyboard. + */ + @Override + public KeyboardAccessibilityNodeProvider getAccessibilityNodeProvider(final View host) { + return getAccessibilityNodeProvider(); + } + + /** + * @return A lazily-instantiated node provider for this view delegate. + */ + protected KeyboardAccessibilityNodeProvider getAccessibilityNodeProvider() { + // Instantiate the provide only when requested. Since the system + // will call this method multiple times it is a good practice to + // cache the provider instance. + if (mAccessibilityNodeProvider == null) { + mAccessibilityNodeProvider = new KeyboardAccessibilityNodeProvider(mKeyboardView); + } + return mAccessibilityNodeProvider; + } + + /** + * Get a key that a hover event is on. + * + * @param event The hover event. + * @return key The key that the <code>event</code> is on. + */ + protected final Key getHoverKeyOf(final MotionEvent event) { + final int actionIndex = event.getActionIndex(); + final int x = (int)event.getX(actionIndex); + final int y = (int)event.getY(actionIndex); + return mKeyDetector.detectHitKey(x, y); + } + + /** + * Receives hover events when touch exploration is turned on in SDK versions ICS and higher. + * + * @param event The hover event. + * @return {@code true} if the event is handled. + */ + public boolean onHoverEvent(final MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_HOVER_ENTER: + onHoverEnter(event); + break; + case MotionEvent.ACTION_HOVER_MOVE: + onHoverMove(event); + break; + case MotionEvent.ACTION_HOVER_EXIT: + onHoverExit(event); + break; + default: + Log.w(getClass().getSimpleName(), "Unknown hover event: " + event); + break; + } + return true; + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_ENTER} event. + * + * @param event A hover enter event. + */ + protected void onHoverEnter(final MotionEvent event) { + final Key key = getHoverKeyOf(event); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnter: key=" + key); + } + if (key != null) { + onHoverEnterTo(key); + } + setLastHoverKey(key); + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_MOVE} event. + * + * @param event A hover move event. + */ + protected void onHoverMove(final MotionEvent event) { + final Key lastKey = getLastHoverKey(); + final Key key = getHoverKeyOf(event); + if (key != lastKey) { + if (lastKey != null) { + onHoverExitFrom(lastKey); + } + if (key != null) { + onHoverEnterTo(key); + } + } + if (key != null) { + onHoverMoveWithin(key); + } + setLastHoverKey(key); + } + + /** + * Process {@link MotionEvent#ACTION_HOVER_EXIT} event. + * + * @param event A hover exit event. + */ + protected void onHoverExit(final MotionEvent event) { + final Key lastKey = getLastHoverKey(); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); + } + if (lastKey != null) { + onHoverExitFrom(lastKey); + } + final Key key = getHoverKeyOf(event); + // Make sure we're not getting an EXIT event because the user slid + // off the keyboard area, then force a key press. + if (key != null) { + onRegisterHoverKey(key, event); + onHoverExitFrom(key); + } + setLastHoverKey(null); + } + + /** + * Register a key that is selected by a hover event + * + * @param key A key to be registered. + * @param event A hover exit event that triggers key registering. + */ + protected void onRegisterHoverKey(final Key key, final MotionEvent event) { + if (DEBUG_HOVER) { + Log.d(TAG, "onRegisterHoverKey: key=" + key); + } + simulateTouchEvent(MotionEvent.ACTION_DOWN, event); + simulateTouchEvent(MotionEvent.ACTION_UP, event); + } + + /** + * Simulating a touch event by injecting a synthesized touch event into {@link PointerTracker}. + * + * @param touchAction The action of the synthesizing touch event. + * @param hoverEvent The base hover event from that the touch event is synthesized. + */ + protected void simulateTouchEvent(final int touchAction, final MotionEvent hoverEvent) { + final MotionEvent touchEvent = synthesizeTouchEvent(touchAction, hoverEvent); + final int actionIndex = touchEvent.getActionIndex(); + final int pointerId = touchEvent.getPointerId(actionIndex); + final PointerTracker tracker = PointerTracker.getPointerTracker(pointerId); + tracker.processMotionEvent(touchEvent, mKeyDetector); + touchEvent.recycle(); + } + + /** + * Synthesize a touch event from a hover event. + * + * @param touchAction The action of the synthesizing touch event. + * @param hoverEvent The base hover event from that the touch event is synthesized. + * @return The synthesized touch event of <code>touchAction</code> that has pointer information + * of <code>event</code>. + */ + protected static MotionEvent synthesizeTouchEvent(final int touchAction, + final MotionEvent hoverEvent) { + final MotionEvent touchEvent = MotionEvent.obtain(hoverEvent); + touchEvent.setAction(touchAction); + return touchEvent; + } + + /** + * Handles a hover enter event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverEnterTo(final Key key) { + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnterTo: key=" + key); + } + key.onPressed(); + mKeyboardView.invalidateKey(key); + final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); + provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_ENTER); + provider.performActionForKey(key, AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS); + } + + /** + * Handles a hover move event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverMoveWithin(final Key key) { } + + /** + * Handles a hover exit event on a key. + * + * @param key The currently hovered key. + */ + protected void onHoverExitFrom(final Key key) { + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExitFrom: key=" + key); + } + key.onReleased(); + mKeyboardView.invalidateKey(key); + final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider(); + provider.sendAccessibilityEventForKey(key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT); + } +} diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java index ec1ab3565..18673a366 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java @@ -47,8 +47,8 @@ import java.util.List; * virtual views, thus conveying their logical structure. * </p> */ -public final class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat { - private static final String TAG = AccessibilityEntityProvider.class.getSimpleName(); +final class KeyboardAccessibilityNodeProvider extends AccessibilityNodeProviderCompat { + private static final String TAG = KeyboardAccessibilityNodeProvider.class.getSimpleName(); private static final int UNDEFINED = Integer.MIN_VALUE; private final KeyCodeDescriptionMapper mKeyCodeDescriptionMapper; @@ -64,23 +64,15 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider private int mAccessibilityFocusedView = UNDEFINED; /** The current keyboard view. */ - private KeyboardView mKeyboardView; + private final KeyboardView mKeyboardView; /** The current keyboard. */ private Keyboard mKeyboard; - public AccessibilityEntityProvider(final KeyboardView keyboardView) { + public KeyboardAccessibilityNodeProvider(final KeyboardView keyboardView) { + super(); mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance(); mAccessibilityUtils = AccessibilityUtils.getInstance(); - setView(keyboardView); - } - - /** - * Sets the keyboard view represented by this node provider. - * - * @param keyboardView The keyboard view to represent. - */ - public void setView(final KeyboardView keyboardView) { mKeyboardView = keyboardView; updateParentLocation(); @@ -142,7 +134,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider event.setClassName(key.getClass().getName()); event.setContentDescription(keyDescription); event.setEnabled(true); - final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event); + final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event); record.setSource(mKeyboardView, virtualViewId); return event; } @@ -237,7 +229,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider if (key == null) { return false; } - return performActionForKey(key, action, arguments); + return performActionForKey(key, action); } /** @@ -245,25 +237,16 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider * * @param key The on which to perform the action. * @param action The action to perform. - * @param arguments The action's arguments. * @return The result of performing the action, or false if the action is not supported. */ - boolean performActionForKey(final Key key, final int action, final Bundle arguments) { - final int virtualViewId = getVirtualViewIdOf(key); - + boolean performActionForKey(final Key key, final int action) { switch (action) { case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS: - if (mAccessibilityFocusedView == virtualViewId) { - return false; - } - mAccessibilityFocusedView = virtualViewId; + mAccessibilityFocusedView = getVirtualViewIdOf(key); sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUSED); return true; case AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS: - if (mAccessibilityFocusedView != virtualViewId) { - return false; - } mAccessibilityFocusedView = UNDEFINED; sendAccessibilityEventForKey( key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED); diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java new file mode 100644 index 000000000..96f84dde9 --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java @@ -0,0 +1,303 @@ +/* + * 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.accessibility; + +import android.content.Context; +import android.graphics.Rect; +import android.os.SystemClock; +import android.util.Log; +import android.util.SparseIntArray; +import android.view.MotionEvent; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +/** + * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance + * accessibility support via composition rather via inheritance. + */ +public final class MainKeyboardAccessibilityDelegate + extends KeyboardAccessibilityDelegate<MainKeyboardView> + implements AccessibilityLongPressTimer.LongPressTimerCallback { + private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName(); + + /** Map of keyboard modes to resource IDs. */ + private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray(); + + static { + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time); + KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url); + } + + /** The most recently set keyboard mode. */ + private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN; + private static final int KEYBOARD_IS_HIDDEN = -1; + // The rectangle region to ignore hover events. + private final Rect mBoundsToIgnoreHoverEvent = new Rect(); + + private final AccessibilityLongPressTimer mAccessibilityLongPressTimer; + + public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView, + final KeyDetector keyDetector) { + super(mainKeyboardView, keyDetector); + mAccessibilityLongPressTimer = new AccessibilityLongPressTimer( + this /* callback */, mainKeyboardView.getContext()); + } + + /** + * {@inheritDoc} + */ + @Override + public void setKeyboard(final Keyboard keyboard) { + if (keyboard == null) { + return; + } + final Keyboard lastKeyboard = getKeyboard(); + super.setKeyboard(keyboard); + final int lastKeyboardMode = mLastKeyboardMode; + mLastKeyboardMode = keyboard.mId.mMode; + + // Since this method is called even when accessibility is off, make sure + // to check the state before announcing anything. + if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + return; + } + // Announce the language name only when the language is changed. + if (lastKeyboard == null || !keyboard.mId.mSubtype.equals(lastKeyboard.mId.mSubtype)) { + announceKeyboardLanguage(keyboard); + return; + } + // Announce the mode only when the mode is changed. + if (keyboard.mId.mMode != lastKeyboardMode) { + announceKeyboardMode(keyboard); + return; + } + // Announce the keyboard type only when the type is changed. + if (keyboard.mId.mElementId != lastKeyboard.mId.mElementId) { + announceKeyboardType(keyboard, lastKeyboard); + return; + } + } + + /** + * Called when the keyboard is hidden and accessibility is enabled. + */ + public void onHideWindow() { + announceKeyboardHidden(); + mLastKeyboardMode = KEYBOARD_IS_HIDDEN; + } + + /** + * Announces which language of keyboard is being displayed. + * + * @param keyboard The new keyboard. + */ + private void announceKeyboardLanguage(final Keyboard keyboard) { + final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale( + keyboard.mId.mSubtype); + sendWindowStateChanged(languageText); + } + + /** + * Announces which type of keyboard is being displayed. + * If the keyboard type is unknown, no announcement is made. + * + * @param keyboard The new keyboard. + */ + private void announceKeyboardMode(final Keyboard keyboard) { + final Context context = mKeyboardView.getContext(); + final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(keyboard.mId.mMode); + if (modeTextResId == 0) { + return; + } + final String modeText = context.getString(modeTextResId); + final String text = context.getString(R.string.announce_keyboard_mode, modeText); + sendWindowStateChanged(text); + } + + /** + * Announces which type of keyboard is being displayed. + * + * @param keyboard The new keyboard. + * @param lastKeyboard The last keyboard. + */ + private void announceKeyboardType(final Keyboard keyboard, final Keyboard lastKeyboard) { + final int lastElementId = lastKeyboard.mId.mElementId; + final int resId; + switch (keyboard.mId.mElementId) { + case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: + case KeyboardId.ELEMENT_ALPHABET: + if (lastElementId == KeyboardId.ELEMENT_ALPHABET + || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { + // Transition between alphabet mode and automatic shifted mode should be silently + // ignored because it can be determined by each key's talk back announce. + return; + } + resId = R.string.spoken_description_mode_alpha; + break; + case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: + if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { + // Resetting automatic shifted mode by pressing the shift key causes the transition + // from automatic shifted to manual shifted that should be silently ignored. + return; + } + resId = R.string.spoken_description_shiftmode_on; + break; + case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: + if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) { + // Resetting caps locked mode by pressing the shift key causes the transition + // from shift locked to shift lock shifted that should be silently ignored. + return; + } + resId = R.string.spoken_description_shiftmode_locked; + break; + case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: + resId = R.string.spoken_description_shiftmode_locked; + break; + case KeyboardId.ELEMENT_SYMBOLS: + resId = R.string.spoken_description_mode_symbol; + break; + case KeyboardId.ELEMENT_SYMBOLS_SHIFTED: + resId = R.string.spoken_description_mode_symbol_shift; + break; + case KeyboardId.ELEMENT_PHONE: + resId = R.string.spoken_description_mode_phone; + break; + case KeyboardId.ELEMENT_PHONE_SYMBOLS: + resId = R.string.spoken_description_mode_phone_shift; + break; + default: + return; + } + sendWindowStateChanged(resId); + } + + /** + * Announces that the keyboard has been hidden. + */ + private void announceKeyboardHidden() { + sendWindowStateChanged(R.string.announce_keyboard_hidden); + } + + @Override + protected void onRegisterHoverKey(final Key key, final MotionEvent event) { + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + if (DEBUG_HOVER) { + Log.d(TAG, "onRegisterHoverKey: key=" + key + + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); + } + if (mBoundsToIgnoreHoverEvent.contains(x, y)) { + // This hover exit event points to the key that should be ignored. + // Clear the ignoring region to handle further hover events. + mBoundsToIgnoreHoverEvent.setEmpty(); + return; + } + super.onRegisterHoverKey(key, event); + } + + @Override + protected void onHoverEnterTo(final Key key) { + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnterTo: key=" + key + + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); + } + mAccessibilityLongPressTimer.cancelLongPress(); + if (mBoundsToIgnoreHoverEvent.contains(x, y)) { + return; + } + // This hover enter event points to the key that isn't in the ignoring region. + // Further hover events should be handled. + mBoundsToIgnoreHoverEvent.setEmpty(); + super.onHoverEnterTo(key); + if (key.isLongPressEnabled()) { + mAccessibilityLongPressTimer.startLongPress(key); + } + } + + @Override + protected void onHoverExitFrom(final Key key) { + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExitFrom: key=" + key + + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y)); + } + mAccessibilityLongPressTimer.cancelLongPress(); + super.onHoverExitFrom(key); + } + + @Override + public void onLongPressed(final Key key) { + if (DEBUG_HOVER) { + Log.d(TAG, "onLongPressed: key=" + key); + } + final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID); + final long eventTime = SystemClock.uptimeMillis(); + final int x = key.getHitBox().centerX(); + final int y = key.getHitBox().centerY(); + final MotionEvent downEvent = MotionEvent.obtain( + eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */); + // Inject a fake down event to {@link PointerTracker} to handle a long press correctly. + tracker.processMotionEvent(downEvent, mKeyDetector); + // The above fake down event triggers an unnecessary long press timer that should be + // canceled. + tracker.cancelLongPressTimer(); + downEvent.recycle(); + // Invoke {@link MainKeyboardView#onLongPress(PointerTracker)} as if a long press timeout + // has passed. + mKeyboardView.onLongPress(tracker); + // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout) + // or a key invokes IME switcher dialog, we should just ignore the next + // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether + // {@link PointerTracker} is in operation or not. + if (tracker.isInOperation()) { + // This long press shows a more keys keyboard and further hover events should be + // handled. + mBoundsToIgnoreHoverEvent.setEmpty(); + return; + } + // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}. + // We should ignore further hover events on this key. + mBoundsToIgnoreHoverEvent.set(key.getHitBox()); + if (key.hasNoPanelAutoMoreKey()) { + // This long press has registered a code point without showing a more keys keyboard. + // We should talk back the code point if possible. + final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys()[0].mCode; + final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint( + mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey); + if (text != null) { + sendWindowStateChanged(text); + } + } + } +} diff --git a/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java new file mode 100644 index 000000000..3a56c5d2a --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java @@ -0,0 +1,114 @@ +/* + * 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.accessibility; + +import android.graphics.Rect; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.MoreKeysKeyboardView; +import com.android.inputmethod.latin.Constants; + +/** + * This class represents a delegate that can be registered in {@link MoreKeysKeyboardView} to + * enhance accessibility support via composition rather via inheritance. + */ +public class MoreKeysKeyboardAccessibilityDelegate + extends KeyboardAccessibilityDelegate<MoreKeysKeyboardView> { + private static final String TAG = MoreKeysKeyboardAccessibilityDelegate.class.getSimpleName(); + + private final Rect mMoreKeysKeyboardValidBounds = new Rect(); + private static final int CLOSING_INSET_IN_PIXEL = 1; + private int mOpenAnnounceResId; + private int mCloseAnnounceResId; + + public MoreKeysKeyboardAccessibilityDelegate(final MoreKeysKeyboardView moreKeysKeyboardView, + final KeyDetector keyDetector) { + super(moreKeysKeyboardView, keyDetector); + } + + public void setOpenAnnounce(final int resId) { + mOpenAnnounceResId = resId; + } + + public void setCloseAnnounce(final int resId) { + mCloseAnnounceResId = resId; + } + + public void onShowMoreKeysKeyboard() { + sendWindowStateChanged(mOpenAnnounceResId); + } + + @Override + protected void onHoverEnter(final MotionEvent event) { + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverEnter: key=" + getHoverKeyOf(event)); + } + super.onHoverEnter(event); + final int actionIndex = event.getActionIndex(); + final int x = (int)event.getX(actionIndex); + final int y = (int)event.getY(actionIndex); + final int pointerId = event.getPointerId(actionIndex); + final long eventTime = event.getEventTime(); + mKeyboardView.onDownEvent(x, y, pointerId, eventTime); + } + + @Override + protected void onHoverMove(final MotionEvent event) { + super.onHoverMove(event); + final int actionIndex = event.getActionIndex(); + final int x = (int)event.getX(actionIndex); + final int y = (int)event.getY(actionIndex); + final int pointerId = event.getPointerId(actionIndex); + final long eventTime = event.getEventTime(); + mKeyboardView.onMoveEvent(x, y, pointerId, eventTime); + } + + @Override + protected void onHoverExit(final MotionEvent event) { + final Key lastKey = getLastHoverKey(); + if (DEBUG_HOVER) { + Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey); + } + if (lastKey != null) { + super.onHoverExitFrom(lastKey); + } + setLastHoverKey(null); + final int actionIndex = event.getActionIndex(); + final int x = (int)event.getX(actionIndex); + final int y = (int)event.getY(actionIndex); + final int pointerId = event.getPointerId(actionIndex); + final long eventTime = event.getEventTime(); + // A hover exit event at one pixel width or height area on the edges of more keys keyboard + // are treated as closing. + mMoreKeysKeyboardValidBounds.set(0, 0, mKeyboardView.getWidth(), mKeyboardView.getHeight()); + mMoreKeysKeyboardValidBounds.inset(CLOSING_INSET_IN_PIXEL, CLOSING_INSET_IN_PIXEL); + if (mMoreKeysKeyboardValidBounds.contains(x, y)) { + // Invoke {@link MoreKeysKeyboardView#onUpEvent(int,int,int,long)} as if this hover + // exit event selects a key. + mKeyboardView.onUpEvent(x, y, pointerId, eventTime); + mKeyboardView.dismissMoreKeysPanel(); + return; + } + // Close the more keys keyboard. + mKeyboardView.onMoveEvent( + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, pointerId, eventTime); + sendWindowStateChanged(mCloseAnnounceResId); + } +} diff --git a/java/src/com/android/inputmethod/accessibility/MoreSuggestionsAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MoreSuggestionsAccessibilityDelegate.java new file mode 100644 index 000000000..dfc866113 --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/MoreSuggestionsAccessibilityDelegate.java @@ -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. + */ + +package com.android.inputmethod.accessibility; + +import android.view.MotionEvent; + +import com.android.inputmethod.keyboard.KeyDetector; +import com.android.inputmethod.keyboard.MoreKeysKeyboardView; + +public final class MoreSuggestionsAccessibilityDelegate + extends MoreKeysKeyboardAccessibilityDelegate { + public MoreSuggestionsAccessibilityDelegate(final MoreKeysKeyboardView moreKeysKeyboardView, + final KeyDetector keyDetector) { + super(moreKeysKeyboardView, keyDetector); + } + + @Override + protected void simulateTouchEvent(final int touchAction, final MotionEvent hoverEvent) { + final MotionEvent touchEvent = synthesizeTouchEvent(touchAction, hoverEvent); + mKeyboardView.onTouchEvent(touchEvent); + touchEvent.recycle(); + } +} diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 60f7e2def..4d51821f2 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -27,7 +27,6 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.lang.reflect.Field; import java.util.ArrayList; @@ -73,13 +72,13 @@ public final class SuggestionSpanUtils { return pickedWord; } - final ArrayList<String> suggestionsList = CollectionUtils.newArrayList(); + final ArrayList<String> suggestionsList = new ArrayList<>(); for (int i = 0; i < suggestedWords.size(); ++i) { if (suggestionsList.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) { break; } final SuggestedWordInfo info = suggestedWords.getInfo(i); - if (info.mKind == SuggestedWordInfo.KIND_PREDICTION) { + if (info.isKindOf(SuggestedWordInfo.KIND_PREDICTION)) { continue; } final String word = suggestedWords.getWord(i); diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java index a0d76415c..6e32e74ab 100644 --- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java @@ -29,8 +29,8 @@ public final class UserDictionaryCompatUtils { Context.class, String.class, Integer.TYPE, String.class, Locale.class); @SuppressWarnings("deprecation") - public static void addWord(final Context context, final String word, final int freq, - final String shortcut, final Locale locale) { + public static void addWord(final Context context, final String word, + final int freq, final String shortcut, final Locale locale) { if (hasNewerAddWord()) { CompatUtils.invoke(Words.class, null, METHOD_addWord, context, word, freq, shortcut, locale); diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java index dec739d39..767cc423d 100644 --- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java @@ -24,23 +24,13 @@ import java.lang.reflect.Method; // Currently {@link #getPaddingEnd(View)} and {@link #setPaddingRelative(View,int,int,int,int)} // are missing from android-support-v4 static library in KitKat SDK. public final class ViewCompatUtils { - // Note that View.LAYOUT_DIRECTION_LTR and View.LAYOUT_DIRECTION_RTL have been introduced in - // API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1). - public static final int LAYOUT_DIRECTION_LTR = (Integer)CompatUtils.getFieldValue(null, 0x0, - CompatUtils.getField(View.class, "LAYOUT_DIRECTION_LTR")); - public static final int LAYOUT_DIRECTION_RTL = (Integer)CompatUtils.getFieldValue(null, 0x1, - CompatUtils.getField(View.class, "LAYOUT_DIRECTION_RTL")); - - // Note that View.getPaddingEnd(), View.setPaddingRelative(int,int,int,int), and - // View.getLayoutDirection() have been introduced in API level 17 - // (Build.VERSION_CODE.JELLY_BEAN_MR1). + // Note that View.getPaddingEnd(), View.setPaddingRelative(int,int,int,int) have been + // introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1). private static final Method METHOD_getPaddingEnd = CompatUtils.getMethod( View.class, "getPaddingEnd"); private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod( View.class, "setPaddingRelative", Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); - private static final Method METHOD_getLayoutDirection = CompatUtils.getMethod( - View.class, "getLayoutDirection"); private ViewCompatUtils() { // This utility class is not publicly instantiable. @@ -61,11 +51,4 @@ public final class ViewCompatUtils { } CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom); } - - public static int getLayoutDirection(final View view) { - if (METHOD_getLayoutDirection == null) { - return LAYOUT_DIRECTION_LTR; - } - return (Integer)CompatUtils.invoke(view, 0, METHOD_getLayoutDirection); - } } diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java index 706bdea8e..3d294acd7 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java @@ -16,7 +16,6 @@ package com.android.inputmethod.dictionarypack; -import android.app.DownloadManager; import android.app.DownloadManager.Request; import android.content.ContentValues; import android.content.Context; @@ -325,8 +324,9 @@ public final class ActionBatch { MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_AVAILABLE, mWordList.mId, mWordList.mLocale, mWordList.mDescription, null == mWordList.mLocalFilename ? "" : mWordList.mLocalFilename, - mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum, - mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); + mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum, + mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, + mWordList.mFormatVersion); PrivateLog.log("Insert 'available' record for " + mWordList.mDescription + " and locale " + mWordList.mLocale); db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values); @@ -374,7 +374,7 @@ public final class ActionBatch { final ContentValues values = MetadataDbHelper.makeContentValues(0, MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED, mWordList.mId, mWordList.mLocale, mWordList.mDescription, - "", mWordList.mRemoteFilename, mWordList.mLastUpdate, + "", mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum, mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription @@ -416,8 +416,9 @@ public final class ActionBatch { oldValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN), mWordList.mId, mWordList.mLocale, mWordList.mDescription, oldValues.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN), - mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mChecksum, - mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); + mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum, + mWordList.mChecksum, mWordList.mFileSize, mWordList.mVersion, + mWordList.mFormatVersion); PrivateLog.log("Updating record for " + mWordList.mDescription + " and locale " + mWordList.mLocale); db.update(MetadataDbHelper.METADATA_TABLE_NAME, values, @@ -598,7 +599,7 @@ public final class ActionBatch { private final Queue<Action> mActions; public ActionBatch() { - mActions = new LinkedList<Action>(); + mActions = new LinkedList<>(); } public void add(final Action a) { diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java index 2623eff56..1d84e5888 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java @@ -29,7 +29,6 @@ import android.view.View; import android.widget.ProgressBar; public class DictionaryDownloadProgressBar extends ProgressBar { - @SuppressWarnings("unused") private static final String TAG = DictionaryDownloadProgressBar.class.getSimpleName(); private static final int NOT_A_DOWNLOADMANAGER_PENDING_ID = 0; @@ -119,7 +118,6 @@ public class DictionaryDownloadProgressBar extends ProgressBar { try { final UpdateHelper updateHelper = new UpdateHelper(); final Query query = new Query().setFilterById(mId); - int lastProgress = 0; setIndeterminate(true); while (!isInterrupted()) { final Cursor cursor = mDownloadManagerWrapper.query(query); diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java index 13c07de35..8e026171d 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java @@ -18,8 +18,6 @@ package com.android.inputmethod.dictionarypack; import android.view.View; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.ArrayList; import java.util.HashMap; @@ -39,8 +37,8 @@ public class DictionaryListInterfaceState { public int mStatus = MetadataDbHelper.STATUS_UNKNOWN; } - private HashMap<String, State> mWordlistToState = CollectionUtils.newHashMap(); - private ArrayList<View> mViewCache = CollectionUtils.newArrayList(); + private HashMap<String, State> mWordlistToState = new HashMap<>(); + private ArrayList<View> mViewCache = new ArrayList<>(); public boolean isOpen(final String wordlistId) { final State state = mWordlistToState.get(wordlistId); diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java index 80def701d..f5bd84c8c 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java @@ -89,10 +89,13 @@ public final class DictionaryProvider extends ContentProvider { private static final class WordListInfo { public final String mId; public final String mLocale; + public final String mRawChecksum; public final int mMatchLevel; - public WordListInfo(final String id, final String locale, final int matchLevel) { + public WordListInfo(final String id, final String locale, final String rawChecksum, + final int matchLevel) { mId = id; mLocale = locale; + mRawChecksum = rawChecksum; mMatchLevel = matchLevel; } } @@ -106,7 +109,8 @@ public final class DictionaryProvider extends ContentProvider { private static final class ResourcePathCursor extends AbstractCursor { // Column names for the cursor returned by this content provider. - static private final String[] columnNames = { "id", "locale" }; + static private final String[] columnNames = { MetadataDbHelper.WORDLISTID_COLUMN, + MetadataDbHelper.LOCALE_COLUMN, MetadataDbHelper.RAW_CHECKSUM_COLUMN }; // The list of word lists served by this provider that match the client request. final WordListInfo[] mWordLists; @@ -141,6 +145,7 @@ public final class DictionaryProvider extends ContentProvider { switch (column) { case 0: return mWordLists[mPos].mId; case 1: return mWordLists[mPos].mLocale; + case 2: return mWordLists[mPos].mRawChecksum; default : return null; } } @@ -352,11 +357,13 @@ public final class DictionaryProvider extends ContentProvider { return Collections.<WordListInfo>emptyList(); } try { - final HashMap<String, WordListInfo> dicts = new HashMap<String, WordListInfo>(); + final HashMap<String, WordListInfo> dicts = new HashMap<>(); final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); final int localFileNameIndex = results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN); + final int rawChecksumIndex = + results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN); final int statusIndex = results.getColumnIndex(MetadataDbHelper.STATUS_COLUMN); if (results.moveToFirst()) { do { @@ -379,6 +386,7 @@ public final class DictionaryProvider extends ContentProvider { } final String wordListLocale = results.getString(localeIndex); final String wordListLocalFilename = results.getString(localFileNameIndex); + final String wordListRawChecksum = results.getString(rawChecksumIndex); final int wordListStatus = results.getInt(statusIndex); // Test the requested locale against this wordlist locale. The requested locale // has to either match exactly or be more specific than the dictionary - a @@ -412,8 +420,8 @@ public final class DictionaryProvider extends ContentProvider { final WordListInfo currentBestMatch = dicts.get(wordListCategory); if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) { - dicts.put(wordListCategory, - new WordListInfo(wordListId, wordListLocale, matchLevel)); + dicts.put(wordListCategory, new WordListInfo(wordListId, wordListLocale, + wordListRawChecksum, matchLevel)); } } while (results.moveToNext()); } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index dae2f22a4..11982fa65 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -33,13 +33,13 @@ import android.preference.PreferenceGroup; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; -import android.view.animation.AnimationUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AnimationUtils; import com.android.inputmethod.latin.R; @@ -67,8 +67,8 @@ public final class DictionarySettingsFragment extends PreferenceFragment private boolean mChangedSettings; private DictionaryListInterfaceState mDictionaryListInterfaceState = new DictionaryListInterfaceState(); - private TreeMap<String, WordListPreference> mCurrentPreferenceMap = - new TreeMap<String, WordListPreference>(); // never null + // never null + private TreeMap<String, WordListPreference> mCurrentPreferenceMap = new TreeMap<>(); private final BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() { @Override @@ -280,19 +280,18 @@ public final class DictionarySettingsFragment extends PreferenceFragment : activity.getContentResolver().query(contentUri, null, null, null, null); if (null == cursor) { - final ArrayList<Preference> result = new ArrayList<Preference>(); + final ArrayList<Preference> result = new ArrayList<>(); result.add(createErrorMessage(activity, R.string.cannot_connect_to_dict_service)); return result; } try { if (!cursor.moveToFirst()) { - final ArrayList<Preference> result = new ArrayList<Preference>(); + final ArrayList<Preference> result = new ArrayList<>(); result.add(createErrorMessage(activity, R.string.no_dictionaries_available)); return result; } else { final String systemLocaleString = Locale.getDefault().toString(); - final TreeMap<String, WordListPreference> prefMap = - new TreeMap<String, WordListPreference>(); + final TreeMap<String, WordListPreference> prefMap = new TreeMap<>(); final int idIndex = cursor.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); final int versionIndex = cursor.getColumnIndex(MetadataDbHelper.VERSION_COLUMN); final int localeIndex = cursor.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); diff --git a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java b/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java index 77f67b8a3..4f0805c5c 100644 --- a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java +++ b/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java @@ -175,7 +175,7 @@ public final class LocaleUtils { return saveLocale; } - private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); /** * Creates a locale from a string specification. diff --git a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java b/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java index e47e86e4b..ccd054c84 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java +++ b/java/src/com/android/inputmethod/dictionarypack/MD5Calculator.java @@ -20,7 +20,7 @@ import java.io.InputStream; import java.io.IOException; import java.security.MessageDigest; -final class MD5Calculator { +public final class MD5Calculator { private MD5Calculator() {} // This helper class is not instantiable public static String checksum(final InputStream in) throws IOException { diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index 4a8fa51ee..17dd781d5 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.text.TextUtils; import android.util.Log; @@ -46,7 +47,7 @@ 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 = 7; + private static final int CURRENT_METADATA_DATABASE_VERSION = 9; private final static long NOT_A_DOWNLOAD_ID = -1; @@ -66,7 +67,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { public static final String VERSION_COLUMN = "version"; public static final String FORMATVERSION_COLUMN = "formatversion"; public static final String FLAGS_COLUMN = "flags"; - public static final int COLUMN_COUNT = 13; + public static final String RAW_CHECKSUM_COLUMN = "rawChecksum"; + public static final int COLUMN_COUNT = 14; private static final String CLIENT_CLIENT_ID_COLUMN = "clientid"; private static final String CLIENT_METADATA_URI_COLUMN = "uri"; @@ -119,8 +121,9 @@ public class MetadataDbHelper extends SQLiteOpenHelper { + CHECKSUM_COLUMN + " TEXT, " + FILESIZE_COLUMN + " INTEGER, " + VERSION_COLUMN + " INTEGER," - + FORMATVERSION_COLUMN + " INTEGER," - + FLAGS_COLUMN + " INTEGER," + + FORMATVERSION_COLUMN + " INTEGER, " + + FLAGS_COLUMN + " INTEGER, " + + RAW_CHECKSUM_COLUMN + " TEXT," + "PRIMARY KEY (" + WORDLISTID_COLUMN + "," + VERSION_COLUMN + "));"; private static final String METADATA_CREATE_CLIENT_TABLE = "CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " (" @@ -136,7 +139,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { static final String[] METADATA_TABLE_COLUMNS = { PENDINGID_COLUMN, TYPE_COLUMN, STATUS_COLUMN, WORDLISTID_COLUMN, LOCALE_COLUMN, DESCRIPTION_COLUMN, LOCAL_FILENAME_COLUMN, REMOTE_FILENAME_COLUMN, DATE_COLUMN, CHECKSUM_COLUMN, - FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN }; + FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN, + RAW_CHECKSUM_COLUMN }; // List of all client table columns. static final String[] CLIENT_TABLE_COLUMNS = { CLIENT_CLIENT_ID_COLUMN, CLIENT_METADATA_URI_COLUMN, CLIENT_PENDINGID_COLUMN, FLAGS_COLUMN }; @@ -156,7 +160,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { // this legacy database. New clients should make sure to always pass a client ID so as // to avoid conflicts. final String clientId = null != clientIdOrNull ? clientIdOrNull : ""; - if (null == sInstanceMap) sInstanceMap = new TreeMap<String, MetadataDbHelper>(); + if (null == sInstanceMap) sInstanceMap = new TreeMap<>(); MetadataDbHelper helper = sInstanceMap.get(clientId); if (null == helper) { helper = new MetadataDbHelper(context, clientId); @@ -215,6 +219,17 @@ public class MetadataDbHelper extends SQLiteOpenHelper { createClientTable(db); } + private void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db, final String clientId) { + try { + db.execSQL("SELECT " + RAW_CHECKSUM_COLUMN + " FROM " + + METADATA_TABLE_NAME + " LIMIT 0;"); + } catch (SQLiteException e) { + Log.i(TAG, "No " + RAW_CHECKSUM_COLUMN + " column : creating it"); + db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN " + + RAW_CHECKSUM_COLUMN + " TEXT;"); + } + } + /** * Upgrade the database. Upgrade from version 3 is supported. * Version 3 has a DB named METADATA_DATABASE_NAME_STEM containing a table METADATA_TABLE_NAME. @@ -260,6 +275,12 @@ public class MetadataDbHelper extends SQLiteOpenHelper { db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); onCreate(db); } + // A rawChecksum column that did not exist in the previous versions was added that + // corresponds to the md5 checksum of the file after decompression/decryption. This is to + // strengthen the system against corrupted dictionary files. + // The most secure way to upgrade a database is to just test for the column presence, and + // add it if it's not there. + addRawChecksumColumnUnlessPresent(db, mClientId); } /** @@ -431,7 +452,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { public static ContentValues makeContentValues(final int pendingId, final int type, final int status, final String wordlistId, final String locale, final String description, final String filename, final String url, final long date, - final String checksum, final long filesize, final int version, + final String rawChecksum, final String checksum, final long filesize, final int version, final int formatVersion) { final ContentValues result = new ContentValues(COLUMN_COUNT); result.put(PENDINGID_COLUMN, pendingId); @@ -443,6 +464,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { result.put(LOCAL_FILENAME_COLUMN, filename); result.put(REMOTE_FILENAME_COLUMN, url); result.put(DATE_COLUMN, date); + result.put(RAW_CHECKSUM_COLUMN, rawChecksum); result.put(CHECKSUM_COLUMN, checksum); result.put(FILESIZE_COLUMN, filesize); result.put(VERSION_COLUMN, version); @@ -478,6 +500,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { if (null == result.get(REMOTE_FILENAME_COLUMN)) result.put(REMOTE_FILENAME_COLUMN, ""); // 0 for the update date : 1970/1/1. Unless specified. if (null == result.get(DATE_COLUMN)) result.put(DATE_COLUMN, 0); + // Raw checksum unknown unless specified + if (null == result.get(RAW_CHECKSUM_COLUMN)) result.put(RAW_CHECKSUM_COLUMN, ""); // Checksum unknown unless specified if (null == result.get(CHECKSUM_COLUMN)) result.put(CHECKSUM_COLUMN, ""); // No filesize unless specified @@ -525,6 +549,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { putStringResult(result, cursor, LOCAL_FILENAME_COLUMN); putStringResult(result, cursor, REMOTE_FILENAME_COLUMN); putIntResult(result, cursor, DATE_COLUMN); + putStringResult(result, cursor, RAW_CHECKSUM_COLUMN); putStringResult(result, cursor, CHECKSUM_COLUMN); putIntResult(result, cursor, FILESIZE_COLUMN); putIntResult(result, cursor, VERSION_COLUMN); @@ -614,7 +639,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { public static ArrayList<DownloadRecord> getDownloadRecordsForDownloadId(final Context context, final long downloadId) { final SQLiteDatabase defaultDb = getDb(context, ""); - final ArrayList<DownloadRecord> results = new ArrayList<DownloadRecord>(); + final ArrayList<DownloadRecord> results = new ArrayList<>(); final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, CLIENT_TABLE_COLUMNS, null, null, null, null, null); try { @@ -898,7 +923,7 @@ public class MetadataDbHelper extends SQLiteOpenHelper { // - Remove the old entry from the table // - Erase the old file // We start by gathering the names of the files we should delete. - final List<String> filenames = new LinkedList<String>(); + final List<String> filenames = new LinkedList<>(); final Cursor c = db.query(METADATA_TABLE_NAME, new String[] { LOCAL_FILENAME_COLUMN }, LOCALE_COLUMN + " = ? AND " + diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java index 5c2289911..d66b69050 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataHandler.java @@ -43,7 +43,7 @@ public class MetadataHandler { * @return the constructed list of wordlist metadata. */ private static List<WordListMetadata> makeMetadataObject(final Cursor results) { - final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<WordListMetadata>(); + final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<>(); if (null != results && results.moveToFirst()) { final int localeColumn = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); final int typeColumn = results.getColumnIndex(MetadataDbHelper.TYPE_COLUMN); @@ -52,6 +52,8 @@ public class MetadataHandler { final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); final int updateIndex = results.getColumnIndex(MetadataDbHelper.DATE_COLUMN); final int fileSizeIndex = results.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN); + final int rawChecksumIndex = + results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN); final int checksumIndex = results.getColumnIndex(MetadataDbHelper.CHECKSUM_COLUMN); final int localFilenameIndex = results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN); @@ -66,6 +68,7 @@ public class MetadataHandler { results.getString(descriptionColumn), results.getLong(updateIndex), results.getLong(fileSizeIndex), + results.getString(rawChecksumIndex), results.getString(checksumIndex), results.getString(localFilenameIndex), results.getString(remoteFilenameIndex), diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java b/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java index 27670fddf..52290cadc 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataParser.java @@ -37,6 +37,7 @@ public class MetadataParser { private static final String DESCRIPTION_FIELD_NAME = MetadataDbHelper.DESCRIPTION_COLUMN; private static final String UPDATE_FIELD_NAME = "update"; private static final String FILESIZE_FIELD_NAME = MetadataDbHelper.FILESIZE_COLUMN; + private static final String RAW_CHECKSUM_FIELD_NAME = MetadataDbHelper.RAW_CHECKSUM_COLUMN; private static final String CHECKSUM_FIELD_NAME = MetadataDbHelper.CHECKSUM_COLUMN; private static final String REMOTE_FILENAME_FIELD_NAME = MetadataDbHelper.REMOTE_FILENAME_COLUMN; @@ -51,7 +52,7 @@ public class MetadataParser { */ private static WordListMetadata parseOneWordList(final JsonReader reader) throws IOException, BadFormatException { - final TreeMap<String, String> arguments = new TreeMap<String, String>(); + final TreeMap<String, String> arguments = new TreeMap<>(); reader.beginObject(); while (reader.hasNext()) { final String name = reader.nextName(); @@ -80,6 +81,7 @@ public class MetadataParser { arguments.get(DESCRIPTION_FIELD_NAME), Long.parseLong(arguments.get(UPDATE_FIELD_NAME)), Long.parseLong(arguments.get(FILESIZE_FIELD_NAME)), + arguments.get(RAW_CHECKSUM_FIELD_NAME), arguments.get(CHECKSUM_FIELD_NAME), null, arguments.get(REMOTE_FILENAME_FIELD_NAME), @@ -98,7 +100,7 @@ public class MetadataParser { public static List<WordListMetadata> parseMetadata(final InputStreamReader input) throws IOException, BadFormatException { JsonReader reader = new JsonReader(input); - final ArrayList<WordListMetadata> readInfo = new ArrayList<WordListMetadata>(); + final ArrayList<WordListMetadata> readInfo = new ArrayList<>(); reader.beginArray(); while (reader.hasNext()) { final WordListMetadata thisMetadata = parseOneWordList(reader); diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index dcff490db..95a094232 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -177,7 +177,7 @@ public final class UpdateHandler { */ public static boolean tryUpdate(final Context context, final boolean updateNow) { // TODO: loop through all clients instead of only doing the default one. - final TreeSet<String> uris = new TreeSet<String>(); + final TreeSet<String> uris = new TreeSet<>(); final Cursor cursor = MetadataDbHelper.queryClientIds(context); if (null == cursor) return false; try { @@ -557,7 +557,7 @@ public final class UpdateHandler { // Instantiation of a parameterized type is not possible in Java, so it's not possible to // return the same type of list that was passed - probably the same reason why Collections // does not do it. So we need to decide statically which concrete type to return. - return new LinkedList<T>(src); + return new LinkedList<>(src); } /** @@ -740,10 +740,10 @@ public final class UpdateHandler { final ActionBatch actions = new ActionBatch(); // Upgrade existing word lists DebugLogUtils.l("Comparing dictionaries"); - final Set<String> wordListIds = new TreeSet<String>(); + final Set<String> wordListIds = new TreeSet<>(); // TODO: Can these be null? - if (null == from) from = new ArrayList<WordListMetadata>(); - if (null == to) to = new ArrayList<WordListMetadata>(); + if (null == from) from = new ArrayList<>(); + if (null == to) to = new ArrayList<>(); for (WordListMetadata wlData : from) wordListIds.add(wlData.mId); for (WordListMetadata wlData : to) wordListIds.add(wlData.mId); for (String id : wordListIds) { diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java b/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java index 69bff9597..9e510a68b 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListMetadata.java @@ -30,6 +30,7 @@ public class WordListMetadata { public final String mDescription; public final long mLastUpdate; public final long mFileSize; + public final String mRawChecksum; public final String mChecksum; public final String mLocalFilename; public final String mRemoteFilename; @@ -50,13 +51,15 @@ public class WordListMetadata { public WordListMetadata(final String id, final int type, final String description, final long lastUpdate, final long fileSize, - final String checksum, final String localFilename, final String remoteFilename, - final int version, final int formatVersion, final int flags, final String locale) { + final String rawChecksum, final String checksum, final String localFilename, + final String remoteFilename, final int version, final int formatVersion, + final int flags, final String locale) { mId = id; mType = type; mDescription = description; mLastUpdate = lastUpdate; // In milliseconds mFileSize = fileSize; + mRawChecksum = rawChecksum; mChecksum = checksum; mLocalFilename = localFilename; mRemoteFilename = remoteFilename; @@ -77,6 +80,7 @@ public class WordListMetadata { final String description = values.getAsString(MetadataDbHelper.DESCRIPTION_COLUMN); final Long lastUpdate = values.getAsLong(MetadataDbHelper.DATE_COLUMN); final Long fileSize = values.getAsLong(MetadataDbHelper.FILESIZE_COLUMN); + final String rawChecksum = values.getAsString(MetadataDbHelper.RAW_CHECKSUM_COLUMN); final String checksum = values.getAsString(MetadataDbHelper.CHECKSUM_COLUMN); final String localFilename = values.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); final String remoteFilename = values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN); @@ -98,8 +102,8 @@ public class WordListMetadata { || null == locale) { throw new IllegalArgumentException(); } - return new WordListMetadata(id, type, description, lastUpdate, fileSize, checksum, - localFilename, remoteFilename, version, formatVersion, flags, locale); + return new WordListMetadata(id, type, description, lastUpdate, fileSize, rawChecksum, + checksum, localFilename, remoteFilename, version, formatVersion, flags, locale); } @Override @@ -110,6 +114,7 @@ public class WordListMetadata { sb.append("\nDescription : ").append(mDescription); sb.append("\nLastUpdate : ").append(mLastUpdate); sb.append("\nFileSize : ").append(mFileSize); + sb.append("\nRawChecksum : ").append(mRawChecksum); sb.append("\nChecksum : ").append(mChecksum); sb.append("\nLocalFilename : ").append(mLocalFilename); sb.append("\nRemoteFilename : ").append(mRemoteFilename); diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java index 8b59dc52a..61bc11b39 100644 --- a/java/src/com/android/inputmethod/event/CombinerChain.java +++ b/java/src/com/android/inputmethod/event/CombinerChain.java @@ -20,9 +20,9 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; +import java.util.HashMap; /** * This class implements the logic chain between receiving events and generating code points. @@ -43,20 +43,32 @@ public class CombinerChain { private SpannableStringBuilder mStateFeedback; private final ArrayList<Combiner> mCombiners; + private static final HashMap<String, Class<? extends Combiner>> IMPLEMENTED_COMBINERS = + new HashMap<>(); + static { + IMPLEMENTED_COMBINERS.put("MyanmarReordering", MyanmarReordering.class); + } + private static final String COMBINER_SPEC_SEPARATOR = ";"; + /** * Create an combiner chain. * * The combiner chain takes events as inputs and outputs code points and combining state. * For example, if the input language is Japanese, the combining chain will typically perform - * kana conversion. + * kana conversion. This takes a string for initial text, taken to be present before the + * cursor: we'll start after this. * + * @param initialText The text that has already been combined so far. * @param combinerList A list of combiners to be applied in order. */ - public CombinerChain(final Combiner... combinerList) { - mCombiners = CollectionUtils.newArrayList(); + public CombinerChain(final String initialText, final Combiner... combinerList) { + mCombiners = new ArrayList<>(); // The dead key combiner is always active, and always first mCombiners.add(new DeadKeyCombiner()); - mCombinedText = new StringBuilder(); + for (final Combiner combiner : combinerList) { + mCombiners.add(combiner); + } + mCombinedText = new StringBuilder(initialText); mStateFeedback = new SpannableStringBuilder(); } @@ -74,7 +86,7 @@ public class CombinerChain { * @param newEvent the new event to process */ public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { - final ArrayList<Event> modifiablePreviousEvents = new ArrayList<Event>(previousEvents); + final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents); Event event = newEvent; for (final Combiner combiner : mCombiners) { // A combiner can never return more than one event; it can return several @@ -114,4 +126,30 @@ public class CombinerChain { final SpannableStringBuilder s = new SpannableStringBuilder(mCombinedText); return s.append(mStateFeedback); } + + public static Combiner[] createCombiners(final String spec) { + if (TextUtils.isEmpty(spec)) { + return new Combiner[0]; + } + final String[] combinerDescriptors = spec.split(COMBINER_SPEC_SEPARATOR); + final Combiner[] combiners = new Combiner[combinerDescriptors.length]; + int i = 0; + for (final String combinerDescriptor : combinerDescriptors) { + final Class<? extends Combiner> combinerClass = + IMPLEMENTED_COMBINERS.get(combinerDescriptor); + if (null == combinerClass) { + throw new RuntimeException("Unknown combiner descriptor: " + combinerDescriptor); + } + try { + combiners[i++] = combinerClass.newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor, + e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Unable to instantiate combiner: " + combinerDescriptor, + e); + } + } + return combiners; + } } diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java new file mode 100644 index 000000000..32919932d --- /dev/null +++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java @@ -0,0 +1,258 @@ +/* + * 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 com.android.inputmethod.latin.Constants; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A combiner that reorders input for Myanmar. + */ +public class MyanmarReordering implements Combiner { + // U+1031 MYANMAR VOWEL SIGN E + private final static int VOWEL_E = 0x1031; // Code point for vowel E that we need to reorder + // U+200C ZERO WIDTH NON-JOINER + // U+200B ZERO WIDTH SPACE + private final static int ZERO_WIDTH_NON_JOINER = 0x200B; // should be 0x200C + + private final ArrayList<Event> mCurrentEvents = new ArrayList<>(); + + // List of consonants : + // U+1000 MYANMAR LETTER KA + // U+1001 MYANMAR LETTER KHA + // U+1002 MYANMAR LETTER GA + // U+1003 MYANMAR LETTER GHA + // U+1004 MYANMAR LETTER NGA + // U+1005 MYANMAR LETTER CA + // U+1006 MYANMAR LETTER CHA + // U+1007 MYANMAR LETTER JA + // U+1008 MYANMAR LETTER JHA + // U+1009 MYANMAR LETTER NYA + // U+100A MYANMAR LETTER NNYA + // U+100B MYANMAR LETTER TTA + // U+100C MYANMAR LETTER TTHA + // U+100D MYANMAR LETTER DDA + // U+100E MYANMAR LETTER DDHA + // U+100F MYANMAR LETTER NNA + // U+1010 MYANMAR LETTER TA + // U+1011 MYANMAR LETTER THA + // U+1012 MYANMAR LETTER DA + // U+1013 MYANMAR LETTER DHA + // U+1014 MYANMAR LETTER NA + // U+1015 MYANMAR LETTER PA + // U+1016 MYANMAR LETTER PHA + // U+1017 MYANMAR LETTER BA + // U+1018 MYANMAR LETTER BHA + // U+1019 MYANMAR LETTER MA + // U+101A MYANMAR LETTER YA + // U+101B MYANMAR LETTER RA + // U+101C MYANMAR LETTER LA + // U+101D MYANMAR LETTER WA + // U+101E MYANMAR LETTER SA + // U+101F MYANMAR LETTER HA + // U+1020 MYANMAR LETTER LLA + // U+103F MYANMAR LETTER GREAT SA + private static boolean isConsonant(final int codePoint) { + return (codePoint >= 0x1000 && codePoint <= 0x1020) || 0x103F == codePoint; + } + + // List of medials : + // U+103B MYANMAR CONSONANT SIGN MEDIAL YA + // U+103C MYANMAR CONSONANT SIGN MEDIAL RA + // U+103D MYANMAR CONSONANT SIGN MEDIAL WA + // U+103E MYANMAR CONSONANT SIGN MEDIAL HA + // U+105E MYANMAR CONSONANT SIGN MON MEDIAL NA + // U+105F MYANMAR CONSONANT SIGN MON MEDIAL MA + // U+1060 MYANMAR CONSONANT SIGN MON MEDIAL LA + // U+1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA + private static int[] MEDIAL_LIST = { 0x103B, 0x103C, 0x103D, 0x103E, + 0x105E, 0x105F, 0x1060, 0x1082}; + private static boolean isMedial(final int codePoint) { + return Arrays.binarySearch(MEDIAL_LIST, codePoint) >= 0; + } + + private static boolean isConsonantOrMedial(final int codePoint) { + return isConsonant(codePoint) || isMedial(codePoint); + } + + private Event getLastEvent() { + final int size = mCurrentEvents.size(); + if (size <= 0) { + return null; + } + return mCurrentEvents.get(size - 1); + } + + private CharSequence getCharSequence() { + final StringBuilder s = new StringBuilder(); + for (final Event e : mCurrentEvents) { + s.appendCodePoint(e.mCodePoint); + } + return s; + } + + /** + * Clears the currently combining stream of events and returns the resulting software text + * event corresponding to the stream. Optionally adds a new event to the cleared stream. + * @param newEvent the new event to add to the stream. null if none. + * @return the resulting software text event. Null if none. + */ + private Event clearAndGetResultingEvent(final Event newEvent) { + final CharSequence combinedText; + if (mCurrentEvents.size() > 0) { + combinedText = getCharSequence(); + mCurrentEvents.clear(); + } else { + combinedText = null; + } + if (null != newEvent) { + mCurrentEvents.add(newEvent); + } + return null == combinedText ? null + : Event.createSoftwareTextEvent(combinedText, Event.NOT_A_KEY_CODE); + } + + @Override + public Event processEvent(ArrayList<Event> previousEvents, Event newEvent) { + final int codePoint = newEvent.mCodePoint; + if (VOWEL_E == codePoint) { + final Event lastEvent = getLastEvent(); + if (null == lastEvent) { + mCurrentEvents.add(newEvent); + return null; + } else if (isConsonantOrMedial(lastEvent.mCodePoint)) { + final Event resultingEvent = clearAndGetResultingEvent(null); + mCurrentEvents.add(Event.createSoftwareKeypressEvent(ZERO_WIDTH_NON_JOINER, + Event.NOT_A_KEY_CODE, + Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, + false /* isKeyRepeat */)); + mCurrentEvents.add(newEvent); + return resultingEvent; + } else { // VOWEL_E == lastCodePoint. But if that was anything else this is correct too. + return clearAndGetResultingEvent(newEvent); + } + } if (isConsonant(codePoint)) { + final Event lastEvent = getLastEvent(); + if (null == lastEvent) { + mCurrentEvents.add(newEvent); + return null; + } else if (VOWEL_E == lastEvent.mCodePoint) { + final int eventSize = mCurrentEvents.size(); + if (eventSize >= 2 + && mCurrentEvents.get(eventSize - 2).mCodePoint == ZERO_WIDTH_NON_JOINER) { + // We have a ZWJN before a vowel E. We need to remove the ZWNJ and then + // reorder the vowel with respect to the consonant. + mCurrentEvents.remove(eventSize - 1); + mCurrentEvents.remove(eventSize - 2); + mCurrentEvents.add(newEvent); + mCurrentEvents.add(lastEvent); + return null; + } + // If there is already a consonant, then we are starting a new syllable. + for (int i = eventSize - 2; i >= 0; --i) { + if (isConsonant(mCurrentEvents.get(i).mCodePoint)) { + return clearAndGetResultingEvent(newEvent); + } + } + // If we come here, we didn't have a consonant so we reorder + mCurrentEvents.remove(eventSize - 1); + mCurrentEvents.add(newEvent); + mCurrentEvents.add(lastEvent); + return null; + } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine + return clearAndGetResultingEvent(newEvent); + } + } else if (isMedial(codePoint)) { + final Event lastEvent = getLastEvent(); + if (null == lastEvent) { + mCurrentEvents.add(newEvent); + return null; + } else if (VOWEL_E == lastEvent.mCodePoint) { + final int eventSize = mCurrentEvents.size(); + // If there is already a consonant, then we are in the middle of a syllable, and we + // need to reorder. + boolean hasConsonant = false; + for (int i = eventSize - 2; i >= 0; --i) { + if (isConsonant(mCurrentEvents.get(i).mCodePoint)) { + hasConsonant = true; + break; + } + } + if (hasConsonant) { + mCurrentEvents.remove(eventSize - 1); + mCurrentEvents.add(newEvent); + mCurrentEvents.add(lastEvent); + return null; + } + // Otherwise, we just commit everything. + return clearAndGetResultingEvent(null); + } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine + return clearAndGetResultingEvent(newEvent); + } + } else if (Constants.CODE_DELETE == newEvent.mKeyCode) { + final Event lastEvent = getLastEvent(); + final int eventSize = mCurrentEvents.size(); + if (null != lastEvent) { + if (VOWEL_E == lastEvent.mCodePoint) { + // We have a VOWEL_E at the end. There are four cases. + // - The vowel is the only code point in the buffer. Remove it. + // - The vowel is preceded by a ZWNJ. Remove both vowel E and ZWNJ. + // - The vowel is preceded by a consonant/medial, remove the consonant/medial. + // - In all other cases, it's strange, so just remove the last code point. + if (eventSize <= 1) { + mCurrentEvents.clear(); + } else { // eventSize >= 2 + final int previousCodePoint = mCurrentEvents.get(eventSize - 2).mCodePoint; + if (previousCodePoint == ZERO_WIDTH_NON_JOINER) { + mCurrentEvents.remove(eventSize - 1); + mCurrentEvents.remove(eventSize - 2); + } else if (isConsonantOrMedial(previousCodePoint)) { + mCurrentEvents.remove(eventSize - 2); + } else { + mCurrentEvents.remove(eventSize - 1); + } + } + return null; + } else if (eventSize > 0) { + mCurrentEvents.remove(eventSize - 1); + return null; + } + } + } + // This character is not part of the combining scheme, so we should reset everything. + if (mCurrentEvents.size() > 0) { + // If we have events in flight, then add the new event and return the resulting event. + mCurrentEvents.add(newEvent); + return clearAndGetResultingEvent(null); + } else { + // If we don't have any events in flight, then just pass this one through. + return newEvent; + } + } + + @Override + public CharSequence getCombiningStateFeedback() { + return getCharSequence(); + } + + @Override + public void reset() { + mCurrentEvents.clear(); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java deleted file mode 100644 index d8b5758a6..000000000 --- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java +++ /dev/null @@ -1,974 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.keyboard; - -import static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Rect; -import android.os.Build; -import android.os.CountDownTimer; -import android.preference.PreferenceManager; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.AttributeSet; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TabHost; -import android.widget.TabHost.OnTabChangeListener; -import android.widget.TextView; - -import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard; -import com.android.inputmethod.keyboard.internal.EmojiLayoutParams; -import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView; -import com.android.inputmethod.keyboard.internal.KeyDrawParams; -import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.ResourceUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -/** - * View class to implement Emoji palettes. - * The Emoji keyboard consists of group of views {@link R.layout#emoji_palettes_view}. - * <ol> - * <li> Emoji category tabs. - * <li> Delete button. - * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab. - * <li> Back to main keyboard button and enter button. - * </ol> - * Because of the above reasons, this class doesn't extend {@link KeyboardView}. - */ -public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener, - ViewPager.OnPageChangeListener, View.OnClickListener, View.OnTouchListener, - EmojiPageKeyboardView.OnKeyEventListener { - static final String TAG = EmojiPalettesView.class.getSimpleName(); - private static final boolean DEBUG_PAGER = false; - private final int mKeyBackgroundId; - private final int mEmojiFunctionalKeyBackgroundId; - private final ColorStateList mTabLabelColor; - private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener; - private EmojiPalettesAdapter mEmojiPalettesAdapter; - private final EmojiLayoutParams mEmojiLayoutParams; - - private TextView mAlphabetKeyLeft; - private TextView mAlphabetKeyRight; - private TabHost mTabHost; - private ViewPager mEmojiPager; - private int mCurrentPagerPosition = 0; - private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView; - - private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; - - private static final int CATEGORY_ID_UNSPECIFIED = -1; - public static final int CATEGORY_ID_RECENTS = 0; - public static final int CATEGORY_ID_PEOPLE = 1; - public static final int CATEGORY_ID_OBJECTS = 2; - public static final int CATEGORY_ID_NATURE = 3; - public static final int CATEGORY_ID_PLACES = 4; - public static final int CATEGORY_ID_SYMBOLS = 5; - public static final int CATEGORY_ID_EMOTICONS = 6; - - private static class CategoryProperties { - public int mCategoryId; - public int mPageCount; - public CategoryProperties(final int categoryId, final int pageCount) { - mCategoryId = categoryId; - mPageCount = pageCount; - } - } - - private static class EmojiCategory { - private static final String[] sCategoryName = { - "recents", - "people", - "objects", - "nature", - "places", - "symbols", - "emoticons" }; - private static final int[] sCategoryIcon = { - R.drawable.ic_emoji_recent_light, - R.drawable.ic_emoji_people_light, - R.drawable.ic_emoji_objects_light, - R.drawable.ic_emoji_nature_light, - R.drawable.ic_emoji_places_light, - R.drawable.ic_emoji_symbols_light, - 0 }; - private static final String[] sCategoryLabel = - { null, null, null, null, null, null, ":-)" }; - private static final int[] sAccessibilityDescriptionResourceIdsForCategories = { - R.string.spoken_descrption_emoji_category_recents, - R.string.spoken_descrption_emoji_category_people, - R.string.spoken_descrption_emoji_category_objects, - R.string.spoken_descrption_emoji_category_nature, - R.string.spoken_descrption_emoji_category_places, - R.string.spoken_descrption_emoji_category_symbols, - R.string.spoken_descrption_emoji_category_emoticons }; - private static final int[] sCategoryElementId = { - KeyboardId.ELEMENT_EMOJI_RECENTS, - KeyboardId.ELEMENT_EMOJI_CATEGORY1, - KeyboardId.ELEMENT_EMOJI_CATEGORY2, - KeyboardId.ELEMENT_EMOJI_CATEGORY3, - KeyboardId.ELEMENT_EMOJI_CATEGORY4, - KeyboardId.ELEMENT_EMOJI_CATEGORY5, - KeyboardId.ELEMENT_EMOJI_CATEGORY6 }; - private final SharedPreferences mPrefs; - private final Resources mRes; - private final int mMaxPageKeyCount; - private final KeyboardLayoutSet mLayoutSet; - private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap(); - private final ArrayList<CategoryProperties> mShownCategories = - CollectionUtils.newArrayList(); - private final ConcurrentHashMap<Long, DynamicGridKeyboard> - mCategoryKeyboardMap = new ConcurrentHashMap<Long, DynamicGridKeyboard>(); - - private int mCurrentCategoryId = CATEGORY_ID_UNSPECIFIED; - private int mCurrentCategoryPageId = 0; - - public EmojiCategory(final SharedPreferences prefs, final Resources res, - final KeyboardLayoutSet layoutSet) { - mPrefs = prefs; - mRes = res; - mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count); - mLayoutSet = layoutSet; - for (int i = 0; i < sCategoryName.length; ++i) { - mCategoryNameToIdMap.put(sCategoryName[i], i); - } - addShownCategoryId(CATEGORY_ID_RECENTS); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KeyLimePie") - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KitKat")) { - addShownCategoryId(CATEGORY_ID_PEOPLE); - addShownCategoryId(CATEGORY_ID_OBJECTS); - addShownCategoryId(CATEGORY_ID_NATURE); - addShownCategoryId(CATEGORY_ID_PLACES); - mCurrentCategoryId = - Settings.readLastShownEmojiCategoryId(mPrefs, CATEGORY_ID_PEOPLE); - } else { - mCurrentCategoryId = - Settings.readLastShownEmojiCategoryId(mPrefs, CATEGORY_ID_SYMBOLS); - } - addShownCategoryId(CATEGORY_ID_SYMBOLS); - addShownCategoryId(CATEGORY_ID_EMOTICONS); - getKeyboard(CATEGORY_ID_RECENTS, 0 /* cagetoryPageId */) - .loadRecentKeys(mCategoryKeyboardMap.values()); - } - - private void addShownCategoryId(final int categoryId) { - // Load a keyboard of categoryId - getKeyboard(categoryId, 0 /* cagetoryPageId */); - final CategoryProperties properties = - new CategoryProperties(categoryId, getCategoryPageCount(categoryId)); - mShownCategories.add(properties); - } - - public String getCategoryName(final int categoryId, final int categoryPageId) { - return sCategoryName[categoryId] + "-" + categoryPageId; - } - - public int getCategoryId(final String name) { - final String[] strings = name.split("-"); - return mCategoryNameToIdMap.get(strings[0]); - } - - public int getCategoryIcon(final int categoryId) { - return sCategoryIcon[categoryId]; - } - - public String getCategoryLabel(final int categoryId) { - return sCategoryLabel[categoryId]; - } - - public String getAccessibilityDescription(final int categoryId) { - return mRes.getString(sAccessibilityDescriptionResourceIdsForCategories[categoryId]); - } - - public ArrayList<CategoryProperties> getShownCategories() { - return mShownCategories; - } - - public int getCurrentCategoryId() { - return mCurrentCategoryId; - } - - public int getCurrentCategoryPageSize() { - return getCategoryPageSize(mCurrentCategoryId); - } - - public int getCategoryPageSize(final int categoryId) { - for (final CategoryProperties prop : mShownCategories) { - if (prop.mCategoryId == categoryId) { - return prop.mPageCount; - } - } - Log.w(TAG, "Invalid category id: " + categoryId); - // Should not reach here. - return 0; - } - - public void setCurrentCategoryId(final int categoryId) { - mCurrentCategoryId = categoryId; - Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId); - } - - public void setCurrentCategoryPageId(final int id) { - mCurrentCategoryPageId = id; - } - - public int getCurrentCategoryPageId() { - return mCurrentCategoryPageId; - } - - public void saveLastTypedCategoryPage() { - Settings.writeLastTypedEmojiCategoryPageId( - mPrefs, mCurrentCategoryId, mCurrentCategoryPageId); - } - - public boolean isInRecentTab() { - return mCurrentCategoryId == CATEGORY_ID_RECENTS; - } - - public int getTabIdFromCategoryId(final int categoryId) { - for (int i = 0; i < mShownCategories.size(); ++i) { - if (mShownCategories.get(i).mCategoryId == categoryId) { - return i; - } - } - Log.w(TAG, "categoryId not found: " + categoryId); - return 0; - } - - // Returns the view pager's page position for the categoryId - public int getPageIdFromCategoryId(final int categoryId) { - final int lastSavedCategoryPageId = - Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId); - int sum = 0; - for (int i = 0; i < mShownCategories.size(); ++i) { - final CategoryProperties props = mShownCategories.get(i); - if (props.mCategoryId == categoryId) { - return sum + lastSavedCategoryPageId; - } - sum += props.mPageCount; - } - Log.w(TAG, "categoryId not found: " + categoryId); - return 0; - } - - public int getRecentTabId() { - return getTabIdFromCategoryId(CATEGORY_ID_RECENTS); - } - - private int getCategoryPageCount(final int categoryId) { - final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); - return (keyboard.getSortedKeys().size() - 1) / mMaxPageKeyCount + 1; - } - - // Returns a pair of the category id and the category page id from the view pager's page - // position. The category page id is numbered in each category. And the view page position - // is the position of the current shown page in the view pager which contains all pages of - // all categories. - public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) { - int sum = 0; - for (final CategoryProperties properties : mShownCategories) { - final int temp = sum; - sum += properties.mPageCount; - if (sum > position) { - return new Pair<Integer, Integer>(properties.mCategoryId, position - temp); - } - } - return null; - } - - // Returns a keyboard from the view pager's page position. - public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) { - final Pair<Integer, Integer> categoryAndId = - getCategoryIdAndPageIdFromPagePosition(position); - if (categoryAndId != null) { - return getKeyboard(categoryAndId.first, categoryAndId.second); - } - return null; - } - - private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) { - return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; - } - - public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) { - synchronized (mCategoryKeyboardMap) { - final Long categotyKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id); - if (mCategoryKeyboardMap.containsKey(categotyKeyboardMapKey)) { - return mCategoryKeyboardMap.get(categotyKeyboardMapKey); - } - - if (categoryId == CATEGORY_ID_RECENTS) { - final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs, - mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), - mMaxPageKeyCount, categoryId); - mCategoryKeyboardMap.put(categotyKeyboardMapKey, kbd); - return kbd; - } - - final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); - final Key[][] sortedKeys = sortKeysIntoPages( - keyboard.getSortedKeys(), mMaxPageKeyCount); - for (int pageId = 0; pageId < sortedKeys.length; ++pageId) { - final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs, - mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), - mMaxPageKeyCount, categoryId); - for (final Key emojiKey : sortedKeys[pageId]) { - if (emojiKey == null) { - break; - } - tempKeyboard.addKeyLast(emojiKey); - } - mCategoryKeyboardMap.put( - getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard); - } - return mCategoryKeyboardMap.get(categotyKeyboardMapKey); - } - } - - public int getTotalPageCountOfAllCategories() { - int sum = 0; - for (CategoryProperties properties : mShownCategories) { - sum += properties.mPageCount; - } - return sum; - } - - private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() { - @Override - public int compare(final Key lhs, final Key rhs) { - final Rect lHitBox = lhs.getHitBox(); - final Rect rHitBox = rhs.getHitBox(); - if (lHitBox.top < rHitBox.top) { - return -1; - } else if (lHitBox.top > rHitBox.top) { - return 1; - } - if (lHitBox.left < rHitBox.left) { - return -1; - } else if (lHitBox.left > rHitBox.left) { - return 1; - } - if (lhs.getCode() == rhs.getCode()) { - return 0; - } - return lhs.getCode() < rhs.getCode() ? -1 : 1; - } - }; - - private static Key[][] sortKeysIntoPages(final List<Key> inKeys, final int maxPageCount) { - final ArrayList<Key> keys = CollectionUtils.newArrayList(inKeys); - Collections.sort(keys, EMOJI_KEY_COMPARATOR); - final int pageCount = (keys.size() - 1) / maxPageCount + 1; - final Key[][] retval = new Key[pageCount][maxPageCount]; - for (int i = 0; i < keys.size(); ++i) { - retval[i / maxPageCount][i % maxPageCount] = keys.get(i); - } - return retval; - } - } - - private final EmojiCategory mEmojiCategory; - - public EmojiPalettesView(final Context context, final AttributeSet attrs) { - this(context, attrs, R.attr.emojiPalettesViewStyle); - } - - public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) { - super(context, attrs, defStyle); - final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, - R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - mKeyBackgroundId = keyboardViewAttr.getResourceId( - R.styleable.KeyboardView_keyBackground, 0); - mEmojiFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId( - R.styleable.KeyboardView_keyBackgroundEmojiFunctional, 0); - keyboardViewAttr.recycle(); - final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs, - R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView); - mTabLabelColor = emojiPalettesViewAttr.getColorStateList( - R.styleable.EmojiPalettesView_emojiTabLabelColor); - emojiPalettesViewAttr.recycle(); - final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( - context, null /* editorInfo */); - final Resources res = context.getResources(); - mEmojiLayoutParams = new EmojiLayoutParams(res); - builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); - builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), - mEmojiLayoutParams.mEmojiKeyboardHeight); - builder.setOptions(false /* shortcutImeEnabled */, false /* showsVoiceInputKey */, - false /* languageSwitchKeyEnabled */); - mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context), - context.getResources(), builder.build()); - mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context); - } - - @Override - protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - final Resources res = getContext().getResources(); - // The main keyboard expands to the entire this {@link KeyboardView}. - final int width = ResourceUtils.getDefaultKeyboardWidth(res) - + getPaddingLeft() + getPaddingRight(); - final int height = ResourceUtils.getDefaultKeyboardHeight(res) - + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) - + getPaddingTop() + getPaddingBottom(); - setMeasuredDimension(width, height); - } - - private void addTab(final TabHost host, final int categoryId) { - final String tabId = mEmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */); - final TabHost.TabSpec tspec = host.newTabSpec(tabId); - tspec.setContent(R.id.emoji_keyboard_dummy); - if (mEmojiCategory.getCategoryIcon(categoryId) != 0) { - final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( - R.layout.emoji_keyboard_tab_icon, null); - iconView.setImageResource(mEmojiCategory.getCategoryIcon(categoryId)); - iconView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId)); - tspec.setIndicator(iconView); - } - if (mEmojiCategory.getCategoryLabel(categoryId) != null) { - final TextView textView = (TextView)LayoutInflater.from(getContext()).inflate( - R.layout.emoji_keyboard_tab_label, null); - textView.setText(mEmojiCategory.getCategoryLabel(categoryId)); - textView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId)); - textView.setTextColor(mTabLabelColor); - tspec.setIndicator(textView); - } - host.addTab(tspec); - } - - @Override - protected void onFinishInflate() { - mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost); - mTabHost.setup(); - for (final CategoryProperties properties : mEmojiCategory.getShownCategories()) { - addTab(mTabHost, properties.mCategoryId); - } - mTabHost.setOnTabChangedListener(this); - mTabHost.getTabWidget().setStripEnabled(true); - - mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this); - - mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager); - mEmojiPager.setAdapter(mEmojiPalettesAdapter); - mEmojiPager.setOnPageChangeListener(this); - mEmojiPager.setOffscreenPageLimit(0); - mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE); - mEmojiLayoutParams.setPagerProperties(mEmojiPager); - - mEmojiCategoryPageIndicatorView = - (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view); - mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); - - setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); - - final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); - mEmojiLayoutParams.setActionBarProperties(actionBar); - - // deleteKey depends only on OnTouchListener. - final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete); - deleteKey.setTag(Constants.CODE_DELETE); - deleteKey.setOnTouchListener(mDeleteKeyOnTouchListener); - - // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on - // {@link View.OnClickListener} as well as {@link View.OnTouchListener}. - // {@link View.OnTouchListener} is used as the trigger of key-press, while - // {@link View.OnClickListener} is used as the trigger of key-release which does not occur - // if the event is canceled by moving off the finger from the view. - // The text on alphabet keys are set at - // {@link #startEmojiPalettes(String,int,float,Typeface)}. - mAlphabetKeyLeft = (TextView)findViewById(R.id.emoji_keyboard_alphabet_left); - mAlphabetKeyLeft.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); - mAlphabetKeyLeft.setTag(Constants.CODE_ALPHA_FROM_EMOJI); - mAlphabetKeyLeft.setOnTouchListener(this); - mAlphabetKeyLeft.setOnClickListener(this); - mAlphabetKeyRight = (TextView)findViewById(R.id.emoji_keyboard_alphabet_right); - mAlphabetKeyRight.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); - mAlphabetKeyRight.setTag(Constants.CODE_ALPHA_FROM_EMOJI); - mAlphabetKeyRight.setOnTouchListener(this); - mAlphabetKeyRight.setOnClickListener(this); - final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space); - spaceKey.setBackgroundResource(mKeyBackgroundId); - spaceKey.setTag(Constants.CODE_SPACE); - spaceKey.setOnTouchListener(this); - spaceKey.setOnClickListener(this); - mEmojiLayoutParams.setKeyProperties(spaceKey); - } - - @Override - public boolean dispatchTouchEvent(final MotionEvent ev) { - // Add here to the stack trace to nail down the {@link IllegalArgumentException} exception - // in MotionEvent that sporadically happens. - // TODO: Remove this override method once the issue has been addressed. - return super.dispatchTouchEvent(ev); - } - - @Override - public void onTabChanged(final String tabId) { - final int categoryId = mEmojiCategory.getCategoryId(tabId); - setCurrentCategoryId(categoryId, false /* force */); - updateEmojiCategoryPageIdView(); - } - - @Override - public void onPageSelected(final int position) { - final Pair<Integer, Integer> newPos = - mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); - setCurrentCategoryId(newPos.first /* categoryId */, false /* force */); - mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */); - updateEmojiCategoryPageIdView(); - mCurrentPagerPosition = position; - } - - @Override - public void onPageScrollStateChanged(final int state) { - // Ignore this message. Only want the actual page selected. - } - - @Override - public void onPageScrolled(final int position, final float positionOffset, - final int positionOffsetPixels) { - mEmojiPalettesAdapter.onPageScrolled(); - final Pair<Integer, Integer> newPos = - mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); - final int newCategoryId = newPos.first; - final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId); - final int currentCategoryId = mEmojiCategory.getCurrentCategoryId(); - final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId(); - final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize(); - if (newCategoryId == currentCategoryId) { - mEmojiCategoryPageIndicatorView.setCategoryPageId( - newCategorySize, newPos.second, positionOffset); - } else if (newCategoryId > currentCategoryId) { - mEmojiCategoryPageIndicatorView.setCategoryPageId( - currentCategorySize, currentCategoryPageId, positionOffset); - } else if (newCategoryId < currentCategoryId) { - mEmojiCategoryPageIndicatorView.setCategoryPageId( - currentCategorySize, currentCategoryPageId, positionOffset - 1); - } - } - - /** - * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnTouchListener} - * interface to handle touch events from View-based elements such as the space bar. - * Note that this method is used only for observing {@link MotionEvent#ACTION_DOWN} to trigger - * {@link KeyboardActionListener#onPressKey}. {@link KeyboardActionListener#onReleaseKey} will - * be covered by {@link #onClick} as long as the event is not canceled. - */ - @Override - public boolean onTouch(final View v, final MotionEvent event) { - if (event.getActionMasked() != MotionEvent.ACTION_DOWN) { - return false; - } - final Object tag = v.getTag(); - if (!(tag instanceof Integer)) { - return false; - } - final int code = (Integer) tag; - mKeyboardActionListener.onPressKey( - code, 0 /* repeatCount */, true /* isSinglePointer */); - // It's important to return false here. Otherwise, {@link #onClick} and touch-down visual - // feedback stop working. - return false; - } - - /** - * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnClickListener} - * interface to handle non-canceled touch-up events from View-based elements such as the space - * bar. - */ - @Override - public void onClick(View v) { - final Object tag = v.getTag(); - if (!(tag instanceof Integer)) { - return; - } - final int code = (Integer) tag; - mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE, - false /* isKeyRepeat */); - mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); - } - - /** - * Called from {@link EmojiPageKeyboardView} through - * {@link com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView.OnKeyEventListener} - * interface to handle touch events from non-View-based elements such as Emoji buttons. - */ - @Override - public void onPressKey(final Key key) { - final int code = key.getCode(); - mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */); - } - - /** - * Called from {@link EmojiPageKeyboardView} through - * {@link com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView.OnKeyEventListener} - * interface to handle touch events from non-View-based elements such as Emoji buttons. - */ - @Override - public void onReleaseKey(final Key key) { - mEmojiPalettesAdapter.addRecentKey(key); - mEmojiCategory.saveLastTypedCategoryPage(); - final int code = key.getCode(); - if (code == Constants.CODE_OUTPUT_TEXT) { - mKeyboardActionListener.onTextInput(key.getOutputText()); - } else { - mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE, - false /* isKeyRepeat */); - } - mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); - } - - public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { - if (!enabled) return; - // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? - setLayerType(LAYER_TYPE_HARDWARE, null); - } - - private static void setupAlphabetKey(final TextView alphabetKey, final String label, - final KeyDrawParams params) { - alphabetKey.setText(label); - alphabetKey.setTextColor(params.mTextColor); - alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize); - alphabetKey.setTypeface(params.mTypeface); - } - - public void startEmojiPalettes(final String switchToAlphaLabel, - final KeyVisualAttributes keyVisualAttr) { - if (DEBUG_PAGER) { - Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition); - } - final KeyDrawParams params = new KeyDrawParams(); - params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr); - setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params); - setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params); - mEmojiPager.setAdapter(mEmojiPalettesAdapter); - mEmojiPager.setCurrentItem(mCurrentPagerPosition); - } - - public void stopEmojiPalettes() { - if (DEBUG_PAGER) { - Log.d(TAG, "deallocate emoji palettes memory"); - } - mEmojiPalettesAdapter.flushPendingRecentKeys(); - mEmojiPager.setAdapter(null); - } - - public void setKeyboardActionListener(final KeyboardActionListener listener) { - mKeyboardActionListener = listener; - mDeleteKeyOnTouchListener.setKeyboardActionListener(mKeyboardActionListener); - } - - private void updateEmojiCategoryPageIdView() { - if (mEmojiCategoryPageIndicatorView == null) { - return; - } - mEmojiCategoryPageIndicatorView.setCategoryPageId( - mEmojiCategory.getCurrentCategoryPageSize(), - mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */); - } - - private void setCurrentCategoryId(final int categoryId, final boolean force) { - final int oldCategoryId = mEmojiCategory.getCurrentCategoryId(); - if (oldCategoryId == categoryId && !force) { - return; - } - - if (oldCategoryId == CATEGORY_ID_RECENTS) { - // Needs to save pending updates for recent keys when we get out of the recents - // category because we don't want to move the recent emojis around while the user - // is in the recents category. - mEmojiPalettesAdapter.flushPendingRecentKeys(); - } - - mEmojiCategory.setCurrentCategoryId(categoryId); - final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId); - final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId); - if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition( - mEmojiPager.getCurrentItem()).first != categoryId) { - mEmojiPager.setCurrentItem(newCategoryPageId, false /* smoothScroll */); - } - if (force || mTabHost.getCurrentTab() != newTabId) { - mTabHost.setCurrentTab(newTabId); - } - } - - private static class EmojiPalettesAdapter extends PagerAdapter { - private final EmojiPageKeyboardView.OnKeyEventListener mListener; - private final DynamicGridKeyboard mRecentsKeyboard; - private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = - CollectionUtils.newSparseArray(); - private final EmojiCategory mEmojiCategory; - private int mActivePosition = 0; - - public EmojiPalettesAdapter(final EmojiCategory emojiCategory, - final EmojiPageKeyboardView.OnKeyEventListener listener) { - mEmojiCategory = emojiCategory; - mListener = listener; - mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0); - } - - public void flushPendingRecentKeys() { - mRecentsKeyboard.flushPendingRecentKeys(); - final KeyboardView recentKeyboardView = - mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId()); - if (recentKeyboardView != null) { - recentKeyboardView.invalidateAllKeys(); - } - } - - public void addRecentKey(final Key key) { - if (mEmojiCategory.isInRecentTab()) { - mRecentsKeyboard.addPendingKey(key); - return; - } - mRecentsKeyboard.addKeyFirst(key); - final KeyboardView recentKeyboardView = - mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId()); - if (recentKeyboardView != null) { - recentKeyboardView.invalidateAllKeys(); - } - } - - public void onPageScrolled() { - // Make sure the delayed key-down event (highlight effect and haptic feedback) will be - // canceled. - final EmojiPageKeyboardView currentKeyboardView = - mActiveKeyboardViews.get(mActivePosition); - if (currentKeyboardView != null) { - currentKeyboardView.releaseCurrentKey(); - } - } - - @Override - public int getCount() { - return mEmojiCategory.getTotalPageCountOfAllCategories(); - } - - @Override - public void setPrimaryItem(final ViewGroup container, final int position, - final Object object) { - if (mActivePosition == position) { - return; - } - final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition); - if (oldKeyboardView != null) { - oldKeyboardView.releaseCurrentKey(); - oldKeyboardView.deallocateMemory(); - } - mActivePosition = position; - } - - @Override - public Object instantiateItem(final ViewGroup container, final int position) { - if (DEBUG_PAGER) { - Log.d(TAG, "instantiate item: " + position); - } - final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position); - if (oldKeyboardView != null) { - oldKeyboardView.deallocateMemory(); - // This may be redundant but wanted to be safer.. - mActiveKeyboardViews.remove(position); - } - final Keyboard keyboard = - mEmojiCategory.getKeyboardFromPagePosition(position); - final LayoutInflater inflater = LayoutInflater.from(container.getContext()); - final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate( - R.layout.emoji_keyboard_page, container, false /* attachToRoot */); - keyboardView.setKeyboard(keyboard); - keyboardView.setOnKeyEventListener(mListener); - container.addView(keyboardView); - mActiveKeyboardViews.put(position, keyboardView); - return keyboardView; - } - - @Override - public boolean isViewFromObject(final View view, final Object object) { - return view == object; - } - - @Override - public void destroyItem(final ViewGroup container, final int position, - final Object object) { - if (DEBUG_PAGER) { - Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName()); - } - final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position); - if (keyboardView != null) { - keyboardView.deallocateMemory(); - mActiveKeyboardViews.remove(position); - } - if (object instanceof View) { - container.removeView((View)object); - } else { - Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object); - } - } - } - - private static class DeleteKeyOnTouchListener implements OnTouchListener { - private static final long MAX_REPEAT_COUNT_TIME = TimeUnit.SECONDS.toMillis(30); - private final int mDeleteKeyPressedBackgroundColor; - private final long mKeyRepeatStartTimeout; - private final long mKeyRepeatInterval; - - public DeleteKeyOnTouchListener(Context context) { - final Resources res = context.getResources(); - mDeleteKeyPressedBackgroundColor = - res.getColor(R.color.emoji_key_pressed_background_color); - mKeyRepeatStartTimeout = res.getInteger(R.integer.config_key_repeat_start_timeout); - mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); - mTimer = new CountDownTimer(MAX_REPEAT_COUNT_TIME, mKeyRepeatInterval) { - @Override - public void onTick(long millisUntilFinished) { - final long elapsed = MAX_REPEAT_COUNT_TIME - millisUntilFinished; - if (elapsed < mKeyRepeatStartTimeout) { - return; - } - onKeyRepeat(); - } - @Override - public void onFinish() { - onKeyRepeat(); - } - }; - } - - /** Key-repeat state. */ - private static final int KEY_REPEAT_STATE_INITIALIZED = 0; - // The key is touched but auto key-repeat is not started yet. - private static final int KEY_REPEAT_STATE_KEY_DOWN = 1; - // At least one key-repeat event has already been triggered and the key is not released. - private static final int KEY_REPEAT_STATE_KEY_REPEAT = 2; - - private KeyboardActionListener mKeyboardActionListener = - KeyboardActionListener.EMPTY_LISTENER; - - // TODO: Do the same things done in PointerTracker - private final CountDownTimer mTimer; - private int mState = KEY_REPEAT_STATE_INITIALIZED; - private int mRepeatCount = 0; - - public void setKeyboardActionListener(final KeyboardActionListener listener) { - mKeyboardActionListener = listener; - } - - @Override - public boolean onTouch(final View v, final MotionEvent event) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - onTouchDown(v); - return true; - case MotionEvent.ACTION_MOVE: - final float x = event.getX(); - final float y = event.getY(); - if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) { - // Stop generating key events once the finger moves away from the view area. - onTouchCanceled(v); - } - return true; - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: - onTouchUp(v); - return true; - } - return false; - } - - private void handleKeyDown() { - mKeyboardActionListener.onPressKey( - Constants.CODE_DELETE, mRepeatCount, true /* isSinglePointer */); - } - - private void handleKeyUp() { - mKeyboardActionListener.onCodeInput(Constants.CODE_DELETE, - NOT_A_COORDINATE, NOT_A_COORDINATE, false /* isKeyRepeat */); - mKeyboardActionListener.onReleaseKey( - Constants.CODE_DELETE, false /* withSliding */); - ++mRepeatCount; - } - - private void onTouchDown(final View v) { - mTimer.cancel(); - mRepeatCount = 0; - handleKeyDown(); - v.setBackgroundColor(mDeleteKeyPressedBackgroundColor); - mState = KEY_REPEAT_STATE_KEY_DOWN; - mTimer.start(); - } - - private void onTouchUp(final View v) { - mTimer.cancel(); - if (mState == KEY_REPEAT_STATE_KEY_DOWN) { - handleKeyUp(); - } - v.setBackgroundColor(Color.TRANSPARENT); - mState = KEY_REPEAT_STATE_INITIALIZED; - } - - private void onTouchCanceled(final View v) { - mTimer.cancel(); - v.setBackgroundColor(Color.TRANSPARENT); - mState = KEY_REPEAT_STATE_INITIALIZED; - } - - // Called by {@link #mTimer} in the UI thread as an auto key-repeat signal. - private void onKeyRepeat() { - switch (mState) { - case KEY_REPEAT_STATE_INITIALIZED: - // Basically this should not happen. - break; - case KEY_REPEAT_STATE_KEY_DOWN: - // Do not call {@link #handleKeyDown} here because it has already been called - // in {@link #onTouchDown}. - handleKeyUp(); - mState = KEY_REPEAT_STATE_KEY_REPEAT; - break; - case KEY_REPEAT_STATE_KEY_REPEAT: - handleKeyDown(); - handleKeyUp(); - break; - } - } - } -} diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 816a94300..88cde1111 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -60,6 +60,7 @@ public class Key implements Comparable<Key> { private final int mLabelFlags; private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01; private static final int LABEL_FLAGS_ALIGN_RIGHT = 0x02; + private static final int LABEL_FLAGS_ALIGN_BUTTOM = 0x04; private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08; private static final int LABEL_FLAGS_FONT_NORMAL = 0x10; private static final int LABEL_FLAGS_FONT_MONO_SPACE = 0x20; @@ -86,6 +87,7 @@ public class Key implements Comparable<Key> { private static final int LABEL_FLAGS_PRESERVE_CASE = 0x10000; private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000; private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000; + private static final int LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR = 0x80000; private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000; private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000; @@ -193,7 +195,8 @@ public class Key implements Comparable<Key> { mHintLabel = hintLabel; mLabelFlags = labelFlags; mBackgroundType = backgroundType; - mActionFlags = 0; + // TODO: Pass keyActionFlags as an argument. + mActionFlags = ACTION_FLAGS_NO_KEY_PREVIEW; mMoreKeys = null; mMoreKeysColumnAndFlags = 0; mLabel = label; @@ -218,7 +221,7 @@ public class Key implements Comparable<Key> { * * @param keySpec the key specification. * @param keyAttr the Key XML attributes array. - * @param keyStyle the {@link KeyStyle} of this key. + * @param style the {@link KeyStyle} of this key. * @param params the keyboard building parameters. * @param row the row that this key belongs to. row's x-coordinate will be the right edge of * this key. @@ -465,15 +468,24 @@ public class Key implements Comparable<Key> { @Override public String toString() { - final String label; - if (StringUtils.codePointCount(mLabel) == 1 && mLabel.codePointAt(0) == mCode) { - label = ""; - } else { - label = "/" + mLabel; + return toShortString() + " " + getX() + "," + getY() + " " + getWidth() + "x" + getHeight(); + } + + public String toShortString() { + final int code = getCode(); + if (code == Constants.CODE_OUTPUT_TEXT) { + return getOutputText(); } - return String.format(Locale.ROOT, "%s%s %d,%d %dx%d %s/%s/%s", - Constants.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel, - KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); + return Constants.printableCode(code); + } + + public String toLongString() { + final int iconId = getIconId(); + final String topVisual = (iconId == KeyboardIconsSet.ICON_UNDEFINED) + ? KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(iconId) : getLabel(); + final String hintLabel = getHintLabel(); + final String visual = (hintLabel == null) ? topVisual : topVisual + "^" + hintLabel; + return toString() + " " + visual + "/" + backgroundName(mBackgroundType); } private static String backgroundName(final int backgroundType) { @@ -583,6 +595,9 @@ public class Key implements Comparable<Key> { } public final int selectTextColor(final KeyDrawParams params) { + if ((mLabelFlags & LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR) != 0) { + return params.mFunctionalTextColor; + } return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor; } @@ -642,6 +657,10 @@ public class Key implements Comparable<Key> { return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; } + public final boolean isAlignButtom() { + return (mLabelFlags & LABEL_FLAGS_ALIGN_BUTTOM) != 0; + } + public final boolean isAlignLeftOfCenter() { return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; } @@ -857,17 +876,6 @@ public class Key implements Comparable<Key> { android.R.attr.state_empty }; - // functional normal state (with properties) - private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { - android.R.attr.state_single - }; - - // functional pressed state (with properties) - private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { - android.R.attr.state_single, - android.R.attr.state_pressed - }; - // action normal state (with properties) private static final int[] KEY_STATE_ACTIVE_NORMAL = { android.R.attr.state_active @@ -880,25 +888,43 @@ public class Key implements Comparable<Key> { }; /** - * Returns the drawable state for the key, based on the current state and type of the key. - * @return the drawable state of the key. + * Returns the background drawable for the key, based on the current state and type of the key. + * @return the background drawable of the key. * @see android.graphics.drawable.StateListDrawable#setState(int[]) */ - public final int[] getCurrentDrawableState() { + public final Drawable selectBackgroundDrawable(final Drawable keyBackground, + final Drawable functionalKeyBackground, final Drawable spacebarBackground) { + final Drawable background; + if (mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL) { + background = functionalKeyBackground; + } else if (getCode() == Constants.CODE_SPACE) { + background = spacebarBackground; + } else { + background = keyBackground; + } + final int[] stateSet; switch (mBackgroundType) { - case BACKGROUND_TYPE_FUNCTIONAL: - return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL; case BACKGROUND_TYPE_ACTION: - return mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; + stateSet = mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; + break; case BACKGROUND_TYPE_STICKY_OFF: - return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; + stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; + break; case BACKGROUND_TYPE_STICKY_ON: - return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; + stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; + break; case BACKGROUND_TYPE_EMPTY: - return mPressed ? KEY_STATE_PRESSED : KEY_STATE_EMPTY; + stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_EMPTY; + break; + case BACKGROUND_TYPE_FUNCTIONAL: + stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; + break; default: /* BACKGROUND_TYPE_NORMAL */ - return mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; + stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; + break; } + background.setState(stateSet); + return background; } public static class Spacer extends Key { diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index f646a03c2..85dfea4e7 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -22,9 +22,9 @@ 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.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -83,7 +83,7 @@ public class Keyboard { public final List<Key> mAltCodeKeysWhileTyping; public final KeyboardIconsSet mIconsSet; - private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray(); + private final SparseArray<Key> mKeyCache = new SparseArray<>(); private final ProximityInfo mProximityInfo; private final boolean mProximityCharsCorrectionEnabled; @@ -103,8 +103,7 @@ public class Keyboard { mTopPadding = params.mTopPadding; mVerticalGap = params.mVerticalGap; - mSortedKeys = Collections.unmodifiableList( - CollectionUtils.newArrayList(params.mSortedKeys)); + mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys)); mShiftKeys = Collections.unmodifiableList(params.mShiftKeys); mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping); mIconsSet = params.mIconsSet; @@ -159,7 +158,7 @@ public class Keyboard { /** * Return the sorted list of keys of this keyboard. * The keys are sorted from top-left to bottom-right order. - * The list may contain {@link Spacer} object as well. + * The list may contain {@link Key.Spacer} object as well. * @return the sorted unmodifiable list of {@link Key}s of this keyboard. */ public List<Key> getSortedKeys() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 93a55fe6a..3c1167538 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -70,7 +70,6 @@ public final class KeyboardId { public final int mElementId; public final EditorInfo mEditorInfo; public final boolean mClobberSettingsKey; - public final boolean mSupportsSwitchingToShortcutIme; public final boolean mLanguageSwitchKeyEnabled; public final String mCustomActionLabel; public final boolean mHasShortcutKey; @@ -86,11 +85,10 @@ public final class KeyboardId { mElementId = elementId; mEditorInfo = params.mEditorInfo; mClobberSettingsKey = params.mNoSettingsKey; - mSupportsSwitchingToShortcutIme = params.mSupportsSwitchingToShortcutIme; mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled; mCustomActionLabel = (mEditorInfo.actionLabel != null) ? mEditorInfo.actionLabel.toString() : null; - mHasShortcutKey = mSupportsSwitchingToShortcutIme && params.mShowsVoiceInputKey; + mHasShortcutKey = params.mVoiceInputKeyEnabled; mHashCode = computeHashCode(this); } @@ -103,7 +101,6 @@ public final class KeyboardId { id.mHeight, id.passwordInput(), id.mClobberSettingsKey, - id.mSupportsSwitchingToShortcutIme, id.mHasShortcutKey, id.mLanguageSwitchKeyEnabled, id.isMultiLine(), @@ -124,7 +121,6 @@ public final class KeyboardId { && other.mHeight == mHeight && other.passwordInput() == passwordInput() && other.mClobberSettingsKey == mClobberSettingsKey - && other.mSupportsSwitchingToShortcutIme == mSupportsSwitchingToShortcutIme && other.mHasShortcutKey == mHasShortcutKey && other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled && other.isMultiLine() == isMultiLine() @@ -179,7 +175,7 @@ public final class KeyboardId { @Override public String toString() { - return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), mWidth, mHeight, @@ -189,7 +185,6 @@ public final class KeyboardId { (navigatePrevious() ? " navigatePrevious" : ""), (mClobberSettingsKey ? " clobberSettingsKey" : ""), (passwordInput() ? " passwordInput" : ""), - (mSupportsSwitchingToShortcutIme ? " supportsSwitchingToShortcutIme" : ""), (mHasShortcutKey ? " hasShortcutKey" : ""), (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""), (isMultiLine() ? " isMultiLine" : "") diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index cde5091c4..3e5cfc11a 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -17,8 +17,6 @@ package com.android.inputmethod.keyboard; import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY; import android.content.Context; @@ -41,7 +39,6 @@ import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.XmlParseUtils; @@ -81,7 +78,7 @@ public final class KeyboardLayoutSet { // them from disappearing from sKeyboardCache. private static final Keyboard[] sForcibleKeyboardCache = new Keyboard[FORCIBLE_CACHE_SIZE]; private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = - CollectionUtils.newHashMap(); + new HashMap<>(); private static final KeysCache sKeysCache = new KeysCache(); @SuppressWarnings("serial") @@ -103,12 +100,11 @@ public final class KeyboardLayoutSet { public static final class Params { String mKeyboardLayoutSetName; int mMode; - EditorInfo mEditorInfo; boolean mDisableTouchPositionCorrectionDataForTest; + // TODO: Use {@link InputAttributes} instead of these variables. + EditorInfo mEditorInfo; boolean mIsPasswordField; - boolean mSupportsSwitchingToShortcutIme; - boolean mShowsVoiceInputKey; - boolean mNoMicrophoneKey; + boolean mVoiceInputKeyEnabled; boolean mNoSettingsKey; boolean mLanguageSwitchKeyEnabled; InputMethodSubtype mSubtype; @@ -117,7 +113,7 @@ public final class KeyboardLayoutSet { int mKeyboardHeight; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap = - CollectionUtils.newSparseArray(); + new SparseArray<>(); } public static void clearKeyboardCache() { @@ -181,7 +177,7 @@ public final class KeyboardLayoutSet { } final KeyboardBuilder<KeyboardParams> builder = - new KeyboardBuilder<KeyboardParams>(mContext, new KeyboardParams()); + new KeyboardBuilder<>(mContext, new KeyboardParams()); if (id.isAlphabetKeyboard()) { builder.setAutoGenerate(sKeysCache); } @@ -192,7 +188,7 @@ public final class KeyboardLayoutSet { } builder.setProximityCharsCorrectionEnabled(elementParams.mProximityCharsCorrectionEnabled); final Keyboard keyboard = builder.build(); - sKeyboardCache.put(id, new SoftReference<Keyboard>(keyboard)); + sKeyboardCache.put(id, new SoftReference<>(keyboard)); if ((id.mElementId == KeyboardId.ELEMENT_ALPHABET || id.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) && !mParams.mIsSpellChecker) { @@ -229,14 +225,9 @@ public final class KeyboardLayoutSet { final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO; params.mMode = getKeyboardMode(editorInfo); + // TODO: Consolidate those with {@link InputAttributes}. params.mEditorInfo = editorInfo; params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType); - @SuppressWarnings("deprecation") - final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( - null, NO_MICROPHONE_COMPAT, editorInfo); - params.mNoMicrophoneKey = InputAttributes.inPrivateImeOptions( - mPackageName, NO_MICROPHONE, editorInfo) - || deprecatedNoMicrophone; params.mNoSettingsKey = InputAttributes.inPrivateImeOptions( mPackageName, NO_SETTINGS_KEY, editorInfo); } @@ -249,6 +240,7 @@ public final class KeyboardLayoutSet { public Builder setSubtype(final InputMethodSubtype subtype) { final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype); + // TODO: Consolidate with {@link InputAttributes}. @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( mPackageName, FORCE_ASCII, mParams.mEditorInfo); @@ -269,12 +261,13 @@ public final class KeyboardLayoutSet { return this; } - public Builder setOptions(final boolean isShortcutImeEnabled, - final boolean showsVoiceInputKey, final boolean languageSwitchKeyEnabled) { - mParams.mSupportsSwitchingToShortcutIme = - isShortcutImeEnabled && !mParams.mNoMicrophoneKey && !mParams.mIsPasswordField; - mParams.mShowsVoiceInputKey = showsVoiceInputKey; - mParams.mLanguageSwitchKeyEnabled = languageSwitchKeyEnabled; + public Builder setVoiceInputKeyEnabled(final boolean enabled) { + mParams.mVoiceInputKeyEnabled = enabled; + return this; + } + + public Builder setLanguageSwitchKeyEnabled(final boolean enabled) { + mParams.mLanguageSwitchKeyEnabled = enabled; return this; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 0235fde38..6aeff189f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -26,14 +26,13 @@ import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; +import com.android.inputmethod.keyboard.emoji.EmojiPalettesView; import com.android.inputmethod.keyboard.internal.KeyboardState; import com.android.inputmethod.keyboard.internal.KeyboardTextsSet; import com.android.inputmethod.latin.InputView; import com.android.inputmethod.latin.LatinIME; -import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -61,11 +60,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet(); private SettingsValues mCurrentSettingsValues; - /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of - * what user actually typed. */ - private boolean mIsAutoCorrectionActive; - - private KeyboardTheme mKeyboardTheme = KeyboardTheme.getDefaultKeyboardTheme(); + private KeyboardTheme mKeyboardTheme; private Context mThemeContext; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); @@ -102,7 +97,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) { - if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) { + if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) { mKeyboardTheme = keyboardTheme; mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); KeyboardLayoutSet.clearKeyboardCache(); @@ -120,10 +115,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); - builder.setOptions( - mSubtypeSwitcher.isShortcutImeEnabled(), - settingsValues.mShowsVoiceInputKey, - mLatinIME.shouldSwitchToOtherInputMethods()); + builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); + builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); mKeyboardLayoutSet = builder.build(); mCurrentSettingsValues = settingsValues; try { @@ -131,7 +124,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext); } catch (KeyboardLayoutSetException e) { Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); - LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause()); return; } } @@ -142,12 +134,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } } - public void onFinishInputView() { - mIsAutoCorrectionActive = false; - } - public void onHideWindow() { - mIsAutoCorrectionActive = false; + if (mKeyboardView != null) { + mKeyboardView.onHideWindow(); + } } private void setKeyboard(final Keyboard keyboard) { @@ -165,7 +155,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mCurrentSettingsValues.mKeyPreviewShowUpDuration, mCurrentSettingsValues.mKeyPreviewDismissEndScale, mCurrentSettingsValues.mKeyPreviewDismissDuration); - keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive); keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); @@ -251,10 +240,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setEmojiKeyboard() { + final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); mMainKeyboardFrame.setVisibility(View.GONE); mEmojiPalettesView.startEmojiPalettes( mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL), - mKeyboardView.getKeyVisualAttribute()); + mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet); mEmojiPalettesView.setVisibility(View.VISIBLE); } @@ -340,7 +330,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mKeyboardView.closing(); } - updateKeyboardThemeAndContextThemeWrapper(mLatinIME, mKeyboardTheme); + updateKeyboardThemeAndContextThemeWrapper( + mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( R.layout.input_view, null); mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); @@ -353,11 +344,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled( isHardwareAcceleratedDrawingEnabled); mEmojiPalettesView.setKeyboardActionListener(mLatinIME); - - // This always needs to be set since the accessibility state can - // potentially change without the input view being re-created. - AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView); - return mCurrentInputView; } @@ -367,15 +353,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } } - public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) { - if (mIsAutoCorrectionActive != isAutoCorrection) { - mIsAutoCorrectionActive = isAutoCorrection; - if (mKeyboardView != null) { - mKeyboardView.updateAutoCorrectionState(isAutoCorrection); - } - } - } - public int getKeyboardShiftMode() { final Keyboard keyboard = getKeyboard(); if (keyboard == null) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 4db72ad4d..0ea9c742f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -17,34 +17,76 @@ package com.android.inputmethod.keyboard; import android.content.SharedPreferences; +import android.os.Build; +import android.os.Build.VERSION_CODES; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.settings.Settings; -public final class KeyboardTheme { +import java.util.Arrays; + +public final class KeyboardTheme implements Comparable<KeyboardTheme> { private static final String TAG = KeyboardTheme.class.getSimpleName(); + static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916"; + static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509"; + public static final int THEME_ID_ICS = 0; public static final int THEME_ID_KLP = 2; - private static final int DEFAULT_THEME_ID = THEME_ID_KLP; + public static final int THEME_ID_LXX_DARK = 3; + public static final int DEFAULT_THEME_ID = THEME_ID_KLP; private static final KeyboardTheme[] KEYBOARD_THEMES = { - new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS), - new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP), + new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS, + // This has never been selected because we support ICS or later. + VERSION_CODES.BASE), + new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP, + // Default theme for ICS, JB, and KLP. + VERSION_CODES.ICE_CREAM_SANDWICH), + new KeyboardTheme(THEME_ID_LXX_DARK, R.style.KeyboardTheme_LXX_Dark, + // Default theme for LXX. + // TODO: Update this constant once the *next* version becomes available. + VERSION_CODES.CUR_DEVELOPMENT), }; + static { + // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}. + Arrays.sort(KEYBOARD_THEMES); + } + public final int mThemeId; public final int mStyleId; + private final int mMinApiVersion; // Note: The themeId should be aligned with "themeId" attribute of Keyboard style - // in values/style.xml. - public KeyboardTheme(final int themeId, final int styleId) { + // in values/themes-<style>.xml. + private KeyboardTheme(final int themeId, final int styleId, final int minApiVersion) { mThemeId = themeId; mStyleId = styleId; + mMinApiVersion = minApiVersion; + } + + @Override + public int compareTo(final KeyboardTheme rhs) { + if (mMinApiVersion > rhs.mMinApiVersion) return -1; + if (mMinApiVersion < rhs.mMinApiVersion) return 1; + return 0; + } + + @Override + public boolean equals(final Object o) { + if (o == this) return true; + return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId; } - private static KeyboardTheme searchKeyboardTheme(final int themeId) { + @Override + public int hashCode() { + return mThemeId; + } + + @UsedForTesting + static KeyboardTheme searchKeyboardThemeById(final int themeId) { // TODO: This search algorithm isn't optimal if there are many themes. for (final KeyboardTheme theme : KEYBOARD_THEMES) { if (theme.mThemeId == themeId) { @@ -54,28 +96,87 @@ public final class KeyboardTheme { return null; } - public static KeyboardTheme getDefaultKeyboardTheme() { - return searchKeyboardTheme(DEFAULT_THEME_ID); + private static int getSdkVersion() { + final int sdkVersion = Build.VERSION.SDK_INT; + // TODO: Consider to remove this check once the *next* version becomes available. + if (sdkVersion == VERSION_CODES.KITKAT && Build.VERSION.CODENAME.startsWith("L")) { + return VERSION_CODES.CUR_DEVELOPMENT; + } + return sdkVersion; + } + + @UsedForTesting + static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, + final int sdkVersion) { + final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); + if (klpThemeIdString != null) { + if (sdkVersion <= VERSION_CODES.KITKAT) { + try { + final int themeId = Integer.parseInt(klpThemeIdString); + final KeyboardTheme theme = searchKeyboardThemeById(themeId); + if (theme != null) { + return theme; + } + Log.w(TAG, "Unknown keyboard theme in KLP preference: " + klpThemeIdString); + } catch (final NumberFormatException e) { + Log.w(TAG, "Illegal keyboard theme in KLP preference: " + klpThemeIdString, e); + } + } + // Remove old preference. + Log.i(TAG, "Remove KLP keyboard theme preference: " + klpThemeIdString); + prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply(); + } + // TODO: This search algorithm isn't optimal if there are many themes. + for (final KeyboardTheme theme : KEYBOARD_THEMES) { + if (sdkVersion >= theme.mMinApiVersion) { + return theme; + } + } + return searchKeyboardThemeById(DEFAULT_THEME_ID); + } + + public static void saveKeyboardThemeId(final String themeIdString, + final SharedPreferences prefs) { + saveKeyboardThemeId(themeIdString, prefs, getSdkVersion()); + } + + @UsedForTesting + static String getPreferenceKey(final int sdkVersion) { + if (sdkVersion <= VERSION_CODES.KITKAT) { + return KLP_KEYBOARD_THEME_KEY; + } + return LXX_KEYBOARD_THEME_KEY; + } + + @UsedForTesting + static void saveKeyboardThemeId(final String themeIdString, + final SharedPreferences prefs, final int sdkVersion) { + final String prefKey = getPreferenceKey(sdkVersion); + prefs.edit().putString(prefKey, themeIdString).apply(); } public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) { - final String themeIdString = prefs.getString(Settings.PREF_KEYBOARD_LAYOUT, null); - if (themeIdString == null) { - return getDefaultKeyboardTheme(); + return getKeyboardTheme(prefs, getSdkVersion()); + } + + @UsedForTesting + static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { + final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); + if (lxxThemeIdString == null) { + return getDefaultKeyboardTheme(prefs, sdkVersion); } try { - final int themeId = Integer.parseInt(themeIdString); - final KeyboardTheme theme = searchKeyboardTheme(themeId); + final int themeId = Integer.parseInt(lxxThemeIdString); + final KeyboardTheme theme = searchKeyboardThemeById(themeId); if (theme != null) { return theme; } - Log.w(TAG, "Unknown keyboard theme in preference: " + themeIdString); + Log.w(TAG, "Unknown keyboard theme in LXX preference: " + lxxThemeIdString); } catch (final NumberFormatException e) { - Log.w(TAG, "Illegal keyboard theme in preference: " + themeIdString); + Log.w(TAG, "Illegal keyboard theme in LXX preference: " + lxxThemeIdString, e); } - // Reset preference to default value. - final String defaultThemeIdString = Integer.toString(DEFAULT_THEME_ID); - prefs.edit().putString(Settings.PREF_KEYBOARD_LAYOUT, defaultThemeIdString).apply(); - return getDefaultKeyboardTheme(); + // Remove preference that contains unknown or illegal theme id. + prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply(); + return getDefaultKeyboardTheme(prefs, sdkVersion); } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 8ca00b005..c4ca1c495 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -28,18 +28,15 @@ import android.graphics.Rect; import android.graphics.Region; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; import android.util.AttributeSet; import android.view.View; import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; -import com.android.inputmethod.research.ResearchLogger; import java.util.HashSet; @@ -47,6 +44,9 @@ import java.util.HashSet; * A view that renders a virtual {@link Keyboard}. * * @attr ref R.styleable#KeyboardView_keyBackground + * @attr ref R.styleable#KeyboardView_functionalKeyBackground + * @attr ref R.styleable#KeyboardView_spacebarBackground + * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio * @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding * @attr ref R.styleable#KeyboardView_keyHintLetterPadding * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding @@ -81,7 +81,11 @@ public class KeyboardView extends View { private final float mKeyTextShadowRadius; private final float mVerticalCorrection; private final Drawable mKeyBackground; + private final Drawable mFunctionalKeyBackground; + private final Drawable mSpacebarBackground; + private final float mSpacebarIconWidthRatio; private final Rect mKeyBackgroundPadding = new Rect(); + private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; // HORIZONTAL ELLIPSIS "...", character for popup hint. private static final String POPUP_HINT_CHAR = "\u2026"; @@ -102,7 +106,7 @@ public class KeyboardView extends View { /** True if all keys should be drawn */ private boolean mInvalidateAllKeys; /** The keys that should be drawn */ - private final HashSet<Key> mInvalidatedKeys = CollectionUtils.newHashSet(); + private final HashSet<Key> mInvalidatedKeys = new HashSet<>(); /** The working rectangle variable */ private final Rect mWorkingRect = new Rect(); /** The keyboard bitmap buffer for faster updates */ @@ -124,6 +128,15 @@ public class KeyboardView extends View { R.styleable.KeyboardView, defStyle, R.style.KeyboardView); mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground); mKeyBackground.getPadding(mKeyBackgroundPadding); + final Drawable functionalKeyBackground = keyboardViewAttr.getDrawable( + R.styleable.KeyboardView_functionalKeyBackground); + mFunctionalKeyBackground = (functionalKeyBackground != null) ? functionalKeyBackground + : mKeyBackground; + final Drawable spacebarBackground = keyboardViewAttr.getDrawable( + R.styleable.KeyboardView_spacebarBackground); + mSpacebarBackground = (spacebarBackground != null) ? spacebarBackground : mKeyBackground; + mSpacebarIconWidthRatio = keyboardViewAttr.getFloat( + R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f); mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset( R.styleable.KeyboardView_keyLabelHorizontalPadding, 0); mKeyHintLetterPadding = keyboardViewAttr.getDimension( @@ -133,7 +146,7 @@ public class KeyboardView extends View { mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension( R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0.0f); mKeyTextShadowRadius = keyboardViewAttr.getFloat( - R.styleable.KeyboardView_keyTextShadowRadius, 0.0f); + R.styleable.KeyboardView_keyTextShadowRadius, KET_TEXT_SHADOW_RADIUS_DISABLED); mVerticalCorrection = keyboardViewAttr.getDimension( R.styleable.KeyboardView_verticalCorrection, 0.0f); keyboardViewAttr.recycle(); @@ -171,7 +184,6 @@ public class KeyboardView extends View { */ public void setKeyboard(final Keyboard keyboard) { mKeyboard = keyboard; - LatinImeLogger.onSetKeyboard(keyboard); final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes); mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes); @@ -301,13 +313,6 @@ public class KeyboardView extends View { } } - // Research Logging (Development Only Diagnostics) indicator. - // TODO: Reimplement using a keyboard background image specific to the ResearchLogger, - // and remove this call. - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height); - } - mInvalidatedKeys.clear(); mInvalidateAllKeys = false; } @@ -323,7 +328,9 @@ public class KeyboardView extends View { params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; if (!key.isSpacer()) { - onDrawKeyBackground(key, canvas, mKeyBackground); + final Drawable background = key.selectBackgroundDrawable( + mKeyBackground, mFunctionalKeyBackground, mSpacebarBackground); + onDrawKeyBackground(key, canvas, background); } onDrawKeyTopVisuals(key, canvas, paint, params); @@ -338,17 +345,12 @@ public class KeyboardView extends View { final int bgHeight = key.getHeight() + padding.top + padding.bottom; final int bgX = -padding.left; final int bgY = -padding.top; - final int[] drawableState = key.getCurrentDrawableState(); - background.setState(drawableState); final Rect bounds = background.getBounds(); if (bgWidth != bounds.right || bgHeight != bounds.bottom) { background.setBounds(0, 0, bgWidth, bgHeight); } canvas.translate(bgX, bgY); background.draw(canvas); - if (LatinImeLogger.sVISUALDEBUG) { - drawRectangle(canvas, 0.0f, 0.0f, bgWidth, bgHeight, 0x80c00000, new Paint()); - } canvas.translate(-bgX, -bgY); } @@ -360,10 +362,6 @@ public class KeyboardView extends View { final float centerX = keyWidth * 0.5f; final float centerY = keyHeight * 0.5f; - if (LatinImeLogger.sVISUALDEBUG) { - drawRectangle(canvas, 0.0f, 0.0f, keyWidth, keyHeight, 0x800000c0, new Paint()); - } - // Draw key label. final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); float positionX = centerX; @@ -414,18 +412,23 @@ public class KeyboardView extends View { } } - paint.setColor(key.selectTextColor(params)); if (key.isEnabled()) { - // Set a drop shadow for the text - paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor); + paint.setColor(key.selectTextColor(params)); + // Set a drop shadow for the text if the shadow radius is positive value. + if (mKeyTextShadowRadius > 0.0f) { + paint.setShadowLayer(mKeyTextShadowRadius, 0.0f, 0.0f, params.mTextShadowColor); + } else { + paint.clearShadowLayer(); + } } else { // Make label invisible paint.setColor(Color.TRANSPARENT); + paint.clearShadowLayer(); } blendAlpha(paint, params.mAnimAlpha); canvas.drawText(label, 0, label.length(), positionX, baseline, paint); // Turn off drop shadow and reset x-scale. - paint.setShadowLayer(0.0f, 0.0f, 0.0f, Color.TRANSPARENT); + paint.clearShadowLayer(); paint.setTextScaleX(1.0f); if (icon != null) { @@ -440,12 +443,6 @@ public class KeyboardView extends View { drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); } } - - if (LatinImeLogger.sVISUALDEBUG) { - final Paint line = new Paint(); - drawHorizontalLine(canvas, baseline, keyWidth, 0xc0008000, line); - drawVerticalLine(canvas, positionX, keyHeight, 0xc0800080, line); - } } // Draw hint label. @@ -485,37 +482,28 @@ public class KeyboardView extends View { paint.setTextAlign(Align.CENTER); } canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint); - - if (LatinImeLogger.sVISUALDEBUG) { - final Paint line = new Paint(); - drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); - drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); - } } // Draw key icon. if (label == null && icon != null) { - final int iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); + final int iconWidth; + if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) { + iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio); + } else { + iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); + } final int iconHeight = icon.getIntrinsicHeight(); - final int iconX, alignX; - final int iconY = (keyHeight - iconHeight) / 2; + final int iconY = key.isAlignButtom() ? keyHeight - iconHeight + : (keyHeight - iconHeight) / 2; + final int iconX; if (key.isAlignLeft()) { iconX = mKeyLabelHorizontalPadding; - alignX = iconX; } else if (key.isAlignRight()) { iconX = keyWidth - mKeyLabelHorizontalPadding - iconWidth; - alignX = iconX + iconWidth; } else { // Align center iconX = (keyWidth - iconWidth) / 2; - alignX = iconX + iconWidth / 2; } drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); - - if (LatinImeLogger.sVISUALDEBUG) { - final Paint line = new Paint(); - drawVerticalLine(canvas, alignX, keyHeight, 0xc0800080, line); - drawRectangle(canvas, iconX, iconY, iconWidth, iconHeight, 0x80c00000, line); - } } if (key.hasPopupHint() && key.getMoreKeys() != null) { @@ -537,12 +525,6 @@ public class KeyboardView extends View { - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; final float hintY = keyHeight - mKeyPopupHintLetterPadding; canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); - - if (LatinImeLogger.sVISUALDEBUG) { - final Paint line = new Paint(); - drawHorizontalLine(canvas, (int)hintY, keyWidth, 0xc0808000, line); - drawVerticalLine(canvas, (int)hintX, keyHeight, 0xc0808000, line); - } } protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, @@ -553,32 +535,6 @@ public class KeyboardView extends View { canvas.translate(-x, -y); } - private static void drawHorizontalLine(final Canvas canvas, final float y, final float w, - final int color, final Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.drawLine(0.0f, y, w, y, paint); - } - - private static void drawVerticalLine(final Canvas canvas, final float x, final float h, - final int color, final Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.drawLine(x, 0.0f, x, h, paint); - } - - private static void drawRectangle(final Canvas canvas, final float x, final float y, - final float w, final float h, final int color, final Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.translate(x, y); - canvas.drawRect(0.0f, 0.0f, w, h, paint); - canvas.translate(-x, -y); - } - public Paint newLabelPaint(final Key key) { final Paint paint = new Paint(); paint.setAntiAlias(true); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index ecef8cc6c..9a859bfdb 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -27,7 +27,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.Log; @@ -39,7 +38,7 @@ import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; -import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; +import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate; import com.android.inputmethod.annotations.ExternallyReferenced; import com.android.inputmethod.keyboard.internal.DrawingHandler; import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView; @@ -53,29 +52,22 @@ import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; import com.android.inputmethod.keyboard.internal.SlidingKeyInputDrawingPreview; import com.android.inputmethod.keyboard.internal.TimerHandler; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.DebugSettings; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.SpacebarLanguageUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; -import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; -import com.android.inputmethod.research.ResearchLogger; import java.util.WeakHashMap; /** * A view that is responsible for detecting key presses and touch movements. * - * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled - * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor + * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor - * @attr ref R.styleable#MainKeyboardView_spacebarBackground * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator @@ -118,8 +110,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack /* Space key and its icon and background. */ private Key mSpaceKey; - private Drawable mSpacebarIcon; - private final Drawable mSpacebarBackground; // Stuff to draw language name on spacebar. private final int mLanguageOnSpacebarFinalAlpha; private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; @@ -129,14 +119,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final float mLanguageOnSpacebarTextRatio; private float mLanguageOnSpacebarTextSize; private final int mLanguageOnSpacebarTextColor; + private final float mLanguageOnSpacebarTextShadowRadius; private final int mLanguageOnSpacebarTextShadowColor; + private static final float LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; // The minimum x-scale to fit the language name on spacebar. private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f; - // Stuff to draw auto correction LED on spacebar. - private boolean mAutoCorrectionSpacebarLedOn; - private final boolean mAutoCorrectionSpacebarLedEnabled; - private final Drawable mAutoCorrectionSpacebarLedIcon; - private static final int SPACE_LED_LENGTH_PERCENT = 80; // Stuff to draw altCodeWhileTyping keys. private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; @@ -151,7 +138,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final SlidingKeyInputDrawingPreview mSlidingKeyInputDrawingPreview; // Key preview - private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false; private final KeyPreviewDrawParams mKeyPreviewDrawParams; private final KeyPreviewChoreographer mKeyPreviewChoreographer; @@ -159,8 +145,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final Paint mBackgroundDimAlphaPaint = new Paint(); private boolean mNeedsToDimEntireKeyboard; private final View mMoreKeysKeyboardContainer; - private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = - CollectionUtils.newWeakHashMap(); + private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>(); private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; // More keys panel (used by both more keys keyboard and more suggestions view) // TODO: Consider extending to support multiple more keys panels @@ -176,8 +161,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final TimerHandler mKeyTimerHandler; private final int mLanguageOnSpacebarHorizontalMargin; - private final DrawingHandler mDrawingHandler = - new DrawingHandler(this); + private final DrawingHandler mDrawingHandler = new DrawingHandler(this); + + private MainKeyboardAccessibilityDelegate mAccessibilityDelegate; public MainKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.mainKeyboardViewStyle); @@ -219,16 +205,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack R.styleable.MainKeyboardView_backgroundDimAlpha, 0); mBackgroundDimAlphaPaint.setColor(Color.BLACK); mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha); - mSpacebarBackground = mainKeyboardViewAttr.getDrawable( - R.styleable.MainKeyboardView_spacebarBackground); - mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean( - R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false); - mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable( - R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon); mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction( R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f); mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor( R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0); + mLanguageOnSpacebarTextShadowRadius = mainKeyboardViewAttr.getFloat( + R.styleable.MainKeyboardView_languageOnSpacebarTextShadowRadius, + LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED); mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor( R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0); mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt( @@ -395,18 +378,17 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mMoreKeysKeyboardCache.clear(); mSpaceKey = keyboard.getKey(Constants.CODE_SPACE); - mSpacebarIcon = (mSpaceKey != null) - ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null; final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio; - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final int orientation = getContext().getResources().getConfiguration().orientation; - ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation); - } - // This always needs to be set since the accessibility state can - // potentially change without the keyboard being set again. - AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard); + if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + if (mAccessibilityDelegate == null) { + mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector); + } + mAccessibilityDelegate.setKeyboard(keyboard); + } else { + mAccessibilityDelegate = null; + } } /** @@ -464,15 +446,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void showKeyPreview(final Key key) { - // If key is invalid or IME is already closed, we must not show key preview. - // Trying to show key preview while root window is closed causes - // WindowManager.BadTokenException. - if (key == null) { + // If the key is invalid or has no key preview, we must not show key preview. + if (key == null || key.noKeyPreview()) { return; } - - final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; final Keyboard keyboard = getKeyboard(); + if (keyboard == null) { + return; + } + final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; if (!previewParams.isPopupEnabled()) { previewParams.setVisibleOffset(-keyboard.mVerticalGap); return; @@ -549,6 +531,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } // Note that this method is called from a non-UI thread. + @SuppressWarnings("static-method") public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) { PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable); } @@ -565,24 +548,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack protected void onAttachedToWindow() { super.onAttachedToWindow(); installPreviewPlacerView(); - // Notify the ResearchLogger (development only diagnostics) that the keyboard view has - // been attached. This is needed to properly show the splash screen, which requires that - // the window token of the KeyboardView be non-null. - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this); - } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mDrawingPreviewPlacerView.removeAllViews(); - // Notify the ResearchLogger (development only diagnostics) that the keyboard view has - // been detached. This is needed to invalidate the reference of {@link MainKeyboardView} - // to null. - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow(); - } } private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) { @@ -618,9 +589,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (key == null) { return; } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.mainKeyboardView_onLongPress(); - } final KeyboardActionListener listener = mKeyboardActionListener; if (key.hasNoPanelAutoMoreKey()) { final int moreKeyCode = key.getMoreKeys()[0].mCode; @@ -733,14 +701,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } public boolean processMotionEvent(final MotionEvent me) { - if (LatinImeLogger.sUsabilityStudy) { - UsabilityStudyLogUtils.writeMotionEvent(me); - } - // Currently the same "move" event is being logged twice. - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.mainKeyboardView_processMotionEvent(me); - } - final int index = me.getActionIndex(); final int id = me.getPointerId(index); final PointerTracker tracker = PointerTracker.getPointerTracker(id); @@ -769,22 +729,23 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mMoreKeysKeyboardCache.clear(); } + public void onHideWindow() { + final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; + if (accessibilityDelegate != null) { + accessibilityDelegate.onHideWindow(); + } + } + /** - * Receives hover events from the input framework. - * - * @param event The motion event to be dispatched. - * @return {@code true} if the event was handled by the view, {@code false} - * otherwise + * {@inheritDoc} */ @Override - public boolean dispatchHoverEvent(final MotionEvent event) { - if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent( - event, mKeyDetector); + public boolean onHoverEvent(final MotionEvent event) { + final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; + if (accessibilityDelegate != null) { + return accessibilityDelegate.onHoverEvent(event); } - - // Reflection doesn't support calling superclass methods. - return false; + return super.onHoverEvent(event); } public void updateShortcutKey(final boolean available) { @@ -825,14 +786,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack invalidateKey(mSpaceKey); } - public void updateAutoCorrectionState(final boolean isAutoCorrection) { - if (!mAutoCorrectionSpacebarLedEnabled) { - return; - } - mAutoCorrectionSpacebarLedOn = isAutoCorrection; - invalidateKey(mSpaceKey); - } - private void dimEntireKeyboard(final boolean dimmed) { final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; mNeedsToDimEntireKeyboard = dimmed; @@ -851,42 +804,25 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } - // Draw key background. - @Override - protected void onDrawKeyBackground(final Key key, final Canvas canvas, - final Drawable background) { - if (key.getCode() == Constants.CODE_SPACE) { - super.onDrawKeyBackground(key, canvas, mSpacebarBackground); - return; - } - super.onDrawKeyBackground(key, canvas, background); - } - @Override protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params) { if (key.altCodeWhileTyping() && key.isEnabled()) { params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; } - // Don't draw key top letter when key preview is showing. - if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED - && mKeyPreviewChoreographer.isShowingKeyPreview(key)) { - // TODO: Fade out animation for the key top letter, and fade in animation for the key - // background color when the user presses the key. - return; - } + super.onDrawKeyTopVisuals(key, canvas, paint, params); final int code = key.getCode(); if (code == Constants.CODE_SPACE) { - drawSpacebar(key, canvas, paint); + // If input language are explicitly selected. + if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) { + drawLanguageOnSpacebar(key, canvas, paint); + } // Whether space key needs to show the "..." popup hint for special purposes if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) { drawKeyPopupHint(key, canvas, paint, params); } } else if (code == Constants.CODE_LANGUAGE_SWITCH) { - super.onDrawKeyTopVisuals(key, canvas, paint, params); drawKeyPopupHint(key, canvas, paint, params); - } else { - super.onDrawKeyTopVisuals(key, canvas, paint, params); } } @@ -926,43 +862,29 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return ""; } - private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) { + private void drawLanguageOnSpacebar(final Key key, final Canvas canvas, final Paint paint) { final int width = key.getWidth(); final int height = key.getHeight(); - - // If input language are explicitly selected. - if (mLanguageOnSpacebarFormatType != LanguageOnSpacebarHelper.FORMAT_TYPE_NONE) { - paint.setTextAlign(Align.CENTER); - paint.setTypeface(Typeface.DEFAULT); - paint.setTextSize(mLanguageOnSpacebarTextSize); - final InputMethodSubtype subtype = getKeyboard().mId.mSubtype; - final String language = layoutLanguageOnSpacebar(paint, subtype, width); - // Draw language text with shadow - final float descent = paint.descent(); - final float textHeight = -paint.ascent() + descent; - final float baseline = height / 2 + textHeight / 2; - paint.setColor(mLanguageOnSpacebarTextShadowColor); - paint.setAlpha(mLanguageOnSpacebarAnimAlpha); - canvas.drawText(language, width / 2, baseline - descent - 1, paint); - paint.setColor(mLanguageOnSpacebarTextColor); - paint.setAlpha(mLanguageOnSpacebarAnimAlpha); - canvas.drawText(language, width / 2, baseline - descent, paint); - } - - // Draw the spacebar icon at the bottom - if (mAutoCorrectionSpacebarLedOn) { - final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; - final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight); - } else if (mSpacebarIcon != null) { - final int iconWidth = mSpacebarIcon.getIntrinsicWidth(); - final int iconHeight = mSpacebarIcon.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - drawIcon(canvas, mSpacebarIcon, x, y, iconWidth, iconHeight); + paint.setTextAlign(Align.CENTER); + paint.setTypeface(Typeface.DEFAULT); + paint.setTextSize(mLanguageOnSpacebarTextSize); + final InputMethodSubtype subtype = getKeyboard().mId.mSubtype; + final String language = layoutLanguageOnSpacebar(paint, subtype, width); + // Draw language text with shadow + final float descent = paint.descent(); + final float textHeight = -paint.ascent() + descent; + final float baseline = height / 2 + textHeight / 2; + if (mLanguageOnSpacebarTextShadowRadius > 0.0f) { + paint.setShadowLayer(mLanguageOnSpacebarTextShadowRadius, 0, 0, + mLanguageOnSpacebarTextShadowColor); + } else { + paint.clearShadowLayer(); } + paint.setColor(mLanguageOnSpacebarTextColor); + paint.setAlpha(mLanguageOnSpacebarAnimAlpha); + canvas.drawText(language, width / 2, baseline - descent, paint); + paint.clearShadowLayer(); + paint.setTextScaleX(1.0f); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 65242dd76..0f575d30c 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -17,12 +17,13 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.res.Resources; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import com.android.inputmethod.accessibility.AccessibilityUtils; +import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -34,7 +35,7 @@ import com.android.inputmethod.latin.utils.CoordinateUtils; public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel { private final int[] mCoordinates = CoordinateUtils.newInstance(); - protected final KeyDetector mKeyDetector; + protected KeyDetector mKeyDetector; private Controller mController = EMPTY_CONTROLLER; protected KeyboardActionListener mListener; private int mOriginX; @@ -43,6 +44,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel private int mActivePointerId; + protected MoreKeysKeyboardAccessibilityDelegate mAccessibilityDelegate; + public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.moreKeysKeyboardViewStyle); } @@ -50,10 +53,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel public MoreKeysKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - - final Resources res = context.getResources(); - mKeyDetector = new MoreKeysDetector( - res.getDimension(R.dimen.config_more_keys_keyboard_slide_allowance)); + mKeyDetector = new MoreKeysDetector(getResources().getDimension( + R.dimen.config_more_keys_keyboard_slide_allowance)); } @Override @@ -71,8 +72,26 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel @Override public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); - mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), - -getPaddingTop() + getVerticalCorrection()); + if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { + // With accessibility mode on, any hover event outside {@link MoreKeysKeyboardView} is + // discarded at {@link InputView#dispatchHoverEvent(MotionEvent)}. Because only a hover + // event that is on this view is dispatched by the platform, we should use a + // {@link KeyDetector} that has no sliding allowance and no hysteresis. + if (mAccessibilityDelegate == null) { + mKeyDetector = new KeyDetector(); + mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate( + this, mKeyDetector); + mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard); + mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard); + } + mAccessibilityDelegate.setKeyboard(keyboard); + } else { + mKeyDetector = new MoreKeysDetector(getResources().getDimension( + R.dimen.config_more_keys_keyboard_slide_allowance)); + mAccessibilityDelegate = null; + } + mKeyDetector.setKeyboard( + keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); } @Override @@ -98,6 +117,10 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel mOriginX = x + container.getPaddingLeft(); mOriginY = y + container.getPaddingTop(); controller.onShowMoreKeysPanel(this); + final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; + if (accessibilityDelegate != null) { + accessibilityDelegate.onShowMoreKeysKeyboard(); + } } /** @@ -110,27 +133,33 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel @Override public void onDownEvent(final int x, final int y, final int pointerId, final long eventTime) { mActivePointerId = pointerId; - onMoveKeyInternal(x, y, pointerId); + mCurrentKey = detectKey(x, y, pointerId); } @Override - public void onMoveEvent(int x, int y, final int pointerId, long eventTime) { + public void onMoveEvent(final int x, final int y, final int pointerId, final long eventTime) { if (mActivePointerId != pointerId) { return; } final boolean hasOldKey = (mCurrentKey != null); - onMoveKeyInternal(x, y, pointerId); + mCurrentKey = detectKey(x, y, pointerId); if (hasOldKey && mCurrentKey == null) { - // If the pointer has moved too far away from any target then cancel the panel. + // A more keys keyboard is canceled when detecting no key. mController.onCancelMoreKeysPanel(); } } @Override public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) { - if (mCurrentKey != null && mActivePointerId == pointerId) { + if (mActivePointerId != pointerId) { + return; + } + // Calling {@link #detectKey(int,int,int)} here is harmless because the last move event and + // the following up event share the same coordinates. + mCurrentKey = detectKey(x, y, pointerId); + if (mCurrentKey != null) { updateReleaseKeyGraphics(mCurrentKey); - onCodeInput(mCurrentKey.getCode(), x, y); + onKeyInput(mCurrentKey, x, y); mCurrentKey = null; } } @@ -138,7 +167,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel /** * Performs the specific action for this panel when the user presses a key on the panel. */ - protected void onCodeInput(final int code, final int x, final int y) { + protected void onKeyInput(final Key key, final int x, final int y) { + final int code = key.getCode(); if (code == Constants.CODE_OUTPUT_TEXT) { mListener.onTextInput(mCurrentKey.getOutputText()); } else if (code != Constants.CODE_UNSPECIFIED) { @@ -151,23 +181,22 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel } } - private void onMoveKeyInternal(int x, int y, int pointerId) { - if (mActivePointerId != pointerId) { - // Ignore old pointers when newer pointer is active. - return; - } + private Key detectKey(int x, int y, int pointerId) { final Key oldKey = mCurrentKey; final Key newKey = mKeyDetector.detectHitKey(x, y); - if (newKey != oldKey) { - mCurrentKey = newKey; - invalidateKey(mCurrentKey); - if (oldKey != null) { - updateReleaseKeyGraphics(oldKey); - } - if (newKey != null) { - updatePressKeyGraphics(newKey); - } + if (newKey == oldKey) { + return newKey; } + // A new key is detected. + if (oldKey != null) { + updateReleaseKeyGraphics(oldKey); + invalidateKey(oldKey); + } + if (newKey != null) { + updatePressKeyGraphics(newKey); + invalidateKey(newKey); + } + return newKey; } private void updateReleaseKeyGraphics(final Key key) { @@ -222,6 +251,18 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel return true; } + /** + * {@inheritDoc} + */ + @Override + public boolean onHoverEvent(final MotionEvent event) { + final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; + if (accessibilityDelegate != null) { + return accessibilityDelegate.onHoverEvent(event); + } + return super.onHoverEvent(event); + } + private View getContainerView() { return (View)getParent(); } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 4777166ea..b6905bc1c 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -35,12 +35,9 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ResourceUtils; -import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; @@ -144,7 +141,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, // TODO: Device specific parameter would be better for device specific hack? private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth - private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList(); + private static final ArrayList<PointerTracker> sTrackers = new ArrayList<>(); private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue(); public final int mPointerId; @@ -336,10 +333,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, output, ignoreModifierKey ? " ignoreModifier" : "", altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled")); } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey, - altersCode, code); - } if (ignoreModifierKey) { return; } @@ -374,10 +367,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "", key.isEnabled() ? "": " disabled")); } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding, - ignoreModifierKey); - } if (ignoreModifierKey) { return; } @@ -397,9 +386,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onCancelInput", mPointerId)); } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.pointerTracker_callListenerOnCancelInput(); - } sListener.onCancelInput(); } @@ -690,10 +676,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element, private void onDownEvent(final int x, final int y, final long eventTime, final KeyDetector keyDetector) { + setKeyDetectorInner(keyDetector); if (DEBUG_EVENT) { printTouchEvent("onDownEvent:", x, y, eventTime); } - setKeyDetectorInner(keyDetector); // Naive up-to-down noise filter. final long deltaT = eventTime - mUpTime; if (deltaT < sParams.mTouchNoiseThresholdTime) { @@ -703,9 +689,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, Log.w(TAG, String.format("[%d] onDownEvent:" + " ignore potential noise: time=%d distance=%d", mPointerId, deltaT, distance)); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance); - } cancelTrackingForAction(); return; } @@ -877,10 +860,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element, lastX, lastY, Constants.printableCode(oldKey.getCode()), x, y, Constants.printableCode(key.getCode()))); } - // TODO: This should be moved to outside of this nested if-clause? - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY); - } onUpEventInternal(x, y, eventTime); onDownEventInternal(x, y, eventTime); } @@ -1054,8 +1033,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, final int translatedY = mMoreKeysPanel.translateY(y); mMoreKeysPanel.onUpEvent(translatedX, translatedY, mPointerId, eventTime); } - mMoreKeysPanel.dismissMoreKeysPanel(); - mMoreKeysPanel = null; + dismissMoreKeysPanel(); return; } @@ -1100,6 +1078,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element, mIsTrackingForActionDisabled = true; } + public boolean isInOperation() { + return !mIsTrackingForActionDisabled; + } + + public void cancelLongPressTimer() { + sTimerProxy.cancelLongPressTimerOf(this); + } + public void onLongPressed() { resetKeySelectionByDraggingFinger(); cancelTrackingForAction(); @@ -1122,10 +1108,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, sTimerProxy.cancelKeyTimersOf(this); setReleasedKeyGraphics(mCurrentKey); resetKeySelectionByDraggingFinger(); - if (isShowingMoreKeysPanel()) { - mMoreKeysPanel.dismissMoreKeysPanel(); - mMoreKeysPanel = null; - } + dismissMoreKeysPanel(); } private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime, diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index c89bda40e..c19cd671a 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -22,7 +22,6 @@ import android.util.Log; import com.android.inputmethod.keyboard.internal.TouchPositionCorrection; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; import java.util.ArrayList; @@ -55,6 +54,7 @@ public class ProximityInfo { private final List<Key>[] mGridNeighbors; private final String mLocaleStr; + @SuppressWarnings("unchecked") ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight, final int minWidth, final int height, final int mostCommonKeyWidth, final int mostCommonKeyHeight, final List<Key> sortedKeys, @@ -360,7 +360,7 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get for (int i = 0; i < gridSize; ++i) { final int indexStart = i * keyCount; final int indexEnd = indexStart + neighborCountPerCell[i]; - final ArrayList<Key> neighbors = CollectionUtils.newArrayList(indexEnd - indexStart); + final ArrayList<Key> neighbors = new ArrayList<>(indexEnd - indexStart); for (int index = indexStart; index < indexEnd; index++) { neighbors.add(neighborsFlatBuffer[index]); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java index 67a222732..daeb1f928 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/DynamicGridKeyboard.java @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard.internal; +package com.android.inputmethod.keyboard.emoji; import android.content.SharedPreferences; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.keyboard.EmojiPalettesView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JsonUtils; import java.util.ArrayDeque; @@ -36,7 +34,7 @@ import java.util.List; /** * This is a Keyboard class where you can add keys dynamically shown in a grid layout */ -public class DynamicGridKeyboard extends Keyboard { +final class DynamicGridKeyboard extends Keyboard { private static final String TAG = DynamicGridKeyboard.class.getSimpleName(); private static final int TEMPLATE_KEY_CODE_0 = 0x30; private static final int TEMPLATE_KEY_CODE_1 = 0x31; @@ -48,8 +46,8 @@ public class DynamicGridKeyboard extends Keyboard { private final int mColumnsNum; private final int mMaxKeyCount; private final boolean mIsRecents; - private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque(); - private final ArrayDeque<Key> mPendingKeys = CollectionUtils.newArrayDeque(); + private final ArrayDeque<GridKey> mGridKeys = new ArrayDeque<>(); + private final ArrayDeque<Key> mPendingKeys = new ArrayDeque<>(); private List<Key> mCachedGridKeys; @@ -62,7 +60,7 @@ public class DynamicGridKeyboard extends Keyboard { mVerticalStep = key0.getHeight() + mVerticalGap; mColumnsNum = mBaseWidth / mHorizontalStep; mMaxKeyCount = maxKeyCount; - mIsRecents = categoryId == EmojiPalettesView.CATEGORY_ID_RECENTS; + mIsRecents = categoryId == EmojiCategory.ID_RECENTS; mPrefs = prefs; } @@ -132,7 +130,7 @@ public class DynamicGridKeyboard extends Keyboard { } private void saveRecentKeys() { - final ArrayList<Object> keys = CollectionUtils.newArrayList(); + final ArrayList<Object> keys = new ArrayList<>(); for (final Key key : mGridKeys) { if (key.getOutputText() != null) { keys.add(key.getOutputText()); diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java new file mode 100644 index 000000000..512d4615d --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -0,0 +1,353 @@ +/* + * 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.emoji; + +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.os.Build; +import android.util.Log; +import android.util.Pair; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.settings.Settings; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +final class EmojiCategory { + private final String TAG = EmojiCategory.class.getSimpleName(); + + private static final int ID_UNSPECIFIED = -1; + public static final int ID_RECENTS = 0; + private static final int ID_PEOPLE = 1; + private static final int ID_OBJECTS = 2; + private static final int ID_NATURE = 3; + private static final int ID_PLACES = 4; + private static final int ID_SYMBOLS = 5; + private static final int ID_EMOTICONS = 6; + + public final class CategoryProperties { + public final int mCategoryId; + public final int mPageCount; + public CategoryProperties(final int categoryId, final int pageCount) { + mCategoryId = categoryId; + mPageCount = pageCount; + } + } + + private static final String[] sCategoryName = { + "recents", + "people", + "objects", + "nature", + "places", + "symbols", + "emoticons" }; + + private static final int[] sCategoryTabIconAttr = { + R.styleable.EmojiPalettesView_iconEmojiRecentsTab, + R.styleable.EmojiPalettesView_iconEmojiCategory1Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory2Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory3Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory4Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory5Tab, + R.styleable.EmojiPalettesView_iconEmojiCategory6Tab }; + + private static final int[] sAccessibilityDescriptionResourceIdsForCategories = { + R.string.spoken_descrption_emoji_category_recents, + R.string.spoken_descrption_emoji_category_people, + R.string.spoken_descrption_emoji_category_objects, + R.string.spoken_descrption_emoji_category_nature, + R.string.spoken_descrption_emoji_category_places, + R.string.spoken_descrption_emoji_category_symbols, + R.string.spoken_descrption_emoji_category_emoticons }; + + private static final int[] sCategoryElementId = { + KeyboardId.ELEMENT_EMOJI_RECENTS, + KeyboardId.ELEMENT_EMOJI_CATEGORY1, + KeyboardId.ELEMENT_EMOJI_CATEGORY2, + KeyboardId.ELEMENT_EMOJI_CATEGORY3, + KeyboardId.ELEMENT_EMOJI_CATEGORY4, + KeyboardId.ELEMENT_EMOJI_CATEGORY5, + KeyboardId.ELEMENT_EMOJI_CATEGORY6 }; + + private final SharedPreferences mPrefs; + private final Resources mRes; + private final int mMaxPageKeyCount; + private final KeyboardLayoutSet mLayoutSet; + private final HashMap<String, Integer> mCategoryNameToIdMap = new HashMap<>(); + private final int[] mCategoryTabIconId = new int[sCategoryName.length]; + private final ArrayList<CategoryProperties> mShownCategories = new ArrayList<>(); + private final ConcurrentHashMap<Long, DynamicGridKeyboard> mCategoryKeyboardMap = + new ConcurrentHashMap<>(); + + private int mCurrentCategoryId = EmojiCategory.ID_UNSPECIFIED; + private int mCurrentCategoryPageId = 0; + + public EmojiCategory(final SharedPreferences prefs, final Resources res, + final KeyboardLayoutSet layoutSet, final TypedArray emojiPaletteViewAttr) { + mPrefs = prefs; + mRes = res; + mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count); + mLayoutSet = layoutSet; + for (int i = 0; i < sCategoryName.length; ++i) { + mCategoryNameToIdMap.put(sCategoryName[i], i); + mCategoryTabIconId[i] = emojiPaletteViewAttr.getResourceId( + sCategoryTabIconAttr[i], 0); + } + addShownCategoryId(EmojiCategory.ID_RECENTS); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 + || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KeyLimePie") + || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KitKat")) { + addShownCategoryId(EmojiCategory.ID_PEOPLE); + addShownCategoryId(EmojiCategory.ID_OBJECTS); + addShownCategoryId(EmojiCategory.ID_NATURE); + addShownCategoryId(EmojiCategory.ID_PLACES); + mCurrentCategoryId = + Settings.readLastShownEmojiCategoryId(mPrefs, EmojiCategory.ID_PEOPLE); + } else { + mCurrentCategoryId = + Settings.readLastShownEmojiCategoryId(mPrefs, EmojiCategory.ID_SYMBOLS); + } + addShownCategoryId(EmojiCategory.ID_SYMBOLS); + addShownCategoryId(EmojiCategory.ID_EMOTICONS); + getKeyboard(EmojiCategory.ID_RECENTS, 0 /* cagetoryPageId */) + .loadRecentKeys(mCategoryKeyboardMap.values()); + } + + private void addShownCategoryId(final int categoryId) { + // Load a keyboard of categoryId + getKeyboard(categoryId, 0 /* cagetoryPageId */); + final CategoryProperties properties = + new CategoryProperties(categoryId, getCategoryPageCount(categoryId)); + mShownCategories.add(properties); + } + + public String getCategoryName(final int categoryId, final int categoryPageId) { + return sCategoryName[categoryId] + "-" + categoryPageId; + } + + public int getCategoryId(final String name) { + final String[] strings = name.split("-"); + return mCategoryNameToIdMap.get(strings[0]); + } + + public int getCategoryTabIcon(final int categoryId) { + return mCategoryTabIconId[categoryId]; + } + + public String getAccessibilityDescription(final int categoryId) { + return mRes.getString(sAccessibilityDescriptionResourceIdsForCategories[categoryId]); + } + + public ArrayList<CategoryProperties> getShownCategories() { + return mShownCategories; + } + + public int getCurrentCategoryId() { + return mCurrentCategoryId; + } + + public int getCurrentCategoryPageSize() { + return getCategoryPageSize(mCurrentCategoryId); + } + + public int getCategoryPageSize(final int categoryId) { + for (final CategoryProperties prop : mShownCategories) { + if (prop.mCategoryId == categoryId) { + return prop.mPageCount; + } + } + Log.w(TAG, "Invalid category id: " + categoryId); + // Should not reach here. + return 0; + } + + public void setCurrentCategoryId(final int categoryId) { + mCurrentCategoryId = categoryId; + Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId); + } + + public void setCurrentCategoryPageId(final int id) { + mCurrentCategoryPageId = id; + } + + public int getCurrentCategoryPageId() { + return mCurrentCategoryPageId; + } + + public void saveLastTypedCategoryPage() { + Settings.writeLastTypedEmojiCategoryPageId( + mPrefs, mCurrentCategoryId, mCurrentCategoryPageId); + } + + public boolean isInRecentTab() { + return mCurrentCategoryId == EmojiCategory.ID_RECENTS; + } + + public int getTabIdFromCategoryId(final int categoryId) { + for (int i = 0; i < mShownCategories.size(); ++i) { + if (mShownCategories.get(i).mCategoryId == categoryId) { + return i; + } + } + Log.w(TAG, "categoryId not found: " + categoryId); + return 0; + } + + // Returns the view pager's page position for the categoryId + public int getPageIdFromCategoryId(final int categoryId) { + final int lastSavedCategoryPageId = + Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId); + int sum = 0; + for (int i = 0; i < mShownCategories.size(); ++i) { + final CategoryProperties props = mShownCategories.get(i); + if (props.mCategoryId == categoryId) { + return sum + lastSavedCategoryPageId; + } + sum += props.mPageCount; + } + Log.w(TAG, "categoryId not found: " + categoryId); + return 0; + } + + public int getRecentTabId() { + return getTabIdFromCategoryId(EmojiCategory.ID_RECENTS); + } + + private int getCategoryPageCount(final int categoryId) { + final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); + return (keyboard.getSortedKeys().size() - 1) / mMaxPageKeyCount + 1; + } + + // Returns a pair of the category id and the category page id from the view pager's page + // position. The category page id is numbered in each category. And the view page position + // is the position of the current shown page in the view pager which contains all pages of + // all categories. + public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) { + int sum = 0; + for (final CategoryProperties properties : mShownCategories) { + final int temp = sum; + sum += properties.mPageCount; + if (sum > position) { + return new Pair<>(properties.mCategoryId, position - temp); + } + } + return null; + } + + // Returns a keyboard from the view pager's page position. + public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) { + final Pair<Integer, Integer> categoryAndId = + getCategoryIdAndPageIdFromPagePosition(position); + if (categoryAndId != null) { + return getKeyboard(categoryAndId.first, categoryAndId.second); + } + return null; + } + + private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) { + return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; + } + + public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) { + synchronized (mCategoryKeyboardMap) { + final Long categotyKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id); + if (mCategoryKeyboardMap.containsKey(categotyKeyboardMapKey)) { + return mCategoryKeyboardMap.get(categotyKeyboardMapKey); + } + + if (categoryId == EmojiCategory.ID_RECENTS) { + final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId); + mCategoryKeyboardMap.put(categotyKeyboardMapKey, kbd); + return kbd; + } + + final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); + final Key[][] sortedKeys = sortKeysIntoPages( + keyboard.getSortedKeys(), mMaxPageKeyCount); + for (int pageId = 0; pageId < sortedKeys.length; ++pageId) { + final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId); + for (final Key emojiKey : sortedKeys[pageId]) { + if (emojiKey == null) { + break; + } + tempKeyboard.addKeyLast(emojiKey); + } + mCategoryKeyboardMap.put( + getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard); + } + return mCategoryKeyboardMap.get(categotyKeyboardMapKey); + } + } + + public int getTotalPageCountOfAllCategories() { + int sum = 0; + for (CategoryProperties properties : mShownCategories) { + sum += properties.mPageCount; + } + return sum; + } + + private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() { + @Override + public int compare(final Key lhs, final Key rhs) { + final Rect lHitBox = lhs.getHitBox(); + final Rect rHitBox = rhs.getHitBox(); + if (lHitBox.top < rHitBox.top) { + return -1; + } else if (lHitBox.top > rHitBox.top) { + return 1; + } + if (lHitBox.left < rHitBox.left) { + return -1; + } else if (lHitBox.left > rHitBox.left) { + return 1; + } + if (lhs.getCode() == rhs.getCode()) { + return 0; + } + return lhs.getCode() < rhs.getCode() ? -1 : 1; + } + }; + + private static Key[][] sortKeysIntoPages(final List<Key> inKeys, final int maxPageCount) { + final ArrayList<Key> keys = new ArrayList<>(inKeys); + Collections.sort(keys, EMOJI_KEY_COMPARATOR); + final int pageCount = (keys.size() - 1) / maxPageCount + 1; + final Key[][] retval = new Key[pageCount][maxPageCount]; + for (int i = 0; i < keys.size(); ++i) { + retval[i / maxPageCount][i % maxPageCount] = keys.get(i); + } + return retval; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java index d56a3cf25..43d62c71a 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategoryPageIndicatorView.java @@ -14,31 +14,33 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; - -import com.android.inputmethod.latin.R; +package com.android.inputmethod.keyboard.emoji; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; -import android.widget.LinearLayout; +import android.view.View; -public class EmojiCategoryPageIndicatorView extends LinearLayout { - private static final float BOTTOM_MARGIN_RATIO = 0.66f; +public final class EmojiCategoryPageIndicatorView extends View { + private static final float BOTTOM_MARGIN_RATIO = 1.0f; private final Paint mPaint = new Paint(); private int mCategoryPageSize = 0; private int mCurrentCategoryPageId = 0; private float mOffset = 0.0f; - public EmojiCategoryPageIndicatorView(final Context context) { - this(context, null /* attrs */); + public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) { + this(context, attrs, 0); } - public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) { - super(context, attrs); - mPaint.setColor(context.getResources().getColor( - R.color.emoji_category_page_id_view_foreground)); + public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs, + final int defStyle) { + super(context, attrs, defStyle); + } + + public void setColors(final int foregroundColor, final int backgroundColor) { + mPaint.setColor(foregroundColor); + setBackgroundColor(backgroundColor); } public void setCategoryPageId(final int size, final int id, final float offset) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java index d57ea5a94..582e09124 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiLayoutParams.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard.internal; - -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.ResourceUtils; +package com.android.inputmethod.keyboard.emoji; import android.content.res.Resources; import android.support.v4.view.ViewPager; -import android.widget.ImageView; +import android.view.View; import android.widget.LinearLayout; -public class EmojiLayoutParams { +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.ResourceUtils; + +final class EmojiLayoutParams { private static final int DEFAULT_KEYBOARD_ROWS = 4; public final int mEmojiPagerHeight; @@ -67,10 +67,10 @@ public class EmojiLayoutParams { vp.setLayoutParams(lp); } - public void setCategoryPageIdViewProperties(final LinearLayout ll) { - final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); + public void setCategoryPageIdViewProperties(final View v) { + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams(); lp.height = mEmojiCategoryPageIdViewHeight; - ll.setLayoutParams(lp); + v.setLayoutParams(lp); } public int getActionBarHeight() { @@ -83,10 +83,10 @@ public class EmojiLayoutParams { ll.setLayoutParams(lp); } - public void setKeyProperties(final ImageView ib) { - final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ib.getLayoutParams(); + public void setKeyProperties(final View v) { + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) v.getLayoutParams(); lp.leftMargin = mKeyHorizontalGap / 2; lp.rightMargin = mKeyHorizontalGap / 2; - ib.setLayoutParams(lp); + v.setLayoutParams(lp); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java index e175a051e..80ba60c82 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard.internal; +package com.android.inputmethod.keyboard.emoji; import android.content.Context; import android.os.Handler; @@ -22,19 +22,20 @@ import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; +import com.android.inputmethod.accessibility.AccessibilityUtils; +import com.android.inputmethod.accessibility.KeyboardAccessibilityDelegate; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.R; /** * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard. - * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support. + * Multi-touch unsupported. No gesture support. */ // TODO: Implement key popup preview. -public final class EmojiPageKeyboardView extends KeyboardView implements +final class EmojiPageKeyboardView extends KeyboardView implements GestureDetector.OnGestureListener { private static final long KEY_PRESS_DELAY_TIME = 250; // msec private static final long KEY_RELEASE_DELAY_TIME = 30; // msec @@ -54,6 +55,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements private OnKeyEventListener mListener = EMPTY_LISTENER; private final KeyDetector mKeyDetector = new KeyDetector(); private final GestureDetector mGestureDetector; + private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate; public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.keyboardViewStyle); @@ -78,6 +80,27 @@ public final class EmojiPageKeyboardView extends KeyboardView implements public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */); + if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + if (mAccessibilityDelegate == null) { + mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector); + } + mAccessibilityDelegate.setKeyboard(keyboard); + } else { + mAccessibilityDelegate = null; + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onHoverEvent(final MotionEvent event) { + final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate = + mAccessibilityDelegate; + if (accessibilityDelegate != null) { + return accessibilityDelegate.onHoverEvent(event); + } + return super.onHoverEvent(event); } /** @@ -90,7 +113,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements } final Key key = getKey(e); if (key != null && key != mCurrentKey) { - releaseCurrentKey(); + releaseCurrentKey(false /* withKeyRegistering */); } return true; } @@ -107,7 +130,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements return mKeyDetector.detectHitKey(x, y); } - public void releaseCurrentKey() { + public void releaseCurrentKey(final boolean withKeyRegistering) { mHandler.removeCallbacks(mPendingKeyDown); mPendingKeyDown = null; final Key currentKey = mCurrentKey; @@ -116,13 +139,16 @@ public final class EmojiPageKeyboardView extends KeyboardView implements } currentKey.onReleased(); invalidateKey(currentKey); + if (withKeyRegistering) { + mListener.onReleaseKey(currentKey); + } mCurrentKey = null; } @Override public boolean onDown(final MotionEvent e) { final Key key = getKey(e); - releaseCurrentKey(); + releaseCurrentKey(false /* withKeyRegistering */); mCurrentKey = key; if (key == null) { return false; @@ -151,7 +177,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements final Key key = getKey(e); final Runnable pendingKeyDown = mPendingKeyDown; final Key currentKey = mCurrentKey; - releaseCurrentKey(); + releaseCurrentKey(false /* withKeyRegistering */); if (key == null) { return false; } @@ -177,14 +203,14 @@ public final class EmojiPageKeyboardView extends KeyboardView implements @Override public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX, final float distanceY) { - releaseCurrentKey(); + releaseCurrentKey(false /* withKeyRegistering */); return false; } @Override public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) { - releaseCurrentKey(); + releaseCurrentKey(false /* withKeyRegistering */); return false; } diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java new file mode 100644 index 000000000..68056e0eb --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesAdapter.java @@ -0,0 +1,149 @@ +/* + * 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.emoji; + +import android.support.v4.view.PagerAdapter; +import android.util.Log; +import android.util.SparseArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.latin.R; + +final class EmojiPalettesAdapter extends PagerAdapter { + private static final String TAG = EmojiPalettesAdapter.class.getSimpleName(); + private static final boolean DEBUG_PAGER = false; + + private final EmojiPageKeyboardView.OnKeyEventListener mListener; + private final DynamicGridKeyboard mRecentsKeyboard; + private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = new SparseArray<>(); + private final EmojiCategory mEmojiCategory; + private int mActivePosition = 0; + + public EmojiPalettesAdapter(final EmojiCategory emojiCategory, + final EmojiPageKeyboardView.OnKeyEventListener listener) { + mEmojiCategory = emojiCategory; + mListener = listener; + mRecentsKeyboard = mEmojiCategory.getKeyboard(EmojiCategory.ID_RECENTS, 0); + } + + public void flushPendingRecentKeys() { + mRecentsKeyboard.flushPendingRecentKeys(); + final KeyboardView recentKeyboardView = + mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId()); + if (recentKeyboardView != null) { + recentKeyboardView.invalidateAllKeys(); + } + } + + public void addRecentKey(final Key key) { + if (mEmojiCategory.isInRecentTab()) { + mRecentsKeyboard.addPendingKey(key); + return; + } + mRecentsKeyboard.addKeyFirst(key); + final KeyboardView recentKeyboardView = + mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId()); + if (recentKeyboardView != null) { + recentKeyboardView.invalidateAllKeys(); + } + } + + public void onPageScrolled() { + releaseCurrentKey(false /* withKeyRegistering */); + } + + public void releaseCurrentKey(final boolean withKeyRegistering) { + // Make sure the delayed key-down event (highlight effect and haptic feedback) will be + // canceled. + final EmojiPageKeyboardView currentKeyboardView = + mActiveKeyboardViews.get(mActivePosition); + if (currentKeyboardView == null) { + return; + } + currentKeyboardView.releaseCurrentKey(withKeyRegistering); + } + + @Override + public int getCount() { + return mEmojiCategory.getTotalPageCountOfAllCategories(); + } + + @Override + public void setPrimaryItem(final ViewGroup container, final int position, + final Object object) { + if (mActivePosition == position) { + return; + } + final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition); + if (oldKeyboardView != null) { + oldKeyboardView.releaseCurrentKey(false /* withKeyRegistering */); + oldKeyboardView.deallocateMemory(); + } + mActivePosition = position; + } + + @Override + public Object instantiateItem(final ViewGroup container, final int position) { + if (DEBUG_PAGER) { + Log.d(TAG, "instantiate item: " + position); + } + final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position); + if (oldKeyboardView != null) { + oldKeyboardView.deallocateMemory(); + // This may be redundant but wanted to be safer.. + mActiveKeyboardViews.remove(position); + } + final Keyboard keyboard = + mEmojiCategory.getKeyboardFromPagePosition(position); + final LayoutInflater inflater = LayoutInflater.from(container.getContext()); + final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate( + R.layout.emoji_keyboard_page, container, false /* attachToRoot */); + keyboardView.setKeyboard(keyboard); + keyboardView.setOnKeyEventListener(mListener); + container.addView(keyboardView); + mActiveKeyboardViews.put(position, keyboardView); + return keyboardView; + } + + @Override + public boolean isViewFromObject(final View view, final Object object) { + return view == object; + } + + @Override + public void destroyItem(final ViewGroup container, final int position, + final Object object) { + if (DEBUG_PAGER) { + Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName()); + } + final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position); + if (keyboardView != null) { + keyboardView.deallocateMemory(); + mActiveKeyboardViews.remove(position); + } + if (object instanceof View) { + container.removeView((View)object); + } else { + Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java new file mode 100644 index 000000000..e37cd2369 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -0,0 +1,560 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.emoji; + +import static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.os.CountDownTimer; +import android.preference.PreferenceManager; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.Pair; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TabHost; +import android.widget.TabHost.OnTabChangeListener; +import android.widget.TabWidget; +import android.widget.TextView; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.internal.KeyDrawParams; +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.Constants; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.SubtypeSwitcher; +import com.android.inputmethod.latin.utils.ResourceUtils; + +import java.util.concurrent.TimeUnit; + +/** + * View class to implement Emoji palettes. + * The Emoji keyboard consists of group of views layout/emoji_palettes_view. + * <ol> + * <li> Emoji category tabs. + * <li> Delete button. + * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab. + * <li> Back to main keyboard button and enter button. + * </ol> + * Because of the above reasons, this class doesn't extend {@link KeyboardView}. + */ +public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener, + ViewPager.OnPageChangeListener, View.OnClickListener, View.OnTouchListener, + EmojiPageKeyboardView.OnKeyEventListener { + private final int mFunctionalKeyBackgroundId; + private final int mSpacebarBackgroundId; + private final boolean mCategoryIndicatorEnabled; + private final int mCategoryIndicatorDrawableResId; + private final int mCategoryIndicatorBackgroundResId; + private final int mCategoryPageIndicatorColor; + private final int mCategoryPageIndicatorBackground; + private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener; + private EmojiPalettesAdapter mEmojiPalettesAdapter; + private final EmojiLayoutParams mEmojiLayoutParams; + + private ImageButton mDeleteKey; + private TextView mAlphabetKeyLeft; + private TextView mAlphabetKeyRight; + private View mSpacebar; + // TODO: Remove this workaround. + private View mSpacebarIcon; + private TabHost mTabHost; + private ViewPager mEmojiPager; + private int mCurrentPagerPosition = 0; + private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView; + + private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; + + private final EmojiCategory mEmojiCategory; + + public EmojiPalettesView(final Context context, final AttributeSet attrs) { + this(context, attrs, R.attr.emojiPalettesViewStyle); + } + + public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs, + R.styleable.KeyboardView, defStyle, R.style.KeyboardView); + final int keyBackgroundId = keyboardViewAttr.getResourceId( + R.styleable.KeyboardView_keyBackground, 0); + mFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId( + R.styleable.KeyboardView_functionalKeyBackground, keyBackgroundId); + mSpacebarBackgroundId = keyboardViewAttr.getResourceId( + R.styleable.KeyboardView_spacebarBackground, keyBackgroundId); + keyboardViewAttr.recycle(); + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( + context, null /* editorInfo */); + final Resources res = context.getResources(); + mEmojiLayoutParams = new EmojiLayoutParams(res); + builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); + builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), + mEmojiLayoutParams.mEmojiKeyboardHeight); + final KeyboardLayoutSet layoutSet = builder.build(); + final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs, + R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView); + mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context), + res, layoutSet, emojiPalettesViewAttr); + mCategoryIndicatorEnabled = emojiPalettesViewAttr.getBoolean( + R.styleable.EmojiPalettesView_categoryIndicatorEnabled, false); + mCategoryIndicatorDrawableResId = emojiPalettesViewAttr.getResourceId( + R.styleable.EmojiPalettesView_categoryIndicatorDrawable, 0); + mCategoryIndicatorBackgroundResId = emojiPalettesViewAttr.getResourceId( + R.styleable.EmojiPalettesView_categoryIndicatorBackground, 0); + mCategoryPageIndicatorColor = emojiPalettesViewAttr.getColor( + R.styleable.EmojiPalettesView_categoryPageIndicatorColor, 0); + mCategoryPageIndicatorBackground = emojiPalettesViewAttr.getColor( + R.styleable.EmojiPalettesView_categoryPageIndicatorBackground, 0); + emojiPalettesViewAttr.recycle(); + mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context); + } + + @Override + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + final Resources res = getContext().getResources(); + // The main keyboard expands to the entire this {@link KeyboardView}. + final int width = ResourceUtils.getDefaultKeyboardWidth(res) + + getPaddingLeft() + getPaddingRight(); + final int height = ResourceUtils.getDefaultKeyboardHeight(res) + + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) + + getPaddingTop() + getPaddingBottom(); + setMeasuredDimension(width, height); + } + + private void addTab(final TabHost host, final int categoryId) { + final String tabId = mEmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */); + final TabHost.TabSpec tspec = host.newTabSpec(tabId); + tspec.setContent(R.id.emoji_keyboard_dummy); + final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate( + R.layout.emoji_keyboard_tab_icon, null); + iconView.setImageResource(mEmojiCategory.getCategoryTabIcon(categoryId)); + iconView.setContentDescription(mEmojiCategory.getAccessibilityDescription(categoryId)); + tspec.setIndicator(iconView); + host.addTab(tspec); + } + + @Override + protected void onFinishInflate() { + mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost); + mTabHost.setup(); + for (final EmojiCategory.CategoryProperties properties + : mEmojiCategory.getShownCategories()) { + addTab(mTabHost, properties.mCategoryId); + } + mTabHost.setOnTabChangedListener(this); + final TabWidget tabWidget = mTabHost.getTabWidget(); + tabWidget.setStripEnabled(mCategoryIndicatorEnabled); + if (mCategoryIndicatorEnabled) { + // On TabWidget's strip, what looks like an indicator is actually a background. + // And what looks like a background are actually left and right drawables. + tabWidget.setBackgroundResource(mCategoryIndicatorDrawableResId); + tabWidget.setLeftStripDrawable(mCategoryIndicatorBackgroundResId); + tabWidget.setRightStripDrawable(mCategoryIndicatorBackgroundResId); + } + + mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this); + + mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager); + mEmojiPager.setAdapter(mEmojiPalettesAdapter); + mEmojiPager.setOnPageChangeListener(this); + mEmojiPager.setOffscreenPageLimit(0); + mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE); + mEmojiLayoutParams.setPagerProperties(mEmojiPager); + + mEmojiCategoryPageIndicatorView = + (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view); + mEmojiCategoryPageIndicatorView.setColors( + mCategoryPageIndicatorColor, mCategoryPageIndicatorBackground); + mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); + + setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); + + final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); + mEmojiLayoutParams.setActionBarProperties(actionBar); + + // deleteKey depends only on OnTouchListener. + mDeleteKey = (ImageButton)findViewById(R.id.emoji_keyboard_delete); + mDeleteKey.setBackgroundResource(mFunctionalKeyBackgroundId); + mDeleteKey.setTag(Constants.CODE_DELETE); + mDeleteKey.setOnTouchListener(mDeleteKeyOnTouchListener); + + // {@link #mAlphabetKeyLeft}, {@link #mAlphabetKeyRight, and spaceKey depend on + // {@link View.OnClickListener} as well as {@link View.OnTouchListener}. + // {@link View.OnTouchListener} is used as the trigger of key-press, while + // {@link View.OnClickListener} is used as the trigger of key-release which does not occur + // if the event is canceled by moving off the finger from the view. + // The text on alphabet keys are set at + // {@link #startEmojiPalettes(String,int,float,Typeface)}. + mAlphabetKeyLeft = (TextView)findViewById(R.id.emoji_keyboard_alphabet_left); + mAlphabetKeyLeft.setBackgroundResource(mFunctionalKeyBackgroundId); + mAlphabetKeyLeft.setTag(Constants.CODE_ALPHA_FROM_EMOJI); + mAlphabetKeyLeft.setOnTouchListener(this); + mAlphabetKeyLeft.setOnClickListener(this); + mAlphabetKeyRight = (TextView)findViewById(R.id.emoji_keyboard_alphabet_right); + mAlphabetKeyRight.setBackgroundResource(mFunctionalKeyBackgroundId); + mAlphabetKeyRight.setTag(Constants.CODE_ALPHA_FROM_EMOJI); + mAlphabetKeyRight.setOnTouchListener(this); + mAlphabetKeyRight.setOnClickListener(this); + mSpacebar = findViewById(R.id.emoji_keyboard_space); + mSpacebar.setBackgroundResource(mSpacebarBackgroundId); + mSpacebar.setTag(Constants.CODE_SPACE); + mSpacebar.setOnTouchListener(this); + mSpacebar.setOnClickListener(this); + mEmojiLayoutParams.setKeyProperties(mSpacebar); + mSpacebarIcon = findViewById(R.id.emoji_keyboard_space_icon); + } + + @Override + public boolean dispatchTouchEvent(final MotionEvent ev) { + // Add here to the stack trace to nail down the {@link IllegalArgumentException} exception + // in MotionEvent that sporadically happens. + // TODO: Remove this override method once the issue has been addressed. + return super.dispatchTouchEvent(ev); + } + + @Override + public void onTabChanged(final String tabId) { + AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback( + Constants.CODE_UNSPECIFIED, this); + final int categoryId = mEmojiCategory.getCategoryId(tabId); + setCurrentCategoryId(categoryId, false /* force */); + updateEmojiCategoryPageIdView(); + } + + @Override + public void onPageSelected(final int position) { + final Pair<Integer, Integer> newPos = + mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); + setCurrentCategoryId(newPos.first /* categoryId */, false /* force */); + mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */); + updateEmojiCategoryPageIdView(); + mCurrentPagerPosition = position; + } + + @Override + public void onPageScrollStateChanged(final int state) { + // Ignore this message. Only want the actual page selected. + } + + @Override + public void onPageScrolled(final int position, final float positionOffset, + final int positionOffsetPixels) { + mEmojiPalettesAdapter.onPageScrolled(); + final Pair<Integer, Integer> newPos = + mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); + final int newCategoryId = newPos.first; + final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId); + final int currentCategoryId = mEmojiCategory.getCurrentCategoryId(); + final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId(); + final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize(); + if (newCategoryId == currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + newCategorySize, newPos.second, positionOffset); + } else if (newCategoryId > currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + currentCategorySize, currentCategoryPageId, positionOffset); + } else if (newCategoryId < currentCategoryId) { + mEmojiCategoryPageIndicatorView.setCategoryPageId( + currentCategorySize, currentCategoryPageId, positionOffset - 1); + } + } + + /** + * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnTouchListener} + * interface to handle touch events from View-based elements such as the space bar. + * Note that this method is used only for observing {@link MotionEvent#ACTION_DOWN} to trigger + * {@link KeyboardActionListener#onPressKey}. {@link KeyboardActionListener#onReleaseKey} will + * be covered by {@link #onClick} as long as the event is not canceled. + */ + @Override + public boolean onTouch(final View v, final MotionEvent event) { + if (event.getActionMasked() != MotionEvent.ACTION_DOWN) { + return false; + } + final Object tag = v.getTag(); + if (!(tag instanceof Integer)) { + return false; + } + final int code = (Integer) tag; + mKeyboardActionListener.onPressKey( + code, 0 /* repeatCount */, true /* isSinglePointer */); + // It's important to return false here. Otherwise, {@link #onClick} and touch-down visual + // feedback stop working. + return false; + } + + /** + * Called from {@link EmojiPageKeyboardView} through {@link android.view.View.OnClickListener} + * interface to handle non-canceled touch-up events from View-based elements such as the space + * bar. + */ + @Override + public void onClick(View v) { + final Object tag = v.getTag(); + if (!(tag instanceof Integer)) { + return; + } + final int code = (Integer) tag; + mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE, + false /* isKeyRepeat */); + mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); + } + + /** + * Called from {@link EmojiPageKeyboardView} through + * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener} + * interface to handle touch events from non-View-based elements such as Emoji buttons. + */ + @Override + public void onPressKey(final Key key) { + final int code = key.getCode(); + mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */); + } + + /** + * Called from {@link EmojiPageKeyboardView} through + * {@link com.android.inputmethod.keyboard.emoji.EmojiPageKeyboardView.OnKeyEventListener} + * interface to handle touch events from non-View-based elements such as Emoji buttons. + */ + @Override + public void onReleaseKey(final Key key) { + mEmojiPalettesAdapter.addRecentKey(key); + mEmojiCategory.saveLastTypedCategoryPage(); + final int code = key.getCode(); + if (code == Constants.CODE_OUTPUT_TEXT) { + mKeyboardActionListener.onTextInput(key.getOutputText()); + } else { + mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE, + false /* isKeyRepeat */); + } + mKeyboardActionListener.onReleaseKey(code, false /* withSliding */); + } + + public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) { + if (!enabled) return; + // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off? + setLayerType(LAYER_TYPE_HARDWARE, null); + } + + private static void setupAlphabetKey(final TextView alphabetKey, final String label, + final KeyDrawParams params) { + alphabetKey.setText(label); + alphabetKey.setTextColor(params.mFunctionalTextColor); + alphabetKey.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mLabelSize); + alphabetKey.setTypeface(params.mTypeface); + } + + public void startEmojiPalettes(final String switchToAlphaLabel, + final KeyVisualAttributes keyVisualAttr, final KeyboardIconsSet iconSet) { + final int deleteIconResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_DELETE_KEY); + if (deleteIconResId != 0) { + mDeleteKey.setImageResource(deleteIconResId); + } + final int spacebarResId = iconSet.getIconResourceId(KeyboardIconsSet.NAME_SPACE_KEY); + if (spacebarResId != 0) { + // TODO: Remove this workaround to place the spacebar icon. + mSpacebarIcon.setBackgroundResource(spacebarResId); + } + final KeyDrawParams params = new KeyDrawParams(); + params.updateParams(mEmojiLayoutParams.getActionBarHeight(), keyVisualAttr); + setupAlphabetKey(mAlphabetKeyLeft, switchToAlphaLabel, params); + setupAlphabetKey(mAlphabetKeyRight, switchToAlphaLabel, params); + mEmojiPager.setAdapter(mEmojiPalettesAdapter); + mEmojiPager.setCurrentItem(mCurrentPagerPosition); + } + + public void stopEmojiPalettes() { + mEmojiPalettesAdapter.releaseCurrentKey(true /* withKeyRegistering */); + mEmojiPalettesAdapter.flushPendingRecentKeys(); + mEmojiPager.setAdapter(null); + } + + public void setKeyboardActionListener(final KeyboardActionListener listener) { + mKeyboardActionListener = listener; + mDeleteKeyOnTouchListener.setKeyboardActionListener(mKeyboardActionListener); + } + + private void updateEmojiCategoryPageIdView() { + if (mEmojiCategoryPageIndicatorView == null) { + return; + } + mEmojiCategoryPageIndicatorView.setCategoryPageId( + mEmojiCategory.getCurrentCategoryPageSize(), + mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */); + } + + private void setCurrentCategoryId(final int categoryId, final boolean force) { + final int oldCategoryId = mEmojiCategory.getCurrentCategoryId(); + if (oldCategoryId == categoryId && !force) { + return; + } + + if (oldCategoryId == EmojiCategory.ID_RECENTS) { + // Needs to save pending updates for recent keys when we get out of the recents + // category because we don't want to move the recent emojis around while the user + // is in the recents category. + mEmojiPalettesAdapter.flushPendingRecentKeys(); + } + + mEmojiCategory.setCurrentCategoryId(categoryId); + final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId); + final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId); + if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition( + mEmojiPager.getCurrentItem()).first != categoryId) { + mEmojiPager.setCurrentItem(newCategoryPageId, false /* smoothScroll */); + } + if (force || mTabHost.getCurrentTab() != newTabId) { + mTabHost.setCurrentTab(newTabId); + } + } + + private static class DeleteKeyOnTouchListener implements OnTouchListener { + static final long MAX_REPEAT_COUNT_TIME = TimeUnit.SECONDS.toMillis(30); + final long mKeyRepeatStartTimeout; + final long mKeyRepeatInterval; + + public DeleteKeyOnTouchListener(Context context) { + final Resources res = context.getResources(); + mKeyRepeatStartTimeout = res.getInteger(R.integer.config_key_repeat_start_timeout); + mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); + mTimer = new CountDownTimer(MAX_REPEAT_COUNT_TIME, mKeyRepeatInterval) { + @Override + public void onTick(long millisUntilFinished) { + final long elapsed = MAX_REPEAT_COUNT_TIME - millisUntilFinished; + if (elapsed < mKeyRepeatStartTimeout) { + return; + } + onKeyRepeat(); + } + @Override + public void onFinish() { + onKeyRepeat(); + } + }; + } + + /** Key-repeat state. */ + private static final int KEY_REPEAT_STATE_INITIALIZED = 0; + // The key is touched but auto key-repeat is not started yet. + private static final int KEY_REPEAT_STATE_KEY_DOWN = 1; + // At least one key-repeat event has already been triggered and the key is not released. + private static final int KEY_REPEAT_STATE_KEY_REPEAT = 2; + + private KeyboardActionListener mKeyboardActionListener = + KeyboardActionListener.EMPTY_LISTENER; + + // TODO: Do the same things done in PointerTracker + private final CountDownTimer mTimer; + private int mState = KEY_REPEAT_STATE_INITIALIZED; + private int mRepeatCount = 0; + + public void setKeyboardActionListener(final KeyboardActionListener listener) { + mKeyboardActionListener = listener; + } + + @Override + public boolean onTouch(final View v, final MotionEvent event) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + onTouchDown(v); + return true; + case MotionEvent.ACTION_MOVE: + final float x = event.getX(); + final float y = event.getY(); + if (x < 0.0f || v.getWidth() < x || y < 0.0f || v.getHeight() < y) { + // Stop generating key events once the finger moves away from the view area. + onTouchCanceled(v); + } + return true; + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + onTouchUp(v); + return true; + } + return false; + } + + private void handleKeyDown() { + mKeyboardActionListener.onPressKey( + Constants.CODE_DELETE, mRepeatCount, true /* isSinglePointer */); + } + + private void handleKeyUp() { + mKeyboardActionListener.onCodeInput(Constants.CODE_DELETE, + NOT_A_COORDINATE, NOT_A_COORDINATE, false /* isKeyRepeat */); + mKeyboardActionListener.onReleaseKey( + Constants.CODE_DELETE, false /* withSliding */); + ++mRepeatCount; + } + + private void onTouchDown(final View v) { + mTimer.cancel(); + mRepeatCount = 0; + handleKeyDown(); + v.setPressed(true /* pressed */); + mState = KEY_REPEAT_STATE_KEY_DOWN; + mTimer.start(); + } + + private void onTouchUp(final View v) { + mTimer.cancel(); + if (mState == KEY_REPEAT_STATE_KEY_DOWN) { + handleKeyUp(); + } + v.setPressed(false /* pressed */); + mState = KEY_REPEAT_STATE_INITIALIZED; + } + + private void onTouchCanceled(final View v) { + mTimer.cancel(); + v.setBackgroundColor(Color.TRANSPARENT); + mState = KEY_REPEAT_STATE_INITIALIZED; + } + + // Called by {@link #mTimer} in the UI thread as an auto key-repeat signal. + void onKeyRepeat() { + switch (mState) { + case KEY_REPEAT_STATE_INITIALIZED: + // Basically this should not happen. + break; + case KEY_REPEAT_STATE_KEY_DOWN: + // Do not call {@link #handleKeyDown} here because it has already been called + // in {@link #onTouchDown}. + handleKeyUp(); + mState = KEY_REPEAT_STATE_KEY_REPEAT; + break; + case KEY_REPEAT_STATE_KEY_REPEAT: + handleKeyDown(); + handleKeyUp(); + break; + } + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java index fdc2458d4..3b4c43418 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java @@ -24,7 +24,6 @@ import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.widget.RelativeLayout; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import java.util.ArrayList; @@ -32,7 +31,7 @@ import java.util.ArrayList; public final class DrawingPreviewPlacerView extends RelativeLayout { private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance(); - private final ArrayList<AbstractDrawingPreview> mPreviews = CollectionUtils.newArrayList(); + private final ArrayList<AbstractDrawingPreview> mPreviews = new ArrayList<>(); public DrawingPreviewPlacerView(final Context context, final AttributeSet attrs) { super(context, attrs); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java index d8b00c707..72628e38a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsDrawingPreview.java @@ -29,15 +29,13 @@ import android.util.SparseArray; import android.view.View; import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; /** * Draw preview graphics of multiple gesture trails during gesture input. */ public final class GestureTrailsDrawingPreview extends AbstractDrawingPreview { - private final SparseArray<GestureTrailDrawingPoints> mGestureTrails = - CollectionUtils.newSparseArray(); + private final SparseArray<GestureTrailDrawingPoints> mGestureTrails = new SparseArray<>(); private final GestureTrailDrawingParams mDrawingParams; private final Paint mGesturePaint; private int mOffscreenWidth; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java index 1716fa049..07ac06bab 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java @@ -35,6 +35,7 @@ public final class KeyDrawParams { public int mTextColor; public int mTextInactivatedColor; public int mTextShadowColor; + public int mFunctionalTextColor; public int mHintLetterColor; public int mHintLabelColor; public int mShiftedLetterHintInactivatedColor; @@ -60,6 +61,7 @@ public final class KeyDrawParams { mTextColor = copyFrom.mTextColor; mTextInactivatedColor = copyFrom.mTextInactivatedColor; mTextShadowColor = copyFrom.mTextShadowColor; + mFunctionalTextColor = copyFrom.mFunctionalTextColor; mHintLetterColor = copyFrom.mHintLetterColor; mHintLabelColor = copyFrom.mHintLabelColor; mShiftedLetterHintInactivatedColor = copyFrom.mShiftedLetterHintInactivatedColor; @@ -93,6 +95,7 @@ public final class KeyDrawParams { mTextColor = selectColor(attr.mTextColor, mTextColor); mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor); mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor); + mFunctionalTextColor = selectColor(attr.mFunctionalTextColor, mFunctionalTextColor); mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor); mHintLabelColor = selectColor(attr.mHintLabelColor, mHintLabelColor); mShiftedLetterHintInactivatedColor = selectColor( diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index 625d1f0a4..605519b02 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -32,7 +32,6 @@ import android.widget.TextView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; @@ -48,9 +47,9 @@ import java.util.HashSet; */ public final class KeyPreviewChoreographer { // Free {@link TextView} pool that can be used for key preview. - private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque(); + private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = new ArrayDeque<>(); // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. - private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap(); + private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = new HashMap<>(); private final KeyPreviewDrawParams mParams; @@ -83,7 +82,7 @@ public final class KeyPreviewChoreographer { } public void dismissAllKeyPreviews() { - for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) { + for (final Key key : new HashSet<>(mShowingKeyPreviewTextViews.keySet())) { dismissKeyPreview(key, false /* withAnimation */); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java index 700c9b07c..0b0e761d2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java @@ -21,7 +21,6 @@ import android.util.Log; import android.util.SparseArray; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -34,7 +33,7 @@ public final class KeyStylesSet { private static final String TAG = KeyStylesSet.class.getSimpleName(); private static final boolean DEBUG = false; - private final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap(); + private final HashMap<String, KeyStyle> mStyles = new HashMap<>(); private final KeyboardTextsSet mTextsSet; private final KeyStyle mEmptyKeyStyle; @@ -75,7 +74,7 @@ public final class KeyStylesSet { private static final class DeclaredKeyStyle extends KeyStyle { private final HashMap<String, KeyStyle> mStyles; private final String mParentStyleName; - private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray(); + private final SparseArray<Object> mStyleAttributes = new SparseArray<>(); public DeclaredKeyStyle(final String parentStyleName, final KeyboardTextsSet textsSet, final HashMap<String, KeyStyle> styles) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java index df386fce4..133462ac7 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java @@ -40,6 +40,7 @@ public final class KeyVisualAttributes { public final int mTextColor; public final int mTextInactivatedColor; public final int mTextShadowColor; + public final int mFunctionalTextColor; public final int mHintLetterColor; public final int mHintLabelColor; public final int mShiftedLetterHintInactivatedColor; @@ -61,6 +62,7 @@ public final class KeyVisualAttributes { R.styleable.Keyboard_Key_keyTextColor, R.styleable.Keyboard_Key_keyTextInactivatedColor, R.styleable.Keyboard_Key_keyTextShadowColor, + R.styleable.Keyboard_Key_functionalTextColor, R.styleable.Keyboard_Key_keyHintLetterColor, R.styleable.Keyboard_Key_keyHintLabelColor, R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, @@ -122,6 +124,7 @@ public final class KeyVisualAttributes { mTextInactivatedColor = keyAttr.getColor( R.styleable.Keyboard_Key_keyTextInactivatedColor, 0); mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0); + mFunctionalTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_functionalTextColor, 0); mHintLetterColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLetterColor, 0); mHintLabelColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLabelColor, 0); mShiftedLetterHintInactivatedColor = keyAttr.getColor( diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index dfe0df04c..8bff27574 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -444,6 +444,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { continue; } final int labelFlags = row.getDefaultKeyLabelFlags(); + // TODO: Should be able to assign default keyActionFlags as well. final int backgroundType = row.getDefaultBackgroundType(); final int x = (int)row.getKeyX(null); final int y = row.getKeyY(); @@ -652,9 +653,6 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); - final boolean supportsSwitchingToShortcutImeMatched = matchBoolean(caseAttr, - R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, - id.mSupportsSwitchingToShortcutIme); final boolean hasShortcutKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr, @@ -664,6 +662,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine()); final boolean imeActionMatched = matchInteger(caseAttr, R.styleable.Keyboard_Case_imeAction, id.imeAction()); + final boolean isIconDefinedMatched = isIconDefined(caseAttr, + R.styleable.Keyboard_Case_isIconDefined, mParams.mIconsSet); final boolean localeCodeMatched = matchString(caseAttr, R.styleable.Keyboard_Case_localeCode, id.mLocale.toString()); final boolean languageCodeMatched = matchString(caseAttr, @@ -672,10 +672,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched && modeMatched && navigateNextMatched && navigatePreviousMatched - && passwordInputMatched && clobberSettingsKeyMatched - && supportsSwitchingToShortcutImeMatched && hasShortcutKeyMatched + && passwordInputMatched && clobberSettingsKeyMatched && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched - && localeCodeMatched && languageCodeMatched && countryCodeMatched; + && isIconDefinedMatched && localeCodeMatched && languageCodeMatched + && countryCodeMatched; if (DEBUG) { startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, @@ -695,15 +695,14 @@ public class KeyboardBuilder<KP extends KeyboardParams> { "clobberSettingsKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), - booleanAttr( - caseAttr, R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, - "supportsSwitchingToShortcutIme"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled, "languageSwitchKeyEnabled"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_isMultiLine, "isMultiLine"), + textAttr(caseAttr.getString(R.styleable.Keyboard_Case_isIconDefined), + "isIconDefined"), textAttr(caseAttr.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), textAttr(caseAttr.getString(R.styleable.Keyboard_Case_languageCode), @@ -755,6 +754,16 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return false; } + private static boolean isIconDefined(final TypedArray a, final int index, + final KeyboardIconsSet iconsSet) { + if (!a.hasValue(index)) { + return true; + } + final String iconName = a.getString(index); + final int iconId = KeyboardIconsSet.getIconId(iconName); + return iconsSet.getIconDrawable(iconId) != null; + } + private boolean parseDefault(final XmlPullParser parser, final KeyboardRow row, final boolean skip) throws XmlPullParserException, IOException { if (DEBUG) startTag("<%s>", TAG_DEFAULT); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java index 06da5719b..62b69dcc9 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java @@ -17,14 +17,13 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.HashMap; public final class KeyboardCodesSet { public static final String PREFIX_CODE = "!code/"; - private static final HashMap<String, Integer> sNameToIdMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIdMap = new HashMap<>(); private KeyboardCodesSet() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 6c9b5adc3..7146deb4b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -23,7 +23,6 @@ import android.util.Log; import android.util.SparseIntArray; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.HashMap; @@ -42,7 +41,12 @@ public final class KeyboardIconsSet { public static final String NAME_SPACE_KEY = "space_key"; public static final String NAME_SPACE_KEY_FOR_NUMBER_LAYOUT = "space_key_for_number_layout"; public static final String NAME_ENTER_KEY = "enter_key"; + public static final String NAME_GO_KEY = "go_key"; public static final String NAME_SEARCH_KEY = "search_key"; + public static final String NAME_SEND_KEY = "send_key"; + public static final String NAME_NEXT_KEY = "next_key"; + public static final String NAME_DONE_KEY = "done_key"; + public static final String NAME_PREVIOUS_KEY = "previous_key"; public static final String NAME_TAB_KEY = "tab_key"; public static final String NANE_TAB_KEY_PREVIEW = "tab_key_preview"; public static final String NAME_SHORTCUT_KEY = "shortcut_key"; @@ -55,7 +59,7 @@ public final class KeyboardIconsSet { private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); // Icon name to icon id map. - private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<>(); private static final Object[] NAMES_AND_ATTR_IDS = { NAME_UNDEFINED, ATTR_UNDEFINED, @@ -64,7 +68,12 @@ public final class KeyboardIconsSet { NAME_SETTINGS_KEY, R.styleable.Keyboard_iconSettingsKey, NAME_SPACE_KEY, R.styleable.Keyboard_iconSpaceKey, NAME_ENTER_KEY, R.styleable.Keyboard_iconEnterKey, + NAME_GO_KEY, R.styleable.Keyboard_iconGoKey, NAME_SEARCH_KEY, R.styleable.Keyboard_iconSearchKey, + NAME_SEND_KEY, R.styleable.Keyboard_iconSendKey, + NAME_NEXT_KEY, R.styleable.Keyboard_iconNextKey, + NAME_DONE_KEY, R.styleable.Keyboard_iconDoneKey, + NAME_PREVIOUS_KEY, R.styleable.Keyboard_iconPreviousKey, NAME_TAB_KEY, R.styleable.Keyboard_iconTabKey, NAME_SHORTCUT_KEY, R.styleable.Keyboard_iconShortcutKey, NAME_SPACE_KEY_FOR_NUMBER_LAYOUT, R.styleable.Keyboard_iconSpaceKeyForNumberLayout, @@ -80,6 +89,7 @@ public final class KeyboardIconsSet { private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; private static final String[] ICON_NAMES = new String[NUM_ICONS]; private final Drawable[] mIcons = new Drawable[NUM_ICONS]; + private final int[] mIconResourceIds = new int[NUM_ICONS]; static { int iconId = ICON_UNDEFINED; @@ -87,7 +97,7 @@ public final class KeyboardIconsSet { final String name = (String)NAMES_AND_ATTR_IDS[i]; final Integer attrId = (Integer)NAMES_AND_ATTR_IDS[i + 1]; if (attrId != ATTR_UNDEFINED) { - ATTR_ID_TO_ICON_ID.put(attrId, iconId); + ATTR_ID_TO_ICON_ID.put(attrId, iconId); } sNameToIdsMap.put(name, iconId); ICON_NAMES[iconId] = name; @@ -104,6 +114,7 @@ public final class KeyboardIconsSet { setDefaultBounds(icon); final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId); mIcons[iconId] = icon; + mIconResourceIds[iconId] = keyboardAttrs.getResourceId(attrId, 0); } catch (Resources.NotFoundException e) { Log.w(TAG, "Drawable resource for icon #" + keyboardAttrs.getResources().getResourceEntryName(attrId) @@ -128,6 +139,14 @@ public final class KeyboardIconsSet { throw new RuntimeException("unknown icon name: " + name); } + public int getIconResourceId(final String name) { + final int iconId = getIconId(name); + if (isValidIconId(iconId)) { + return mIconResourceIds[iconId]; + } + throw new RuntimeException("unknown icon name: " + name); + } + public Drawable getIconDrawable(final int iconId) { if (isValidIconId(iconId)) { return mIcons[iconId]; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java index a61a79b85..5df9d3ece 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -21,7 +21,6 @@ import android.util.SparseIntArray; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; import java.util.Comparator; @@ -61,9 +60,9 @@ public class KeyboardParams { public int GRID_HEIGHT; // Keys are sorted from top-left to bottom-right order. - public final SortedSet<Key> mSortedKeys = new TreeSet<Key>(ROW_COLUMN_COMPARATOR); - public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList(); - public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList(); + public final SortedSet<Key> mSortedKeys = new TreeSet<>(ROW_COLUMN_COMPARATOR); + public final ArrayList<Key> mShiftKeys = new ArrayList<>(); + public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<>(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet(); public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java index 0f9497c27..92daf0742 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java @@ -23,7 +23,6 @@ import android.util.Xml; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import org.xmlpull.v1.XmlPullParser; @@ -44,8 +43,9 @@ public final class KeyboardRow { /** The height of this row. */ private final int mRowHeight; - private final ArrayDeque<RowAttributes> mRowAttributesStack = CollectionUtils.newArrayDeque(); + private final ArrayDeque<RowAttributes> mRowAttributesStack = new ArrayDeque<>(); + // TODO: Add keyActionFlags. private static class RowAttributes { /** Default width of a key in this row. */ public final float mDefaultKeyWidth; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 0047aa4a1..cd6abeed3 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -22,7 +22,6 @@ import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -38,7 +37,7 @@ public final class KeyboardTextsSet { private String[] mTextsTable; // Resource name to text map. - private HashMap<String, String> mResourceNameToTextsMap = CollectionUtils.newHashMap(); + private HashMap<String, String> mResourceNameToTextsMap = new HashMap<>(); public void setLocale(final Locale locale, final Context context) { mTextsTable = KeyboardTextsTable.getTextsTable(locale); @@ -141,6 +140,7 @@ public final class KeyboardTextsSet { "label_send_key", "label_next_key", "label_done_key", + "label_search_key", "label_previous_key", // Other labels. "label_pause_key", diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index 14fa76744..ab2555802 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.HashMap; import java.util.Locale; @@ -44,14 +42,12 @@ import java.util.Locale; */ public final class KeyboardTextsTable { // Name to index map. - private static final HashMap<String, Integer> sNameToIndexesMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIndexesMap = new HashMap<>(); // Locale to texts table map. - private static final HashMap<String, String[]> sLocaleToTextsTableMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String[]> sLocaleToTextsTableMap = new HashMap<>(); // TODO: Remove this variable after debugging. // Texts table to locale maps. - private static final HashMap<String[], String> sTextsTableToLocaleMap = - CollectionUtils.newHashMap(); + private static final HashMap<String[], String> sTextsTableToLocaleMap = new HashMap<>(); public static String getText(final String name, final String[] textsTable) { final Integer indexObj = sNameToIndexesMap.get(name); @@ -95,18 +91,18 @@ public final class KeyboardTextsTable { /* 5:23 */ "morekeys_c", /* 6:23 */ "double_quotes", /* 7:22 */ "morekeys_n", - /* 8:22 */ "single_quotes", - /* 9:21 */ "keylabel_to_alpha", + /* 8:22 */ "keylabel_to_alpha", + /* 9:22 */ "single_quotes", /* 10:20 */ "morekeys_s", /* 11:14 */ "morekeys_y", /* 12:13 */ "morekeys_d", /* 13:12 */ "morekeys_z", /* 14:10 */ "morekeys_t", /* 15:10 */ "morekeys_l", - /* 16: 9 */ "morekeys_g", - /* 17: 9 */ "single_angle_quotes", - /* 18: 9 */ "double_angle_quotes", - /* 19: 9 */ "keyspec_currency", + /* 16:10 */ "keyspec_currency", + /* 17: 9 */ "morekeys_g", + /* 18: 9 */ "single_angle_quotes", + /* 19: 9 */ "double_angle_quotes", /* 20: 8 */ "morekeys_r", /* 21: 6 */ "morekeys_k", /* 22: 6 */ "morekeys_cyrillic_ie", @@ -119,29 +115,29 @@ public final class KeyboardTextsTable { /* 29: 5 */ "keyspec_east_slavic_row2_11", /* 30: 5 */ "keyspec_east_slavic_row3_5", /* 31: 5 */ "morekeys_cyrillic_soft_sign", - /* 32: 4 */ "morekeys_nordic_row2_11", - /* 33: 4 */ "morekeys_punctuation", - /* 34: 4 */ "keyspec_symbols_1", - /* 35: 4 */ "keyspec_symbols_2", - /* 36: 4 */ "keyspec_symbols_3", - /* 37: 4 */ "keyspec_symbols_4", - /* 38: 4 */ "keyspec_symbols_5", - /* 39: 4 */ "keyspec_symbols_6", - /* 40: 4 */ "keyspec_symbols_7", - /* 41: 4 */ "keyspec_symbols_8", - /* 42: 4 */ "keyspec_symbols_9", - /* 43: 4 */ "keyspec_symbols_0", - /* 44: 4 */ "keylabel_to_symbol", - /* 45: 4 */ "additional_morekeys_symbols_1", - /* 46: 4 */ "additional_morekeys_symbols_2", - /* 47: 4 */ "additional_morekeys_symbols_3", - /* 48: 4 */ "additional_morekeys_symbols_4", - /* 49: 4 */ "additional_morekeys_symbols_5", - /* 50: 4 */ "additional_morekeys_symbols_6", - /* 51: 4 */ "additional_morekeys_symbols_7", - /* 52: 4 */ "additional_morekeys_symbols_8", - /* 53: 4 */ "additional_morekeys_symbols_9", - /* 54: 4 */ "additional_morekeys_symbols_0", + /* 32: 5 */ "keyspec_symbols_1", + /* 33: 5 */ "keyspec_symbols_2", + /* 34: 5 */ "keyspec_symbols_3", + /* 35: 5 */ "keyspec_symbols_4", + /* 36: 5 */ "keyspec_symbols_5", + /* 37: 5 */ "keyspec_symbols_6", + /* 38: 5 */ "keyspec_symbols_7", + /* 39: 5 */ "keyspec_symbols_8", + /* 40: 5 */ "keyspec_symbols_9", + /* 41: 5 */ "keyspec_symbols_0", + /* 42: 5 */ "keylabel_to_symbol", + /* 43: 5 */ "additional_morekeys_symbols_1", + /* 44: 5 */ "additional_morekeys_symbols_2", + /* 45: 5 */ "additional_morekeys_symbols_3", + /* 46: 5 */ "additional_morekeys_symbols_4", + /* 47: 5 */ "additional_morekeys_symbols_5", + /* 48: 5 */ "additional_morekeys_symbols_6", + /* 49: 5 */ "additional_morekeys_symbols_7", + /* 50: 5 */ "additional_morekeys_symbols_8", + /* 51: 5 */ "additional_morekeys_symbols_9", + /* 52: 5 */ "additional_morekeys_symbols_0", + /* 53: 4 */ "morekeys_nordic_row2_11", + /* 54: 4 */ "morekeys_punctuation", /* 55: 4 */ "keyspec_tablet_comma", /* 56: 3 */ "keyspec_swiss_row1_11", /* 57: 3 */ "keyspec_swiss_row2_10", @@ -266,19 +262,19 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_lqm_rqm", /* morekeys_n */ EMPTY, - /* single_quotes */ "!text/single_lqm_rqm", // Label for "switch to alphabetic" key. /* keylabel_to_alpha */ "ABC", + /* single_quotes */ "!text/single_lqm_rqm", /* morekeys_s ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_g */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ morekeys_l */ + /* keyspec_currency */ "$", + /* morekeys_g */ EMPTY, /* single_angle_quotes */ "!text/single_laqm_raqm", /* double_angle_quotes */ "!text/double_laqm_raqm", - /* keyspec_currency */ "$", /* morekeys_r ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_nordic_row2_11 */ - /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&", + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ morekeys_cyrillic_soft_sign */ /* keyspec_symbols_1 */ "1", /* keyspec_symbols_2 */ "2", /* keyspec_symbols_3 */ "3", @@ -292,8 +288,9 @@ public final class KeyboardTextsTable { // Label for "switch to symbols" key. /* keylabel_to_symbol */ "?123", /* additional_morekeys_symbols_1 ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ additional_morekeys_symbols_0 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~ morekeys_nordic_row2_11 */ + /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&", /* keyspec_tablet_comma */ ",", /* keyspec_swiss_row1_11 ~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, @@ -515,7 +512,7 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, /* ~ morekeys_s */ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE @@ -526,18 +523,18 @@ public final class KeyboardTextsTable { /* Locale ar: Arabic */ private static final String[] TEXTS_ar = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "ج" ARABIC LETTER JEEM /* keylabel_to_alpha */ "\u0623\u200C\u0628\u200C\u062C", - /* morekeys_s ~ */ + /* single_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, - /* ~ morekeys_punctuation */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_cyrillic_soft_sign */ // U+0661: "١" ARABIC-INDIC DIGIT ONE /* keyspec_symbols_1 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO @@ -573,6 +570,8 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", + /* morekeys_nordic_row2_11 */ null, + /* morekeys_punctuation */ null, // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON @@ -603,7 +602,7 @@ public final class KeyboardTextsTable { /* keyspec_right_double_angle_quote */ "\u00BB|\u00AB", /* keyspec_left_single_angle_quote */ "\u2039|\u203A", /* keyspec_right_single_angle_quote */ "\u203A|\u2039", - /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\",\'", + /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,\",\'", // U+0651: "ّ" ARABIC SHADDA /* keyhintlabel_period */ "\u0651", /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics", @@ -688,15 +687,15 @@ public final class KeyboardTextsTable { /* morekeys_c */ "\u00E7,\u0107,\u010D", /* double_quotes ~ */ null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ single_quotes */ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161", /* morekeys_y ~ */ - null, null, null, null, null, - /* ~ morekeys_l */ + null, null, null, null, null, null, + /* ~ keyspec_currency */ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u011F", }; @@ -708,12 +707,12 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ @@ -742,7 +741,6 @@ public final class KeyboardTextsTable { // single_quotes of Bulgarian is default single_quotes_right_left. /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ null, // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE @@ -802,23 +800,23 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, null, null, null, /* ~ morekeys_t */ // U+00B7: "·" MIDDLE DOT // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "l\u00B7l,\u0142", - /* morekeys_g ~ */ + /* keyspec_currency ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, /* ~ morekeys_nordic_row2_11 */ // U+00B7: "·" MIDDLE DOT /* morekeys_punctuation */ "!autoColumnOrder!9,\\,,?,!,\u00B7,#,),(,/,;,',@,:,-,\",+,\\%,&", - /* keyspec_symbols_1 ~ */ + /* keyspec_tablet_comma ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, /* ~ keyspec_south_slavic_row3_8 */ /* morekeys_tablet_punctuation */ "!autoColumnOrder!8,\\,,',\u00B7,#,),(,/,;,@,:,-,\",+,\\%,&", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA @@ -877,8 +875,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0148,\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -894,11 +892,11 @@ public final class KeyboardTextsTable { /* morekeys_z */ "\u017E,\u017A,\u017C", // U+0165: "ť" LATIN SMALL LETTER T WITH CARON /* morekeys_t */ "\u0165", - /* morekeys_l */ null, - /* morekeys_g */ null, + /* morekeys_l ~ */ + null, null, null, + /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency */ null, // U+0159: "ř" LATIN SMALL LETTER R WITH CARON /* morekeys_r */ "\u0159", }; @@ -936,8 +934,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -951,11 +949,12 @@ public final class KeyboardTextsTable { /* morekeys_t */ null, // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "\u0142", + /* keyspec_currency */ null, /* morekeys_g */ null, /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency ~ */ - null, null, null, null, + /* morekeys_r ~ */ + null, null, null, /* ~ morekeys_cyrillic_ie */ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE /* keyspec_nordic_row1_11 */ "\u00E5", @@ -966,8 +965,9 @@ public final class KeyboardTextsTable { // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS /* morekeys_nordic_row2_10 */ "\u00E4", /* keyspec_east_slavic_row1_9 ~ */ - null, null, null, null, null, - /* ~ morekeys_cyrillic_soft_sign */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS /* morekeys_nordic_row2_11 */ "\u00F6", }; @@ -1010,21 +1010,21 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON /* morekeys_s */ "\u00DF,\u015B,\u0161", /* morekeys_y ~ */ - null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency ~ */ + /* morekeys_r ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ keyspec_tablet_comma */ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* keyspec_swiss_row1_11 */ "\u00FC", @@ -1043,8 +1043,8 @@ public final class KeyboardTextsTable { /* Locale el: Greek */ private static final String[] TEXTS_el = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA @@ -1063,40 +1063,40 @@ public final class KeyboardTextsTable { // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101", + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+0153: "œ" LATIN SMALL LIGATURE OE // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE - /* morekeys_o */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5", + /* morekeys_o */ "\u00F3,\u00F4,\u00F6,\u00F2,\u0153,\u00F8,\u014D,\u00F5", + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - /* morekeys_u */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", - // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON - /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113", + /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113", + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE - /* morekeys_i */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", + /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", }; @@ -1169,8 +1169,8 @@ public final class KeyboardTextsTable { // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE // U+014B: "ŋ" LATIN SMALL LETTER ENG /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1201,13 +1201,13 @@ public final class KeyboardTextsTable { // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "\u013A,\u013C,\u013E,\u0140,\u0142", + /* keyspec_currency */ null, // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA /* morekeys_g */ "\u011F,\u0121,\u0123", - /* single_angle_quotes ~ */ - null, null, null, - /* ~ keyspec_currency */ + /* single_angle_quotes */ null, + /* double_angle_quotes */ null, // U+0159: "ř" LATIN SMALL LETTER R WITH CARON // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA @@ -1301,9 +1301,11 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, /* ~ morekeys_nordic_row2_11 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK @@ -1366,8 +1368,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1390,12 +1392,12 @@ public final class KeyboardTextsTable { // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E", + /* keyspec_currency */ null, // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u0123,\u011F", - /* single_angle_quotes ~ */ - null, null, null, - /* ~ keyspec_currency */ + /* single_angle_quotes */ null, + /* double_angle_quotes */ null, // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA // U+0159: "ř" LATIN SMALL LETTER R WITH CARON // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE @@ -1470,22 +1472,22 @@ public final class KeyboardTextsTable { /* Locale fa: Persian */ private static final String[] TEXTS_fa = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+FDFC: "﷼" RIAL SIGN /* keyspec_currency */ "\uFDFC", - /* morekeys_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ morekeys_punctuation */ + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_cyrillic_soft_sign */ // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE /* keyspec_symbols_1 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO @@ -1521,6 +1523,8 @@ public final class KeyboardTextsTable { // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR /* additional_morekeys_symbols_0 */ "0,\u066B,\u066C", + /* morekeys_nordic_row2_11 */ null, + /* morekeys_punctuation */ null, // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK @@ -1547,7 +1551,7 @@ public final class KeyboardTextsTable { /* keyspec_right_double_angle_quote */ "\u00BB|\u00AB", /* keyspec_left_single_angle_quote */ "\u2039|\u203A", /* keyspec_right_single_angle_quote */ "\u203A|\u2039", - /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote", + /* morekeys_tablet_comma */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote", // U+064B: "ً" ARABIC FATHATAN /* keyhintlabel_period */ "\u064B", /* morekeys_tablet_period */ "!text/morekeys_arabic_diacritics", @@ -1629,7 +1633,7 @@ public final class KeyboardTextsTable { /* morekeys_u */ "\u00FC", /* morekeys_e ~ */ null, null, null, null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ single_quotes */ // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -1652,8 +1656,9 @@ public final class KeyboardTextsTable { // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE /* morekeys_nordic_row2_10 */ "\u00F8", /* keyspec_east_slavic_row1_9 ~ */ - null, null, null, null, null, - /* ~ morekeys_cyrillic_soft_sign */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00E6: "æ" LATIN SMALL LETTER AE /* morekeys_nordic_row2_11 */ "\u00E6", }; @@ -1786,21 +1791,21 @@ public final class KeyboardTextsTable { /* Locale hi: Hindi */ private static final String[] TEXTS_hi = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", - /* morekeys_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ morekeys_punctuation */ + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_cyrillic_soft_sign */ // U+0967: "१" DEVANAGARI DIGIT ONE /* keyspec_symbols_1 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO @@ -1848,8 +1853,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_rqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_rqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+00DF: "ß" LATIN SMALL LETTER SHARP S @@ -1862,7 +1867,7 @@ public final class KeyboardTextsTable { // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE /* morekeys_z */ "\u017E,\u017A,\u017C", /* morekeys_t ~ */ - null, null, null, + null, null, null, null, /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", @@ -1914,8 +1919,9 @@ public final class KeyboardTextsTable { /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, + /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", - /* keylabel_to_alpha ~ */ + /* morekeys_s ~ */ null, null, null, null, null, null, null, null, /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", @@ -1925,16 +1931,17 @@ public final class KeyboardTextsTable { /* Locale hy_AM: Armenian (Armenia) */ private static final String[] TEXTS_hy_AM = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM /* keylabel_to_alpha */ "\u0531\u0532\u0533", - /* morekeys_s ~ */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, /* ~ morekeys_nordic_row2_11 */ // U+055E: "՞" ARMENIAN QUESTION MARK // U+055C: "՜" ARMENIAN EXCLAMATION MARK @@ -1947,10 +1954,6 @@ public final class KeyboardTextsTable { // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+055F: "՟" ARMENIAN ABBREVIATION MARK /* morekeys_punctuation */ "!autoColumnOrder!8,\\,,\u055E,\u055C,.,\u055A,\u0559,?,!,\u055D,\u055B,\u058A,\u00BB,\u00AB,\u055F,;,:", - /* keyspec_symbols_1 ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ // U+058F: "֏" ARMENIAN DRAM SIGN // TODO: Enable this when we have glyph for the following letter // <string name="keyspec_currency">֏</string> @@ -2026,8 +2029,8 @@ public final class KeyboardTextsTable { /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s */ null, // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS @@ -2109,21 +2112,21 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_rqm_9qm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_rqm_9qm", // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL /* keylabel_to_alpha */ "\u05D0\u05D1\u05D2", + /* single_quotes */ "!text/single_rqm_9qm", /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + null, null, null, null, null, null, + /* ~ morekeys_l */ // U+20AA: "₪" NEW SHEQEL SIGN /* keyspec_currency */ "\u20AA", - /* morekeys_r ~ */ + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_swiss_row2_11 */ // U+2605: "★" BLACK STAR /* morekeys_star */ "\u2605", @@ -2166,26 +2169,26 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2", + /* single_quotes */ "!text/single_9qm_lqm", }; /* Locale kk: Kazakh */ private static final String[] TEXTS_kk = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, + /* single_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2202,7 +2205,7 @@ public final class KeyboardTextsTable { /* keyspec_east_slavic_row3_5 */ "\u0438", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN /* morekeys_cyrillic_soft_sign */ "\u044A", - /* morekeys_nordic_row2_11 ~ */ + /* keyspec_symbols_1 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2234,14 +2237,14 @@ public final class KeyboardTextsTable { /* Locale km_KH: Khmer (Cambodia) */ private static final String[] TEXTS_km_KH = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+1780: "ក" KHMER LETTER KA // U+1781: "ខ" KHMER LETTER KHA // U+1782: "គ" KHMER LETTER KO /* keylabel_to_alpha */ "\u1780\u1781\u1782", - /* morekeys_s ~ */ + /* single_quotes ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2249,7 +2252,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ morekeys_cyrillic_a */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", @@ -2258,15 +2261,15 @@ public final class KeyboardTextsTable { /* Locale ky: Kirghiz */ private static final String[] TEXTS_ky = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, + /* single_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2283,7 +2286,7 @@ public final class KeyboardTextsTable { /* keyspec_east_slavic_row3_5 */ "\u0438", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN /* morekeys_cyrillic_soft_sign */ "\u044A", - /* morekeys_nordic_row2_11 ~ */ + /* keyspec_symbols_1 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2301,16 +2304,16 @@ public final class KeyboardTextsTable { /* Locale lo_LA: Lao (Laos) */ private static final String[] TEXTS_lo_LA = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0E81: "ກ" LAO LETTER KO // U+0E82: "ຂ" LAO LETTER KHO SUNG // U+0E84: "ຄ" LAO LETTER KHO TAM /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+20AD: "₭" KIP SIGN /* keyspec_currency */ "\u20AD", }; @@ -2372,8 +2375,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -2396,12 +2399,12 @@ public final class KeyboardTextsTable { // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E", + /* keyspec_currency */ null, // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u0123,\u011F", - /* single_angle_quotes ~ */ - null, null, null, - /* ~ keyspec_currency */ + /* single_angle_quotes */ null, + /* double_angle_quotes */ null, // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA // U+0159: "ř" LATIN SMALL LETTER R WITH CARON // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE @@ -2466,8 +2469,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0146,\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -2490,12 +2493,12 @@ public final class KeyboardTextsTable { // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON /* morekeys_l */ "\u013C,\u0142,\u013A,\u013E", + /* keyspec_currency */ null, // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u0123,\u011F", - /* single_angle_quotes ~ */ - null, null, null, - /* ~ keyspec_currency */ + /* single_angle_quotes */ null, + /* double_angle_quotes */ null, // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA // U+0159: "ř" LATIN SMALL LETTER R WITH CARON // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE @@ -2511,12 +2514,12 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ @@ -2544,39 +2547,88 @@ public final class KeyboardTextsTable { /* Locale mn_MN: Mongolian (Mongolia) */ private static final String[] TEXTS_mn_MN = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+20AE: "₮" TUGRIK SIGN /* keyspec_currency */ "\u20AE", }; + /* Locale mr_IN: Marathi (India) */ + private static final String[] TEXTS_mr_IN = { + /* morekeys_a ~ */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ + // Label for "switch to alphabetic" key. + // U+0915: "क" DEVANAGARI LETTER KA + // U+0916: "ख" DEVANAGARI LETTER KHA + // U+0917: "ग" DEVANAGARI LETTER GA + /* keylabel_to_alpha */ "\u0915\u0916\u0917", + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ + // U+20B9: "₹" INDIAN RUPEE SIGN + /* keyspec_currency */ "\u20B9", + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_cyrillic_soft_sign */ + // U+0967: "१" DEVANAGARI DIGIT ONE + /* keyspec_symbols_1 */ "\u0967", + // U+0968: "२" DEVANAGARI DIGIT TWO + /* keyspec_symbols_2 */ "\u0968", + // U+0969: "३" DEVANAGARI DIGIT THREE + /* keyspec_symbols_3 */ "\u0969", + // U+096A: "४" DEVANAGARI DIGIT FOUR + /* keyspec_symbols_4 */ "\u096A", + // U+096B: "५" DEVANAGARI DIGIT FIVE + /* keyspec_symbols_5 */ "\u096B", + // U+096C: "६" DEVANAGARI DIGIT SIX + /* keyspec_symbols_6 */ "\u096C", + // U+096D: "७" DEVANAGARI DIGIT SEVEN + /* keyspec_symbols_7 */ "\u096D", + // U+096E: "८" DEVANAGARI DIGIT EIGHT + /* keyspec_symbols_8 */ "\u096E", + // U+096F: "९" DEVANAGARI DIGIT NINE + /* keyspec_symbols_9 */ "\u096F", + // U+0966: "०" DEVANAGARI DIGIT ZERO + /* keyspec_symbols_0 */ "\u0966", + // Label for "switch to symbols" key. + /* keylabel_to_symbol */ "?\u0967\u0968\u0969", + /* additional_morekeys_symbols_1 */ "1", + /* additional_morekeys_symbols_2 */ "2", + /* additional_morekeys_symbols_3 */ "3", + /* additional_morekeys_symbols_4 */ "4", + /* additional_morekeys_symbols_5 */ "5", + /* additional_morekeys_symbols_6 */ "6", + /* additional_morekeys_symbols_7 */ "7", + /* additional_morekeys_symbols_8 */ "8", + /* additional_morekeys_symbols_9 */ "9", + /* additional_morekeys_symbols_0 */ "0", + }; + /* Locale my_MM: Burmese (Myanmar) */ private static final String[] TEXTS_my_MM = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+1000: "က" MYANMAR LETTER KA // U+1001: "ခ" MYANMAR LETTER KHA // U+1002: "ဂ" MYANMAR LETTER GA /* keylabel_to_alpha */ "\u1000\u1001\u1002", - /* morekeys_s ~ */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, /* ~ morekeys_nordic_row2_11 */ /* morekeys_punctuation */ "!autoColumnOrder!9,\u104A,.,?,!,#,),(,/,;,...,',@,:,-,\",+,\\%,&", - /* keyspec_symbols_1 ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~ additional_morekeys_symbols_0 */ // U+104A: "၊" MYANMAR SIGN LITTLE SECTION // U+104B: "။" MYANMAR SIGN SECTION /* keyspec_tablet_comma */ "\u104A", @@ -2633,9 +2685,10 @@ public final class KeyboardTextsTable { /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, + /* keylabel_to_alpha */ null, /* single_quotes */ "!text/single_9qm_rqm", - /* keylabel_to_alpha ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* morekeys_s ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_cyrillic_ie */ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE /* keyspec_nordic_row1_11 */ "\u00E5", @@ -2646,8 +2699,9 @@ public final class KeyboardTextsTable { // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS /* morekeys_nordic_row2_10 */ "\u00F6", /* keyspec_east_slavic_row1_9 ~ */ - null, null, null, null, null, - /* ~ morekeys_cyrillic_soft_sign */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS /* morekeys_nordic_row2_11 */ "\u00E4", }; @@ -2655,21 +2709,21 @@ public final class KeyboardTextsTable { /* Locale ne_NP: Nepali (Nepal) */ private static final String[] TEXTS_ne_NP = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN /* keyspec_currency */ "\u0930\u0941.", - /* morekeys_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~ morekeys_punctuation */ + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~ morekeys_cyrillic_soft_sign */ // U+0967: "१" DEVANAGARI DIGIT ONE /* keyspec_symbols_1 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO @@ -2751,8 +2805,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_rqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_rqm", /* morekeys_s */ null, // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_y */ "\u0133", @@ -2797,8 +2851,8 @@ public final class KeyboardTextsTable { // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u0144,\u00F1", - /* single_quotes */ "!text/single_9qm_rqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_rqm", // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -2900,8 +2954,8 @@ public final class KeyboardTextsTable { /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_rqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_rqm", // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -2921,12 +2975,12 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_k */ @@ -3004,8 +3058,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u0148,\u0146,\u00F1,\u0144", - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE @@ -3028,12 +3082,12 @@ public final class KeyboardTextsTable { // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "\u013E,\u013A,\u013C,\u0142", + /* keyspec_currency */ null, // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u0123,\u011F", /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency */ null, // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE // U+0159: "ř" LATIN SMALL LETTER R WITH CARON // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA @@ -3052,8 +3106,8 @@ public final class KeyboardTextsTable { /* morekeys_c */ "\u010D,\u0107", /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", /* keylabel_to_alpha */ null, + /* single_quotes */ "!text/single_9qm_lqm", // U+0161: "š" LATIN SMALL LETTER S WITH CARON /* morekeys_s */ "\u0161", /* morekeys_y */ null, @@ -3062,7 +3116,7 @@ public final class KeyboardTextsTable { // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON /* morekeys_z */ "\u017E", /* morekeys_t ~ */ - null, null, null, + null, null, null, null, /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", @@ -3075,21 +3129,20 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ - null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, /* ~ morekeys_g */ /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency ~ */ - null, null, null, - /* ~ morekeys_k */ + /* morekeys_r */ null, + /* morekeys_k */ null, // U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE /* morekeys_cyrillic_ie */ "\u0450", /* keyspec_nordic_row1_11 ~ */ @@ -3169,8 +3222,8 @@ public final class KeyboardTextsTable { // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0148: "ň" LATIN SMALL LETTER N WITH CARON /* morekeys_n */ "\u0144,\u00F1,\u0148", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA @@ -3191,10 +3244,10 @@ public final class KeyboardTextsTable { /* morekeys_t */ "\u0165,\u00FE", // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "\u0142", + /* keyspec_currency */ null, /* morekeys_g */ null, /* single_angle_quotes */ "!text/single_raqm_laqm", /* double_angle_quotes */ "!text/double_raqm_laqm", - /* keyspec_currency */ null, // U+0159: "ř" LATIN SMALL LETTER R WITH CARON /* morekeys_r */ "\u0159", /* morekeys_k */ null, @@ -3209,8 +3262,9 @@ public final class KeyboardTextsTable { // U+0153: "œ" LATIN SMALL LIGATURE OE /* morekeys_nordic_row2_10 */ "\u00F8,\u0153", /* keyspec_east_slavic_row1_9 ~ */ - null, null, null, null, null, - /* ~ morekeys_cyrillic_soft_sign */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, + /* ~ additional_morekeys_symbols_0 */ // U+00E6: "æ" LATIN SMALL LETTER AE /* morekeys_nordic_row2_11 */ "\u00E6", }; @@ -3259,29 +3313,29 @@ public final class KeyboardTextsTable { /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", /* morekeys_y ~ */ - null, null, null, null, null, - /* ~ morekeys_l */ + null, null, null, null, null, null, + /* ~ keyspec_currency */ /* morekeys_g */ "g\'", }; /* Locale th: Thai */ private static final String[] TEXTS_th = { /* morekeys_a ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ single_quotes */ + null, null, null, null, null, null, null, null, + /* ~ morekeys_n */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04", - /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + /* single_quotes ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_l */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT /* keyspec_currency */ "\u0E3F", }; @@ -3374,15 +3428,15 @@ public final class KeyboardTextsTable { /* morekeys_c */ "\u00E7,\u0107,\u010D", /* double_quotes ~ */ null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ single_quotes */ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+0161: "š" LATIN SMALL LETTER S WITH CARON /* morekeys_s */ "\u015F,\u00DF,\u015B,\u0161", /* morekeys_y ~ */ - null, null, null, null, null, - /* ~ morekeys_l */ + null, null, null, null, null, null, + /* ~ keyspec_currency */ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE /* morekeys_g */ "\u011F", }; @@ -3394,19 +3448,19 @@ public final class KeyboardTextsTable { /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, - /* single_quotes */ "!text/single_9qm_lqm", // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", + /* single_quotes */ "!text/single_9qm_lqm", /* morekeys_s ~ */ - null, null, null, null, null, null, null, null, null, - /* ~ double_angle_quotes */ + null, null, null, null, null, null, + /* ~ morekeys_l */ // U+20B4: "₴" HRYVNIA SIGN /* keyspec_currency */ "\u20B4", - /* morekeys_r ~ */ - null, null, null, null, null, null, null, + /* morekeys_g ~ */ + null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_nordic_row2_10 */ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* keyspec_east_slavic_row1_9 */ "\u0449", @@ -3418,7 +3472,7 @@ public final class KeyboardTextsTable { /* keyspec_east_slavic_row3_5 */ "\u0438", // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN /* morekeys_cyrillic_soft_sign */ "\u044A", - /* morekeys_nordic_row2_11 ~ */ + /* keyspec_symbols_1 ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -3512,8 +3566,8 @@ public final class KeyboardTextsTable { // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE /* morekeys_d */ "\u0111", /* morekeys_z ~ */ - null, null, null, null, null, null, - /* ~ double_angle_quotes */ + null, null, null, + /* ~ morekeys_l */ // U+20AB: "₫" DONG SIGN /* keyspec_currency */ "\u20AB", }; @@ -3530,40 +3584,40 @@ public final class KeyboardTextsTable { // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON /* morekeys_a */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101", + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+0153: "œ" LATIN SMALL LIGATURE OE // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE - /* morekeys_o */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5", + /* morekeys_o */ "\u00F3,\u00F4,\u00F6,\u00F2,\u0153,\u00F8,\u014D,\u00F5", + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - /* morekeys_u */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", - // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON - /* morekeys_e */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113", + /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0113", + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE - /* morekeys_i */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", + /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE /* morekeys_n */ "\u00F1", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S /* morekeys_s */ "\u00DF", }; @@ -3640,8 +3694,8 @@ public final class KeyboardTextsTable { // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE // U+014B: "ŋ" LATIN SMALL LETTER ENG /* morekeys_n */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B", - /* single_quotes */ null, /* keylabel_to_alpha */ null, + /* single_quotes */ null, // U+00DF: "ß" LATIN SMALL LETTER SHARP S // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX @@ -3673,14 +3727,14 @@ public final class KeyboardTextsTable { // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE /* morekeys_l */ "\u013A,\u013C,\u013E,\u0140,\u0142", + /* keyspec_currency */ null, // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA /* morekeys_g */ "\u011D,\u011F,\u0121,\u0123", - /* single_angle_quotes ~ */ - null, null, null, - /* ~ keyspec_currency */ + /* single_angle_quotes */ null, + /* double_angle_quotes */ null, // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA // U+0159: "ř" LATIN SMALL LETTER R WITH CARON @@ -3711,26 +3765,26 @@ public final class KeyboardTextsTable { "DEFAULT", TEXTS_DEFAULT, /* 168/168 DEFAULT */ "af" , TEXTS_af, /* 7/ 12 Afrikaans */ "ar" , TEXTS_ar, /* 55/110 Arabic */ - "az_AZ" , TEXTS_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ + "az_AZ" , TEXTS_az_AZ, /* 8/ 18 Azerbaijani (Azerbaijan) */ "be_BY" , TEXTS_be_BY, /* 9/ 32 Belarusian (Belarus) */ - "bg" , TEXTS_bg, /* 2/ 10 Bulgarian */ + "bg" , TEXTS_bg, /* 2/ 9 Bulgarian */ "ca" , TEXTS_ca, /* 11/ 95 Catalan */ "cs" , TEXTS_cs, /* 17/ 21 Czech */ - "da" , TEXTS_da, /* 19/ 33 Danish */ + "da" , TEXTS_da, /* 19/ 54 Danish */ "de" , TEXTS_de, /* 16/ 62 German */ - "el" , TEXTS_el, /* 1/ 10 Greek */ + "el" , TEXTS_el, /* 1/ 9 Greek */ "en" , TEXTS_en, /* 8/ 11 English */ "eo" , TEXTS_eo, /* 26/118 Esperanto */ - "es" , TEXTS_es, /* 8/ 34 Spanish */ + "es" , TEXTS_es, /* 8/ 55 Spanish */ "et_EE" , TEXTS_et_EE, /* 22/ 27 Estonian (Estonia) */ "eu_ES" , TEXTS_eu_ES, /* 7/ 8 Basque (Spain) */ "fa" , TEXTS_fa, /* 58/125 Persian */ - "fi" , TEXTS_fi, /* 10/ 33 Finnish */ + "fi" , TEXTS_fi, /* 10/ 54 Finnish */ "fr" , TEXTS_fr, /* 13/ 62 French */ "gl_ES" , TEXTS_gl_ES, /* 7/ 8 Gallegan (Spain) */ - "hi" , TEXTS_hi, /* 23/ 55 Hindi */ - "hr" , TEXTS_hr, /* 9/ 19 Croatian */ - "hu" , TEXTS_hu, /* 9/ 19 Hungarian */ + "hi" , TEXTS_hi, /* 23/ 53 Hindi */ + "hr" , TEXTS_hr, /* 9/ 20 Croatian */ + "hu" , TEXTS_hu, /* 9/ 20 Hungarian */ "hy_AM" , TEXTS_hy_AM, /* 8/126 Armenian (Armenia) */ "is" , TEXTS_is, /* 10/ 15 Icelandic */ "it" , TEXTS_it, /* 11/ 62 Italian */ @@ -3739,14 +3793,15 @@ public final class KeyboardTextsTable { "kk" , TEXTS_kk, /* 15/121 Kazakh */ "km_KH" , TEXTS_km_KH, /* 2/122 Khmer (Cambodia) */ "ky" , TEXTS_ky, /* 10/ 88 Kirghiz */ - "lo_LA" , TEXTS_lo_LA, /* 2/ 20 Lao (Laos) */ + "lo_LA" , TEXTS_lo_LA, /* 2/ 17 Lao (Laos) */ "lt" , TEXTS_lt, /* 18/ 22 Lithuanian */ "lv" , TEXTS_lv, /* 18/ 22 Latvian */ "mk" , TEXTS_mk, /* 9/ 93 Macedonian */ - "mn_MN" , TEXTS_mn_MN, /* 2/ 20 Mongolian (Mongolia) */ + "mn_MN" , TEXTS_mn_MN, /* 2/ 17 Mongolian (Mongolia) */ + "mr_IN" , TEXTS_mr_IN, /* 23/ 53 Marathi (India) */ "my_MM" , TEXTS_my_MM, /* 8/104 Burmese (Myanmar) */ - "nb" , TEXTS_nb, /* 11/ 33 Norwegian Bokmål */ - "ne_NP" , TEXTS_ne_NP, /* 23/ 55 Nepali (Nepal) */ + "nb" , TEXTS_nb, /* 11/ 54 Norwegian Bokmål */ + "ne_NP" , TEXTS_ne_NP, /* 23/ 53 Nepali (Nepal) */ "nl" , TEXTS_nl, /* 9/ 12 Dutch */ "pl" , TEXTS_pl, /* 10/ 16 Polish */ "pt" , TEXTS_pt, /* 6/ 6 Portuguese */ @@ -3754,15 +3809,15 @@ public final class KeyboardTextsTable { "ro" , TEXTS_ro, /* 6/ 15 Romanian */ "ru" , TEXTS_ru, /* 9/ 32 Russian */ "sk" , TEXTS_sk, /* 20/ 22 Slovak */ - "sl" , TEXTS_sl, /* 8/ 19 Slovenian */ + "sl" , TEXTS_sl, /* 8/ 20 Slovenian */ "sr" , TEXTS_sr, /* 11/ 93 Serbian */ - "sv" , TEXTS_sv, /* 21/ 33 Swedish */ - "sw" , TEXTS_sw, /* 9/ 17 Swahili */ - "th" , TEXTS_th, /* 2/ 20 Thai */ + "sv" , TEXTS_sv, /* 21/ 54 Swedish */ + "sw" , TEXTS_sw, /* 9/ 18 Swahili */ + "th" , TEXTS_th, /* 2/ 17 Thai */ "tl" , TEXTS_tl, /* 7/ 8 Tagalog */ - "tr" , TEXTS_tr, /* 7/ 17 Turkish */ + "tr" , TEXTS_tr, /* 7/ 18 Turkish */ "uk" , TEXTS_uk, /* 11/ 87 Ukrainian */ - "vi" , TEXTS_vi, /* 8/ 20 Vietnamese */ + "vi" , TEXTS_vi, /* 8/ 17 Vietnamese */ "zu" , TEXTS_zu, /* 8/ 11 Zulu */ "zz" , TEXTS_zz, /* 19/112 Alphabet */ }; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java b/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java index 7c2e3e174..7743d4744 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java @@ -17,12 +17,11 @@ package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.HashMap; public final class KeysCache { - private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap(); + private final HashMap<Key, Key> mMap = new HashMap<>(); public void clear() { mMap.clear(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index 56ef4767f..e0d5173ac 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -149,7 +149,7 @@ public final class MoreKeySpec { // Skip empty entry. if (pos - start > 0) { if (list == null) { - list = CollectionUtils.newArrayList(); + list = new ArrayList<>(); } list.add(text.substring(start, pos)); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 5ac34188c..8e89e61ea 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard.internal; import android.util.Log; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.ArrayList; public final class PointerTrackerQueue { @@ -37,7 +35,7 @@ public final class PointerTrackerQueue { // Note: {@link #mExpandableArrayOfActivePointers} and {@link #mArraySize} are synchronized by // {@link #mExpandableArrayOfActivePointers} private final ArrayList<Element> mExpandableArrayOfActivePointers = - CollectionUtils.newArrayList(INITIAL_CAPACITY); + new ArrayList<>(INITIAL_CAPACITY); private int mArraySize = 0; public int size() { diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java index 54bc29559..eb8b34ccd 100644 --- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java +++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java @@ -16,14 +16,14 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.settings.SettingsValues; - import android.content.Context; import android.media.AudioManager; import android.os.Vibrator; import android.view.HapticFeedbackConstants; import android.view.View; +import com.android.inputmethod.latin.settings.SettingsValues; + /** * This class gathers audio feedback and haptic feedback functions. * @@ -86,40 +86,41 @@ public final class AudioAndHapticFeedbackManager { if (mAudioManager == null) { return; } - if (mSoundOn) { - final int sound; - switch (code) { - case Constants.CODE_DELETE: - sound = AudioManager.FX_KEYPRESS_DELETE; - break; - case Constants.CODE_ENTER: - sound = AudioManager.FX_KEYPRESS_RETURN; - break; - case Constants.CODE_SPACE: - sound = AudioManager.FX_KEYPRESS_SPACEBAR; - break; - default: - sound = AudioManager.FX_KEYPRESS_STANDARD; - break; - } - mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume); + if (!mSoundOn) { + return; + } + final int sound; + switch (code) { + case Constants.CODE_DELETE: + sound = AudioManager.FX_KEYPRESS_DELETE; + break; + case Constants.CODE_ENTER: + sound = AudioManager.FX_KEYPRESS_RETURN; + break; + case Constants.CODE_SPACE: + sound = AudioManager.FX_KEYPRESS_SPACEBAR; + break; + default: + sound = AudioManager.FX_KEYPRESS_STANDARD; + break; } + mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume); } public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) { if (!mSettingsValues.mVibrateOn) { return; } - if (mSettingsValues.mKeypressVibrationDuration < 0) { - // Go ahead with the system default - if (viewToPerformHapticFeedbackOn != null) { - viewToPerformHapticFeedbackOn.performHapticFeedback( - HapticFeedbackConstants.KEYBOARD_TAP, - HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - } + if (mSettingsValues.mKeypressVibrationDuration >= 0) { + vibrate(mSettingsValues.mKeypressVibrationDuration); return; } - vibrate(mSettingsValues.mKeypressVibrationDuration); + // Go ahead with the system default + if (viewToPerformHapticFeedbackOn != null) { + viewToPerformHapticFeedbackOn.performHapticFeedback( + HapticFeedbackConstants.KEYBOARD_TAP, + HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + } } public void onSettingsChanged(final SettingsValues settingsValues) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b88509fde..543f74fc4 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -30,7 +30,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; @@ -104,8 +103,7 @@ public final class BinaryDictionary extends Dictionary { private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); - private final SparseArray<DicTraverseSession> mDicTraverseSessions = - CollectionUtils.newSparseArray(); + private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>(); // TODO: There should be a way to remove used DicTraverseSession objects from // {@code mDicTraverseSessions}. @@ -113,11 +111,8 @@ public final class BinaryDictionary extends Dictionary { synchronized(mDicTraverseSessions) { DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); if (traverseSession == null) { - traverseSession = mDicTraverseSessions.get(traverseSessionId); - if (traverseSession == null) { - traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize); - mDicTraverseSessions.put(traverseSessionId, traverseSession); - } + traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize); + mDicTraverseSessions.put(traverseSessionId, traverseSession); } return traverseSession; } @@ -188,13 +183,15 @@ public final class BinaryDictionary extends Dictionary { private static native void getHeaderInfoNative(long dict, int[] outHeaderSize, int[] outFormatVersion, ArrayList<int[]> outAttributeKeys, ArrayList<int[]> outAttributeValues); - private static native void flushNative(long dict, String filePath); + private static native boolean flushNative(long dict, String filePath); private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC); - private static native void flushWithGCNative(long dict, String filePath); + private static native boolean flushWithGCNative(long dict, String filePath); private static native void closeNative(long dict); private static native int getFormatVersionNative(long dict); private static native int getProbabilityNative(long dict, int[] word); - private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1); + private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word); + private static native int getBigramProbabilityNative(long dict, int[] word0, + boolean isBeginningOfSentence, int[] word1); private static native void getWordPropertyNative(long dict, int[] word, int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo, ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo, @@ -203,21 +200,24 @@ public final class BinaryDictionary extends Dictionary { private static native void getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions, - int[] prevWordCodePointArray, int[] outputSuggestionCount, int[] outputCodePoints, - int[] outputScores, int[] outputIndices, int[] outputTypes, - int[] outputAutoCommitFirstWordConfidence, float[] inOutLanguageWeight); - private static native void addUnigramWordNative(long dict, int[] word, int probability, - int[] shortcutTarget, int shortcutProbability, boolean isNotAWord, - boolean isBlacklisted, int timestamp); - private static native void addBigramWordsNative(long dict, int[] word0, int[] word1, - int probability, int timestamp); - private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1); + int[] prevWordCodePointArray, boolean isBeginningOfSentence, + int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores, + int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence, + float[] inOutLanguageWeight); + private static native boolean addUnigramWordNative(long dict, int[] word, int probability, + int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence, + boolean isNotAWord, boolean isBlacklisted, int timestamp); + private static native boolean removeUnigramWordNative(long dict, int[] word); + private static native boolean addBigramWordsNative(long dict, int[] word0, + boolean isBeginningOfSentence, int[] word1, int probability, int timestamp); + private static native boolean removeBigramWordsNative(long dict, int[] word0, + boolean isBeginningOfSentence, int[] word1); private static native int addMultipleDictionaryEntriesNative(long dict, LanguageModelParam[] languageModelParams, int startIndex); - private static native int calculateProbabilityNative(long dict, int unigramProbability, - int bigramProbability); private static native String getPropertyNative(long dict, String query); private static native boolean isCorruptedNative(long dict); + private static native boolean migrateNative(long dict, String dictFilePath, + long newFormatVersion); // TODO: Move native dict into session private final void loadDictionary(final String path, final long startOffset, @@ -248,11 +248,11 @@ public final class BinaryDictionary extends Dictionary { } final int[] outHeaderSize = new int[1]; final int[] outFormatVersion = new int[1]; - final ArrayList<int[]> outAttributeKeys = CollectionUtils.newArrayList(); - final ArrayList<int[]> outAttributeValues = CollectionUtils.newArrayList(); + final ArrayList<int[]> outAttributeKeys = new ArrayList<>(); + final ArrayList<int[]> outAttributeValues = new ArrayList<>(); getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys, outAttributeValues); - final HashMap<String, String> attributes = new HashMap<String, String>(); + final HashMap<String, String> attributes = new HashMap<>(); for (int i = 0; i < outAttributeKeys.size(); i++) { final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray( outAttributeKeys.get(i)); @@ -266,19 +266,9 @@ public final class BinaryDictionary extends Dictionary { new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo)); } - @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { - return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, 0 /* sessionId */, inOutLanguageWeight); - } - - @Override - public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { if (!isValidDictionary()) { @@ -287,8 +277,8 @@ public final class BinaryDictionary extends Dictionary { Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code - final int[] prevWordCodePointArray = (null == prevWord) - ? null : StringUtils.toCodePointArray(prevWord); + final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord) + ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); final InputPointers inputPointers = composer.getInputPointers(); final boolean isGesture = composer.isBatchMode(); final int inputSize; @@ -303,6 +293,7 @@ public final class BinaryDictionary extends Dictionary { } mNativeSuggestOptions.setIsGesture(isGesture); + mNativeSuggestOptions.setBlockOffensiveWords(blockOffensiveWords); mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); if (inOutLanguageWeight != null) { mInputOutputLanguageWeight[0] = inOutLanguageWeight[0]; @@ -314,14 +305,15 @@ public final class BinaryDictionary extends Dictionary { getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), inputPointers.getYCoordinates(), inputPointers.getTimes(), inputPointers.getPointerIds(), mInputCodePoints, inputSize, - mNativeSuggestOptions.getOptions(), prevWordCodePointArray, mOutputSuggestionCount, + mNativeSuggestOptions.getOptions(), prevWordCodePointArray, + prevWordsInfo.mIsBeginningOfSentence, mOutputSuggestionCount, mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes, mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight); if (inOutLanguageWeight != null) { inOutLanguageWeight[0] = mInputOutputLanguageWeight[0]; } final int count = mOutputSuggestionCount[0]; - final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); for (int j = 0; j < count; ++j) { final int start = j * MAX_WORD_LENGTH; int len = 0; @@ -329,21 +321,8 @@ public final class BinaryDictionary extends Dictionary { ++len; } if (len > 0) { - final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS; - if (blockOffensiveWords - && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE) - && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) { - // If we block potentially offensive words, and if the word is possibly - // offensive, then we don't output it unless it's also an exact match. - continue; - } - final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND; - final int score = SuggestedWordInfo.KIND_WHITELIST == kind - ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j]; - // TODO: check that all users of the `kind' parameter are ready to accept - // flags too and pass mOutputTypes[j] instead of kind suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), - score, kind, this /* sourceDict */, + mOutputScores[j], mOutputTypes[j], this /* sourceDict */, mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, mOutputAutoCommitFirstWordConfidence[0])); } @@ -360,28 +339,37 @@ public final class BinaryDictionary extends Dictionary { } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { return getFrequency(word) != NOT_A_PROBABILITY; } @Override public int getFrequency(final String word) { - if (word == null) return NOT_A_PROBABILITY; + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; int[] codePoints = StringUtils.toCodePointArray(word); return getProbabilityNative(mNativeDict, codePoints); } - // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni - // calls when checking for changes in an entire dictionary. - public boolean isValidBigram(final String word0, final String word1) { - return getBigramProbability(word0, word1) != NOT_A_PROBABILITY; + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; + int[] codePoints = StringUtils.toCodePointArray(word); + return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints); } - public int getBigramProbability(final String word0, final String word1) { - if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY; - final int[] codePoints0 = StringUtils.toCodePointArray(word0); - final int[] codePoints1 = StringUtils.toCodePointArray(word1); - return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1); + @UsedForTesting + public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) { + return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY; + } + + public int getNgramProbability(final PrevWordsInfo prevWordsInfo, final String word) { + if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { + return NOT_A_PROBABILITY; + } + final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); + final int[] codePoints1 = StringUtils.toCodePointArray(word); + return getBigramProbabilityNative(mNativeDict, codePoints0, + prevWordsInfo.mIsBeginningOfSentence, codePoints1); } public WordProperty getWordProperty(final String word) { @@ -393,10 +381,10 @@ public final class BinaryDictionary extends Dictionary { final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT]; final int[] outProbabilityInfo = new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT]; - final ArrayList<int[]> outBigramTargets = CollectionUtils.newArrayList(); - final ArrayList<int[]> outBigramProbabilityInfo = CollectionUtils.newArrayList(); - final ArrayList<int[]> outShortcutTargets = CollectionUtils.newArrayList(); - final ArrayList<Integer> outShortcutProbabilities = CollectionUtils.newArrayList(); + final ArrayList<int[]> outBigramTargets = new ArrayList<>(); + final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>(); + final ArrayList<int[]> outShortcutTargets = new ArrayList<>(); + final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>(); getWordPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); @@ -413,8 +401,8 @@ public final class BinaryDictionary extends Dictionary { public WordProperty mWordProperty; public int mNextToken; - public GetNextWordPropertyResult(final WordProperty wordPreperty, final int nextToken) { - mWordProperty = wordPreperty; + public GetNextWordPropertyResult(final WordProperty wordProperty, final int nextToken) { + mWordProperty = wordProperty; mNextToken = nextToken; } } @@ -431,41 +419,66 @@ public final class BinaryDictionary extends Dictionary { } // Add a unigram entry to binary dictionary with unigram attributes in native code. - public void addUnigramWord(final String word, final int probability, - final String shortcutTarget, final int shortcutProbability, final boolean isNotAWord, + public boolean addUnigramEntry(final String word, final int probability, + final String shortcutTarget, final int shortcutProbability, + final boolean isBeginningOfSentence, final boolean isNotAWord, final boolean isBlacklisted, final int timestamp) { - if (TextUtils.isEmpty(word)) { - return; + if (word == null || (word.isEmpty() && !isBeginningOfSentence)) { + return false; } final int[] codePoints = StringUtils.toCodePointArray(word); final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? StringUtils.toCodePointArray(shortcutTarget) : null; - addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, - shortcutProbability, isNotAWord, isBlacklisted, timestamp); + if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, + shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) { + return false; + } mHasUpdated = true; + return true; } - // Add a bigram entry to binary dictionary with timestamp in native code. - public void addBigramWords(final String word0, final String word1, final int probability, - final int timestamp) { - if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { - return; + // Remove a unigram entry from the binary dictionary in native code. + public boolean removeUnigramEntry(final String word) { + if (TextUtils.isEmpty(word)) { + return false; + } + final int[] codePoints = StringUtils.toCodePointArray(word); + if (!removeUnigramWordNative(mNativeDict, codePoints)) { + return false; } - final int[] codePoints0 = StringUtils.toCodePointArray(word0); - final int[] codePoints1 = StringUtils.toCodePointArray(word1); - addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp); mHasUpdated = true; + return true; } - // Remove a bigram entry form binary dictionary in native code. - public void removeBigramWords(final String word0, final String word1) { - if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { - return; + // Add an n-gram entry to the binary dictionary with timestamp in native code. + public boolean addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word, + final int probability, final int timestamp) { + if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { + return false; + } + final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); + final int[] codePoints1 = StringUtils.toCodePointArray(word); + if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, + codePoints1, probability, timestamp)) { + return false; } - final int[] codePoints0 = StringUtils.toCodePointArray(word0); - final int[] codePoints1 = StringUtils.toCodePointArray(word1); - removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); mHasUpdated = true; + return true; + } + + // Remove an n-gram entry from the binary dictionary in native code. + public boolean removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) { + if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { + return false; + } + final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); + final int[] codePoints1 = StringUtils.toCodePointArray(word); + if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, + codePoints1)) { + return false; + } + mHasUpdated = true; + return true; } public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { @@ -495,26 +508,33 @@ public final class BinaryDictionary extends Dictionary { } // Flush to dict file if the dictionary has been updated. - public void flush() { - if (!isValidDictionary()) return; + public boolean flush() { + if (!isValidDictionary()) return false; if (mHasUpdated) { - flushNative(mNativeDict, mDictFilePath); + if (!flushNative(mNativeDict, mDictFilePath)) { + return false; + } reopen(); } + return true; } // Run GC and flush to dict file if the dictionary has been updated. - public void flushWithGCIfHasUpdated() { + public boolean flushWithGCIfHasUpdated() { if (mHasUpdated) { - flushWithGC(); + return flushWithGC(); } + return true; } // Run GC and flush to dict file. - public void flushWithGC() { - if (!isValidDictionary()) return; - flushWithGCNative(mNativeDict, mDictFilePath); + public boolean flushWithGC() { + if (!isValidDictionary()) return false; + if (!flushWithGCNative(mNativeDict, mDictFilePath)) { + return false; + } reopen(); + return true; } /** @@ -533,11 +553,15 @@ public final class BinaryDictionary extends Dictionary { return false; } final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION; - // TODO: Implement migrateNative(tmpDictFilePath, newFormatVersion). + if (!migrateNative(mNativeDict, tmpDictFilePath, newFormatVersion)) { + return false; + } close(); final File dictFile = new File(mDictFilePath); final File tmpDictFile = new File(tmpDictFilePath); - FileUtils.deleteRecursively(dictFile); + if (!FileUtils.deleteRecursively(dictFile)) { + return false; + } if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) { return false; } @@ -547,12 +571,6 @@ public final class BinaryDictionary extends Dictionary { } @UsedForTesting - public int calculateProbability(final int unigramProbability, final int bigramProbability) { - if (!isValidDictionary()) return NOT_A_PROBABILITY; - return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability); - } - - @UsedForTesting public String getPropertyForTest(final String query) { if (!isValidDictionary()) return ""; return getPropertyNative(mNativeDict, query); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index e428b1d54..10b1f1b77 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -28,7 +28,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.dictionarypack.MD5Calculator; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo; import com.android.inputmethod.latin.utils.FileTransforms; @@ -38,12 +38,13 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; 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.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Locale; @@ -163,12 +164,13 @@ public final class BinaryDictionaryFileDumper { if (cursor.getCount() <= 0 || !cursor.moveToFirst()) { return Collections.<WordListInfo>emptyList(); } - final ArrayList<WordListInfo> list = CollectionUtils.newArrayList(); + final ArrayList<WordListInfo> list = new ArrayList<>(); do { final String wordListId = cursor.getString(0); final String wordListLocale = cursor.getString(1); + final String wordListRawChecksum = cursor.getString(2); if (TextUtils.isEmpty(wordListId)) continue; - list.add(new WordListInfo(wordListId, wordListLocale)); + list.add(new WordListInfo(wordListId, wordListLocale, wordListRawChecksum)); } while (cursor.moveToNext()); return list; } catch (RemoteException e) { @@ -217,7 +219,8 @@ public final class BinaryDictionaryFileDumper { * and creating it (and its containing directory) if necessary. */ private static void cacheWordList(final String wordlistId, final String locale, - final ContentProviderClient providerClient, final Context context) { + final String rawChecksum, final ContentProviderClient providerClient, + final Context context) { final int COMPRESSED_CRYPTED_COMPRESSED = 0; final int CRYPTED_COMPRESSED = 1; final int COMPRESSED_CRYPTED = 2; @@ -299,6 +302,13 @@ public final class BinaryDictionaryFileDumper { checkMagicAndCopyFileTo(bufferedInputStream, bufferedOutputStream); bufferedOutputStream.flush(); bufferedOutputStream.close(); + final String actualRawChecksum = MD5Calculator.checksum( + new BufferedInputStream(new FileInputStream(outputFile))); + Log.i(TAG, "Computed checksum for downloaded dictionary. Expected = " + rawChecksum + + " ; actual = " + actualRawChecksum); + if (!TextUtils.isEmpty(rawChecksum) && !rawChecksum.equals(actualRawChecksum)) { + throw new IOException("Could not decode the file correctly : checksum differs"); + } final File finalFile = new File(finalFileName); finalFile.delete(); if (!outputFile.renameTo(finalFile)) { @@ -408,7 +418,7 @@ public final class BinaryDictionaryFileDumper { final List<WordListInfo> idList = getWordListWordListInfos(locale, context, hasDefaultWordList); for (WordListInfo id : idList) { - cacheWordList(id.mId, id.mLocale, providerClient, context); + cacheWordList(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); } } finally { providerClient.release(); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 4c49cb31c..867c18686 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -24,7 +24,6 @@ import android.util.Log; 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.CollectionUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.LocaleUtils; @@ -160,7 +159,7 @@ final public class BinaryDictionaryGetter { public static File[] getCachedWordLists(final String locale, final Context context) { final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context); if (null == directoryList) return EMPTY_FILE_ARRAY; - final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap(); + final HashMap<String, FileAndMatchLevel> cacheFiles = new HashMap<>(); for (File directory : directoryList) { if (!directory.isDirectory()) continue; final String dirLocale = @@ -273,7 +272,7 @@ final public class BinaryDictionaryGetter { final DictPackSettings dictPackSettings = new DictPackSettings(context); boolean foundMainDict = false; - final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList(); + final ArrayList<AssetFileAddress> fileList = new ArrayList<>(); // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName()); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index e71723a15..fa51436de 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -115,6 +115,11 @@ public final class Constants { */ public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype"; + /** + * The subtype extra value used to specify the combining rules. + */ + public static final String COMBINING_RULES = "CombiningRules"; + private ExtraValue() { // This utility class is not publicly instantiable. } @@ -153,6 +158,10 @@ public final class Constants { // A hint on how many characters to cache from the TextView. A good value of this is given by // how many characters we need to be able to almost always find the caps mode. public static final int EDITOR_CONTENTS_CACHE_SIZE = 1024; + // How many characters we accept for the recapitalization functionality. This needs to be + // large enough for all reasonable purposes, but avoid purposeful attacks. 100k sounds about + // right for this. + public static final int MAX_CHARACTERS_FOR_RECAPITALIZATION = 1024 * 100; // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h public static final int DICTIONARY_MAX_WORD_LENGTH = 48; @@ -164,6 +173,8 @@ public final class Constants { // How many continuous deletes at which to start deleting at a higher speed. public static final int DELETE_ACCELERATE_AT = 20; + public static final String WORD_SEPARATOR = " "; + public static boolean isValidCoordinate(final int coordinate) { // Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE}, // and {@link SPELL_CHECKER_COORDINATE}. @@ -185,7 +196,6 @@ public final class Constants { public static final int CODE_SPACE = ' '; public static final int CODE_PERIOD = '.'; public static final int CODE_COMMA = ','; - public static final int CODE_ARMENIAN_PERIOD = 0x0589; public static final int CODE_DASH = '-'; public static final int CODE_SINGLE_QUOTE = '\''; public static final int CODE_DOUBLE_QUOTE = '"'; @@ -201,6 +211,11 @@ public final class Constants { public static final int CODE_CLOSING_SQUARE_BRACKET = ']'; public static final int CODE_CLOSING_CURLY_BRACKET = '}'; public static final int CODE_CLOSING_ANGLE_BRACKET = '>'; + public static final int CODE_INVERTED_QUESTION_MARK = 0xBF; // ¿ + public static final int CODE_INVERTED_EXCLAMATION_MARK = 0xA1; // ¡ + + public static final String REGEXP_PERIOD = "\\."; + public static final String STRING_SPACE = " "; /** * Special keys code. Must be negative. @@ -242,14 +257,16 @@ public final class Constants { case CODE_LANGUAGE_SWITCH: return "languageSwitch"; case CODE_EMOJI: return "emoji"; case CODE_SHIFT_ENTER: return "shiftEnter"; + case CODE_ALPHA_FROM_EMOJI: return "alpha"; case CODE_UNSPECIFIED: return "unspec"; case CODE_TAB: return "tab"; case CODE_ENTER: return "enter"; - case CODE_ALPHA_FROM_EMOJI: return "alpha"; + case CODE_SPACE: return "space"; default: - if (code < CODE_SPACE) return String.format("'\\u%02x'", code); - if (code < 0x100) return String.format("'%c'", code); - return String.format("'\\u%04x'", code); + if (code < CODE_SPACE) return String.format("\\u%02x", code); + if (code < 0x100) return String.format("%c", code); + if (code < 0x10000) return String.format("\\u04x", code); + return String.format("\\U%05x", code); } } diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 09d0ea210..96160fa4e 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -29,10 +29,13 @@ import android.provider.ContactsContract.Contacts; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.personalization.AccountUtils; +import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.io.File; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -59,10 +62,10 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private static final int INDEX_NAME = 1; /** The number of contacts in the most recent dictionary rebuild. */ - static private int sContactCountAtLastRebuild = 0; + private int mContactCountAtLastRebuild = 0; - /** The locale for this contacts dictionary. Controls name bigram predictions. */ - public final Locale mLocale; + /** The hash code of ArrayList of contacts names in the most recent dictionary rebuild. */ + private int mHashCodeAtLastRebuild = 0; private ContentObserver mObserver; @@ -71,25 +74,21 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { */ private final boolean mUseFirstLastBigrams; - public ContactsBinaryDictionary(final Context context, final Locale locale) { - this(context, locale, null /* dictFile */); - } - - public ContactsBinaryDictionary(final Context context, final Locale locale, - final File dictFile) { - this(context, locale, dictFile, NAME); - } - protected ContactsBinaryDictionary(final Context context, final Locale locale, final File dictFile, final String name) { super(context, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_CONTACTS, dictFile); - mLocale = locale; mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); registerObserver(context); reloadDictionaryIfRequired(); } + @UsedForTesting + public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix) { + return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME); + } + private synchronized void registerObserver(final Context context) { if (mObserver != null) return; ContentResolver cres = context.getContentResolver(); @@ -97,7 +96,14 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { new ContentObserver(null) { @Override public void onChange(boolean self) { - setNeedsToReload(); + ExecutorUtils.getExecutor("Check Contacts").execute(new Runnable() { + @Override + public void run() { + if (haveContentsChanged()) { + setNeedsToRecreate(); + } + } + }); } }); } @@ -130,7 +136,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { Log.d(TAG, "loadAccountVocabulary: " + word); } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, + addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } @@ -144,7 +150,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { return; } if (cursor.moveToFirst()) { - sContactCountAtLastRebuild = getContactCount(); + mContactCountAtLastRebuild = getContactCount(); addWordsLocked(cursor); } } catch (final SQLiteException e) { @@ -168,9 +174,11 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private void addWordsLocked(final Cursor cursor) { int count = 0; + final ArrayList<String> names = new ArrayList<>(); while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) { String name = cursor.getString(INDEX_NAME); if (isValidName(name)) { + names.add(name); addNameLocked(name); ++count; } else { @@ -180,6 +188,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } cursor.moveToNext(); } + mHashCodeAtLastRebuild = names.hashCode(); } private int getContactCount() { @@ -209,7 +218,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { */ private void addNameLocked(final String name) { int len = StringUtils.codePointCount(name); - String prevWord = null; + PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; // TODO: Better tokenization for non-Latin writing systems for (int i = 0; i < len; i++) { if (Character.isLetter(name.codePointAt(i))) { @@ -224,19 +233,19 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { final int wordLen = StringUtils.codePointCount(word); if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { if (DEBUG) { - Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord); + Log.d(TAG, "addName " + name + ", " + word + ", " + + prevWordsInfo.mPrevWord); } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addWordDynamicallyLocked(word, FREQUENCY_FOR_CONTACTS, + addUnigramLocked(word, FREQUENCY_FOR_CONTACTS, null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) { + if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && mUseFirstLastBigrams) { runGCIfRequiredLocked(true /* mindsBlockByGC */); - addBigramDynamicallyLocked(prevWord, word, - FREQUENCY_FOR_CONTACTS_BIGRAM, + addNgramEntryLocked(prevWordsInfo, word, FREQUENCY_FOR_CONTACTS_BIGRAM, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } - prevWord = word; + prevWordsInfo = new PrevWordsInfo(word); } } } @@ -259,8 +268,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { return end; } - @Override - protected boolean haveContentsChanged() { + private boolean haveContentsChanged() { final long startTime = SystemClock.uptimeMillis(); final int contactCount = getContactCount(); if (contactCount > MAX_CONTACT_COUNT) { @@ -269,9 +277,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { // TODO: Sort and check only the MAX_CONTACT_COUNT most recent contacts? return false; } - if (contactCount != sContactCountAtLastRebuild) { + if (contactCount != mContactCountAtLastRebuild) { if (DEBUG) { - Log.d(TAG, "Contact count changed: " + sContactCountAtLastRebuild + " to " + Log.d(TAG, "Contact count changed: " + mContactCountAtLastRebuild + " to " + contactCount); } return true; @@ -284,20 +292,20 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { if (null == cursor) { return false; } + final ArrayList<String> names = new ArrayList<>(); try { if (cursor.moveToFirst()) { while (!cursor.isAfterLast()) { String name = cursor.getString(INDEX_NAME); - if (isValidName(name) && !isNameInDictionaryLocked(name)) { - if (DEBUG) { - Log.d(TAG, "Contact name missing: " + name + " (runtime = " - + (SystemClock.uptimeMillis() - startTime) + " ms)"); - } - return true; + if (isValidName(name)) { + names.add(name); } cursor.moveToNext(); } } + if (names.hashCode() != mHashCodeAtLastRebuild) { + return true; + } } finally { cursor.close(); } @@ -314,33 +322,4 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } return false; } - - /** - * Checks if the words in a name are in the current binary dictionary. - */ - private boolean isNameInDictionaryLocked(final String name) { - int len = StringUtils.codePointCount(name); - String prevWord = null; - for (int i = 0; i < len; i++) { - if (Character.isLetter(name.codePointAt(i))) { - int end = getWordEndPosition(name, len, i); - String word = name.substring(i, end); - i = end - 1; - final int wordLen = StringUtils.codePointCount(word); - if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { - if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) { - if (!isValidBigramLocked(prevWord, word)) { - return false; - } - } else { - if (!isValidWordLocked(word)) { - return false; - } - } - prevWord = word; - } - } - } - return true; - } } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 0742fbde9..b55ed125f 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -57,6 +58,8 @@ public abstract class Dictionary { public static final String TYPE_USER_HISTORY = "history"; // Personalization dictionary. public static final String TYPE_PERSONALIZATION = "personalization"; + // Contextual dictionary. + public static final String TYPE_CONTEXTUAL = "contextual"; public final String mDictType; public Dictionary(final String dictType) { @@ -67,43 +70,44 @@ public abstract class Dictionary { * Searches for suggestions for a given context. For the moment the context is only the * previous word. * @param composer the key sequence to match with coordinate info, as a WordComposer - * @param prevWord the previous word, or null if none + * @param prevWordsInfo the information of previous words. * @param proximityInfo the object for key proximity. May be ignored by some implementations. * @param blockOffensiveWords whether to block potentially offensive words * @param additionalFeaturesOptions options about additional features used for the suggestion. + * @param sessionId the session id. * @param inOutLanguageWeight the language weight used for generating suggestions. * inOutLanguageWeight is a float array that has only one element. This can be updated when the * different language weight is used. * @return the list of suggestions (possibly null if none) */ - // TODO: pass more context than just the previous word, to enable better suggestions (n-gram - // and more) abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight); + final int sessionId, final float[] inOutLanguageWeight); - // The default implementation of this method ignores sessionId. - // Subclasses that want to use sessionId need to override this method. - public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final int sessionId, final float[] inOutLanguageWeight) { - return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, inOutLanguageWeight); + /** + * Checks if the given word has to be treated as a valid word. Please note that some + * dictionaries have entries that should be treated as invalid words. + * @param word the word to search for. The search should be case-insensitive. + * @return true if the word is valid, false otherwise + */ + public boolean isValidWord(final String word) { + return isInDictionary(word); } /** - * Checks if the given word occurs in the dictionary - * @param word the word to search for. The search should be case-insensitive. - * @return true if the word exists, false otherwise + * Checks if the given word is in the dictionary regardless of it being valid or not. */ - abstract public boolean isValidWord(final String word); + abstract public boolean isInDictionary(final String word); public int getFrequency(final String word) { return NOT_A_PROBABILITY; } + public int getMaxFrequencyOfExactMatches(final String word) { + return NOT_A_PROBABILITY; + } + /** * Compares the contents of the character array with the typed word and returns true if they * are the same. @@ -163,14 +167,14 @@ public abstract class Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { + final int sessionId, final float[] inOutLanguageWeight) { return null; } @Override - public boolean isValidWord(String word) { + public boolean isInDictionary(String word) { return false; } } diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 16173fffc..89d61ce2a 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -20,7 +20,6 @@ import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; import java.util.Collection; @@ -36,52 +35,52 @@ public final class DictionaryCollection extends Dictionary { public DictionaryCollection(final String dictType) { super(dictType); - mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); + mDictionaries = new CopyOnWriteArrayList<>(); } public DictionaryCollection(final String dictType, final Dictionary... dictionaries) { super(dictType); if (null == dictionaries) { - mDictionaries = CollectionUtils.newCopyOnWriteArrayList(); + mDictionaries = new CopyOnWriteArrayList<>(); } else { - mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); + mDictionaries = new CopyOnWriteArrayList<>(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } } public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) { super(dictType); - mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries); + mDictionaries = new CopyOnWriteArrayList<>(dictionaries); mDictionaries.removeAll(Collections.singleton(null)); } @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { + final int sessionId, final float[] inOutLanguageWeight) { final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries; 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, - prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, - inOutLanguageWeight); - if (null == suggestions) suggestions = CollectionUtils.newArrayList(); + prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId, inOutLanguageWeight); + 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, - prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, - inOutLanguageWeight); + prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId, inOutLanguageWeight); if (null != sugg) suggestions.addAll(sugg); } return suggestions; } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { for (int i = mDictionaries.size() - 1; i >= 0; --i) - if (mDictionaries.get(i).isValidWord(word)) return true; + if (mDictionaries.get(i).isInDictionary(word)) return true; return false; } @@ -90,9 +89,17 @@ public final class DictionaryCollection extends Dictionary { int maxFreq = -1; for (int i = mDictionaries.size() - 1; i >= 0; --i) { final int tempFreq = mDictionaries.get(i).getFrequency(word); - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; - } + maxFreq = Math.max(tempFreq, maxFreq); + } + return maxFreq; + } + + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + int maxFreq = -1; + for (int i = mDictionaries.size() - 1; i >= 0; --i) { + final int tempFreq = mDictionaries.get(i).getMaxFrequencyOfExactMatches(word); + maxFreq = Math.max(tempFreq, maxFreq); } return maxFreq; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 5238395a4..e6e2bcbc7 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -19,21 +19,30 @@ package com.android.inputmethod.latin; import android.content.Context; import android.text.TextUtils; import android.util.Log; +import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.personalization.ContextualDictionary; +import com.android.inputmethod.latin.personalization.PersonalizationDataChunk; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; -import com.android.inputmethod.latin.personalization.PersonalizationHelper; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary; import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.SuggestionResults; import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -41,80 +50,93 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; // TODO: Consolidate dictionaries in native code. -public class DictionaryFacilitatorForSuggest { - public static final String TAG = DictionaryFacilitatorForSuggest.class.getSimpleName(); +public class DictionaryFacilitator { + public static final String TAG = DictionaryFacilitator.class.getSimpleName(); // HACK: This threshold is being used when adding a capitalized entry in the User History // dictionary. private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; private Dictionaries mDictionaries = new Dictionaries(); + private boolean mIsUserDictEnabled = false; private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); // To synchronize assigning mDictionaries to ensure closing dictionaries. - private Object mLock = new Object(); + private final Object mLock = new Object(); + private final DistracterFilter mDistracterFilter; - private static final String[] dictTypesOrderedToGetSuggestion = + private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = new String[] { Dictionary.TYPE_MAIN, Dictionary.TYPE_USER_HISTORY, Dictionary.TYPE_PERSONALIZATION, Dictionary.TYPE_USER, - Dictionary.TYPE_CONTACTS + Dictionary.TYPE_CONTACTS, + Dictionary.TYPE_CONTEXTUAL }; + public static final Map<String, Class<? extends ExpandableBinaryDictionary>> + DICT_TYPE_TO_CLASS = new HashMap<>(); + + static { + DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class); + DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_PERSONALIZATION, PersonalizationDictionary.class); + DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER, UserBinaryDictionary.class); + DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_CONTACTS, ContactsBinaryDictionary.class); + DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_CONTEXTUAL, ContextualDictionary.class); + } + + private static final String DICT_FACTORY_METHOD_NAME = "getDictionary"; + private static final Class<?>[] DICT_FACTORY_METHOD_ARG_TYPES = + new Class[] { Context.class, Locale.class, File.class, String.class }; + + private static final String[] SUB_DICT_TYPES = + Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */, + DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length); + /** * Class contains dictionaries for a locale. */ private static class Dictionaries { public final Locale mLocale; - public final ConcurrentHashMap<String, Dictionary> mDictMap = - CollectionUtils.newConcurrentHashMap(); + private Dictionary mMainDict; public final ConcurrentHashMap<String, ExpandableBinaryDictionary> mSubDictMap = - CollectionUtils.newConcurrentHashMap(); - // TODO: Remove sub dictionary members and use mSubDictMap. - public final UserBinaryDictionary mUserDictionary; + new ConcurrentHashMap<>(); public Dictionaries() { mLocale = null; - mUserDictionary = null; } public Dictionaries(final Locale locale, final Dictionary mainDict, - final ExpandableBinaryDictionary contactsDict, final UserBinaryDictionary userDict, - final ExpandableBinaryDictionary userHistoryDict, - final ExpandableBinaryDictionary personalizationDict) { + final Map<String, ExpandableBinaryDictionary> subDicts) { mLocale = locale; // Main dictionary can be asynchronously loaded. setMainDict(mainDict); - setSubDict(Dictionary.TYPE_CONTACTS, contactsDict); - mUserDictionary = userDict; - setSubDict(Dictionary.TYPE_USER, mUserDictionary); - setSubDict(Dictionary.TYPE_USER_HISTORY, userHistoryDict); - setSubDict(Dictionary.TYPE_PERSONALIZATION, personalizationDict); + for (final Map.Entry<String, ExpandableBinaryDictionary> entry : subDicts.entrySet()) { + setSubDict(entry.getKey(), entry.getValue()); + } } private void setSubDict(final String dictType, final ExpandableBinaryDictionary dict) { if (dict != null) { - mDictMap.put(dictType, dict); mSubDictMap.put(dictType, dict); } } public void setMainDict(final Dictionary mainDict) { // Close old dictionary if exists. Main dictionary can be assigned multiple times. - final Dictionary oldDict; - if (mainDict != null) { - oldDict = mDictMap.put(Dictionary.TYPE_MAIN, mainDict); - } else { - oldDict = mDictMap.remove(Dictionary.TYPE_MAIN); - } + final Dictionary oldDict = mMainDict; + mMainDict = mainDict; if (oldDict != null && mainDict != oldDict) { oldDict.close(); } } - public Dictionary getMainDict() { - return mDictMap.get(Dictionary.TYPE_MAIN); + public Dictionary getDict(final String dictType) { + if (Dictionary.TYPE_MAIN.equals(dictType)) { + return mMainDict; + } else { + return getSubDict(dictType); + } } public ExpandableBinaryDictionary getSubDict(final String dictType) { @@ -122,12 +144,20 @@ public class DictionaryFacilitatorForSuggest { } public boolean hasDict(final String dictType) { - return mDictMap.containsKey(dictType); + if (Dictionary.TYPE_MAIN.equals(dictType)) { + return mMainDict != null; + } else { + return mSubDictMap.containsKey(dictType); + } } public void closeDict(final String dictType) { - final Dictionary dict = mDictMap.remove(dictType); - mSubDictMap.remove(dictType); + final Dictionary dict; + if (Dictionary.TYPE_MAIN.equals(dictType)) { + dict = mMainDict; + } else { + dict = mSubDictMap.remove(dictType); + } if (dict != null) { dict.close(); } @@ -138,80 +168,104 @@ public class DictionaryFacilitatorForSuggest { public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); } - public DictionaryFacilitatorForSuggest() {} + public DictionaryFacilitator() { + mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER; + } + + public DictionaryFacilitator(final DistracterFilter distracterFilter) { + mDistracterFilter = distracterFilter; + } + + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { + mDistracterFilter.updateEnabledSubtypes(enabledSubtypes); + } public Locale getLocale() { return mDictionaries.mLocale; } + private static ExpandableBinaryDictionary getSubDict(final String dictType, + final Context context, final Locale locale, final File dictFile, + final String dictNamePrefix) { + final Class<? extends ExpandableBinaryDictionary> dictClass = + DICT_TYPE_TO_CLASS.get(dictType); + if (dictClass == null) { + return null; + } + try { + final Method factoryMethod = dictClass.getMethod(DICT_FACTORY_METHOD_NAME, + DICT_FACTORY_METHOD_ARG_TYPES); + final Object dict = factoryMethod.invoke(null /* obj */, + new Object[] { context, locale, dictFile, dictNamePrefix }); + return (ExpandableBinaryDictionary) dict; + } catch (final NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + Log.e(TAG, "Cannot create dictionary: " + dictType, e); + return null; + } + } + public void resetDictionaries(final Context context, final Locale newLocale, final boolean useContactsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, final DictionaryInitializationListener listener) { + resetDictionariesWithDictNamePrefix(context, newLocale, useContactsDict, + usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); + } + + public void resetDictionariesWithDictNamePrefix(final Context context, final Locale newLocale, + final boolean useContactsDict, final boolean usePersonalizedDicts, + final boolean forceReloadMainDictionary, + final DictionaryInitializationListener listener, + final String dictNamePrefix) { final boolean localeHasBeenChanged = !newLocale.equals(mDictionaries.mLocale); // We always try to have the main dictionary. Other dictionaries can be unused. final boolean reloadMainDictionary = localeHasBeenChanged || forceReloadMainDictionary; - final boolean closeContactsDictionary = localeHasBeenChanged || !useContactsDict; - final boolean closeUserDictionary = localeHasBeenChanged; - final boolean closeUserHistoryDictionary = localeHasBeenChanged || !usePersonalizedDicts; - final boolean closePersonalizationDictionary = - localeHasBeenChanged || !usePersonalizedDicts; + // TODO: Make subDictTypesToUse configurable by resource or a static final list. + final HashSet<String> subDictTypesToUse = new HashSet<>(); + if (useContactsDict) { + subDictTypesToUse.add(Dictionary.TYPE_CONTACTS); + } + subDictTypesToUse.add(Dictionary.TYPE_USER); + if (usePersonalizedDicts) { + subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY); + subDictTypesToUse.add(Dictionary.TYPE_PERSONALIZATION); + subDictTypesToUse.add(Dictionary.TYPE_CONTEXTUAL); + } final Dictionary newMainDict; if (reloadMainDictionary) { // The main dictionary will be asynchronously loaded. newMainDict = null; } else { - newMainDict = mDictionaries.getMainDict(); - } - - // Open or move contacts dictionary. - final ExpandableBinaryDictionary newContactsDict; - if (!closeContactsDictionary && mDictionaries.hasDict(Dictionary.TYPE_CONTACTS)) { - newContactsDict = mDictionaries.getSubDict(Dictionary.TYPE_CONTACTS); - } else if (useContactsDict) { - newContactsDict = new ContactsBinaryDictionary(context, newLocale); - } else { - newContactsDict = null; - } - - // Open or move user dictionary. - final UserBinaryDictionary newUserDictionary; - if (!closeUserDictionary && mDictionaries.hasDict(Dictionary.TYPE_USER)) { - newUserDictionary = mDictionaries.mUserDictionary; - } else { - newUserDictionary = new UserBinaryDictionary(context, newLocale); - } - - // Open or move user history dictionary. - final ExpandableBinaryDictionary newUserHistoryDict; - if (!closeUserHistoryDictionary && mDictionaries.hasDict(Dictionary.TYPE_USER_HISTORY)) { - newUserHistoryDict = mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); - } else if (usePersonalizedDicts) { - newUserHistoryDict = PersonalizationHelper.getUserHistoryDictionary(context, newLocale); - } else { - newUserHistoryDict = null; + newMainDict = mDictionaries.getDict(Dictionary.TYPE_MAIN); } - // Open or move personalization dictionary. - final ExpandableBinaryDictionary newPersonalizationDict; - if (!closePersonalizationDictionary - && mDictionaries.hasDict(Dictionary.TYPE_PERSONALIZATION)) { - newPersonalizationDict = mDictionaries.getSubDict(Dictionary.TYPE_PERSONALIZATION); - } else if (usePersonalizedDicts) { - newPersonalizationDict = - PersonalizationHelper.getPersonalizationDictionary(context, newLocale); - } else { - newPersonalizationDict = null; + final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>(); + for (final String dictType : SUB_DICT_TYPES) { + if (!subDictTypesToUse.contains(dictType)) { + // This dictionary will not be used. + continue; + } + final ExpandableBinaryDictionary dict; + if (!localeHasBeenChanged && mDictionaries.hasDict(dictType)) { + // Continue to use current dictionary. + dict = mDictionaries.getSubDict(dictType); + } else { + // Start to use new dictionary. + dict = getSubDict(dictType, context, newLocale, null /* dictFile */, + dictNamePrefix); + } + subDicts.put(dictType, dict); } // Replace Dictionaries. - final Dictionaries newDictionaries = new Dictionaries(newLocale, newMainDict, - newContactsDict, newUserDictionary, newUserHistoryDict, newPersonalizationDict); + final Dictionaries newDictionaries = new Dictionaries(newLocale, newMainDict, subDicts); final Dictionaries oldDictionaries; synchronized (mLock) { oldDictionaries = mDictionaries; mDictionaries = newDictionaries; + mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); if (reloadMainDictionary) { asyncReloadMainDictionary(context, newLocale, listener); } @@ -219,24 +273,15 @@ public class DictionaryFacilitatorForSuggest { if (listener != null) { listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); } - // Clean up old dictionaries. if (reloadMainDictionary) { oldDictionaries.closeDict(Dictionary.TYPE_MAIN); } - if (closeContactsDictionary) { - oldDictionaries.closeDict(Dictionary.TYPE_CONTACTS); - } - if (closeUserDictionary) { - oldDictionaries.closeDict(Dictionary.TYPE_USER); - } - if (closeUserHistoryDictionary) { - oldDictionaries.closeDict(Dictionary.TYPE_USER_HISTORY); - } - if (closePersonalizationDictionary) { - oldDictionaries.closeDict(Dictionary.TYPE_PERSONALIZATION); + for (final String dictType : SUB_DICT_TYPES) { + if (localeHasBeenChanged || !subDictTypesToUse.contains(dictType)) { + oldDictionaries.closeDict(dictType); + } } - oldDictionaries.mDictMap.clear(); oldDictionaries.mSubDictMap.clear(); } @@ -270,52 +315,28 @@ public class DictionaryFacilitatorForSuggest { final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles, final Map<String, Map<String, String>> additionalDictAttributes) { Dictionary mainDictionary = null; - ContactsBinaryDictionary contactsDictionary = null; - UserBinaryDictionary userDictionary = null; - UserHistoryDictionary userHistoryDictionary = null; - PersonalizationDictionary personalizationDictionary = null; + final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>(); for (final String dictType : dictionaryTypes) { if (dictType.equals(Dictionary.TYPE_MAIN)) { mainDictionary = DictionaryFactory.createMainDictionaryFromManager(context, locale); - } else if (dictType.equals(Dictionary.TYPE_USER_HISTORY)) { - userHistoryDictionary = - PersonalizationHelper.getUserHistoryDictionary(context, locale); - // Staring with an empty user history dictionary for testing. - // Testing program may populate this dictionary before actual testing. - userHistoryDictionary.reloadDictionaryIfRequired(); - userHistoryDictionary.waitAllTasksForTests(); + } else { + final File dictFile = dictionaryFiles.get(dictType); + final ExpandableBinaryDictionary dict = getSubDict( + dictType, context, locale, dictFile, "" /* dictNamePrefix */); if (additionalDictAttributes.containsKey(dictType)) { - userHistoryDictionary.clearAndFlushDictionaryWithAdditionalAttributes( + dict.clearAndFlushDictionaryWithAdditionalAttributes( additionalDictAttributes.get(dictType)); } - } else if (dictType.equals(Dictionary.TYPE_PERSONALIZATION)) { - personalizationDictionary = - PersonalizationHelper.getPersonalizationDictionary(context, locale); - // Staring with an empty personalization dictionary for testing. - // Testing program may populate this dictionary before actual testing. - personalizationDictionary.reloadDictionaryIfRequired(); - personalizationDictionary.waitAllTasksForTests(); - if (additionalDictAttributes.containsKey(dictType)) { - personalizationDictionary.clearAndFlushDictionaryWithAdditionalAttributes( - additionalDictAttributes.get(dictType)); + if (dict == null) { + throw new RuntimeException("Unknown dictionary type: " + dictType); } - } else if (dictType.equals(Dictionary.TYPE_USER)) { - final File file = dictionaryFiles.get(dictType); - userDictionary = new UserBinaryDictionary(context, locale, file); - userDictionary.reloadDictionaryIfRequired(); - userDictionary.waitAllTasksForTests(); - } else if (dictType.equals(Dictionary.TYPE_CONTACTS)) { - final File file = dictionaryFiles.get(dictType); - contactsDictionary = new ContactsBinaryDictionary(context, locale, file); - contactsDictionary.reloadDictionaryIfRequired(); - contactsDictionary.waitAllTasksForTests(); - } else { - throw new RuntimeException("Unknown dictionary type: " + dictType); + dict.reloadDictionaryIfRequired(); + dict.waitAllTasksForTests(); + subDicts.put(dictType, dict); } } - mDictionaries = new Dictionaries(locale, mainDictionary, contactsDictionary, - userDictionary, userHistoryDictionary, personalizationDictionary); + mDictionaries = new Dictionaries(locale, mainDictionary, subDicts); } public void closeDictionaries() { @@ -324,15 +345,16 @@ public class DictionaryFacilitatorForSuggest { dictionaries = mDictionaries; mDictionaries = new Dictionaries(); } - for (final Dictionary dict : dictionaries.mDictMap.values()) { - dict.close(); + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + dictionaries.closeDict(dictType); } + mDistracterFilter.close(); } // The main dictionary could have been loaded asynchronously. Don't cache the return value // of this method. public boolean hasInitializedMainDictionary() { - final Dictionary mainDict = mDictionaries.getMainDict(); + final Dictionary mainDict = mDictionaries.getDict(Dictionary.TYPE_MAIN); return mainDict != null && mainDict.isInitialized(); } @@ -364,48 +386,59 @@ public class DictionaryFacilitatorForSuggest { } public boolean isUserDictionaryEnabled() { - final UserBinaryDictionary userDictionary = mDictionaries.mUserDictionary; - if (userDictionary == null) { - return false; - } - return userDictionary.mEnabled; + return mIsUserDictEnabled; } - public void addWordToUserDictionary(String word) { - final UserBinaryDictionary userDictionary = mDictionaries.mUserDictionary; - if (userDictionary == null) { + public void addWordToUserDictionary(final Context context, final String word) { + final Locale locale = getLocale(); + if (locale == null) { return; } - userDictionary.addWordToUserDictionary(word); + UserBinaryDictionary.addWordToUserDictionary(context, locale, word); } public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, - final String previousWord, final int timeStampInSeconds) { + final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, + final boolean blockPotentiallyOffensive) { final Dictionaries dictionaries = mDictionaries; + final String[] words = suggestion.split(Constants.WORD_SEPARATOR); + for (int i = 0; i < words.length; i++) { + final String currentWord = words[i]; + final PrevWordsInfo prevWordsInfoForCurrentWord = + (i == 0) ? prevWordsInfo : new PrevWordsInfo(words[i - 1]); + final boolean wasCurrentWordAutoCapitalized = (i == 0) ? wasAutoCapitalized : false; + addWordToUserHistory(dictionaries, prevWordsInfoForCurrentWord, currentWord, + wasCurrentWordAutoCapitalized, timeStampInSeconds, blockPotentiallyOffensive); + } + } + + private void addWordToUserHistory(final Dictionaries dictionaries, + final PrevWordsInfo prevWordsInfo, final String word, final boolean wasAutoCapitalized, + final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { final ExpandableBinaryDictionary userHistoryDictionary = dictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); if (userHistoryDictionary == null) { return; } - final int maxFreq = getMaxFrequency(suggestion); - if (maxFreq == 0) { + final int maxFreq = getFrequency(word); + if (maxFreq == 0 && blockPotentiallyOffensive) { return; } - final String suggestionLowerCase = suggestion.toLowerCase(dictionaries.mLocale); + final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale); final String secondWord; if (wasAutoCapitalized) { - if (isValidWord(suggestion, false /* ignoreCase */) - && !isValidWord(suggestionLowerCase, false /* ignoreCase */)) { + if (isValidWord(word, false /* ignoreCase */) + && !isValidWord(lowerCasedWord, false /* ignoreCase */)) { // If the word was auto-capitalized and exists only as a capitalized word in the // dictionary, then we must not downcase it before registering it. For example, // the name of the contacts in start-of-sentence position would come here with the // wasAutoCapitalized flag: if we downcase it, we'd register a lower-case version // of that contact's name which would end up popping in suggestions. - secondWord = suggestion; + secondWord = word; } else { // If however the word is not in the dictionary, or exists as a lower-case word // only, then we consider that was a lower-case word that had been auto-capitalized. - secondWord = suggestionLowerCase; + secondWord = lowerCasedWord; } } else { // HACK: We'd like to avoid adding the capitalized form of common words to the User @@ -413,46 +446,46 @@ public class DictionaryFacilitatorForSuggest { // consolidation is done. // TODO: Remove this hack when ready. final int lowerCaseFreqInMainDict = dictionaries.hasDict(Dictionary.TYPE_MAIN) ? - dictionaries.getMainDict().getFrequency(suggestionLowerCase) : + dictionaries.getDict(Dictionary.TYPE_MAIN).getFrequency(lowerCasedWord) : Dictionary.NOT_A_PROBABILITY; if (maxFreq < lowerCaseFreqInMainDict && lowerCaseFreqInMainDict >= CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT) { // Use lower cased word as the word can be a distracter of the popular word. - secondWord = suggestionLowerCase; + secondWord = lowerCasedWord; } else { - secondWord = suggestion; + secondWord = word; } } // We demote unrecognized words (frequency < 0, below) by specifying them as "invalid". // We don't add words with 0-frequency (assuming they would be profanity etc.). final boolean isValid = maxFreq > 0; - UserHistoryDictionary.addToDictionary(userHistoryDictionary, previousWord, secondWord, - isValid, timeStampInSeconds); + UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWordsInfo, secondWord, + isValid, timeStampInSeconds, mDistracterFilter); } - public void cancelAddingUserHistory(final String previousWord, final String committedWord) { + public void cancelAddingUserHistory(final PrevWordsInfo prevWordsInfo, + final String committedWord) { final ExpandableBinaryDictionary userHistoryDictionary = mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); if (userHistoryDictionary != null) { - userHistoryDictionary.removeBigramDynamically(previousWord, committedWord); + userHistoryDictionary.removeNgramDynamically(prevWordsInfo, committedWord); } } // TODO: Revise the way to fusion suggestion results. public SuggestionResults getSuggestionResults(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final ArrayList<SuggestedWordInfo> rawSuggestions) { final Dictionaries dictionaries = mDictionaries; - final Map<String, Dictionary> dictMap = dictionaries.mDictMap; final SuggestionResults suggestionResults = new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; - for (final String dictType : dictTypesOrderedToGetSuggestion) { - final Dictionary dictionary = dictMap.get(dictType); + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaries.getDict(dictType); if (null == dictionary) continue; final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestionsWithSessionId(composer, prevWord, proximityInfo, + dictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, sessionId, languageWeight); if (null == dictionarySuggestions) continue; @@ -465,11 +498,11 @@ public class DictionaryFacilitatorForSuggest { } public boolean isValidMainDictWord(final String word) { - final Dictionaries dictionaries = mDictionaries; - if (TextUtils.isEmpty(word) || !dictionaries.hasDict(Dictionary.TYPE_MAIN)) { + final Dictionary mainDict = mDictionaries.getDict(Dictionary.TYPE_MAIN); + if (TextUtils.isEmpty(word) || mainDict == null) { return false; } - return dictionaries.getMainDict().isValidWord(word); + return mainDict.isValidWord(word); } public boolean isValidWord(final String word, final boolean ignoreCase) { @@ -481,8 +514,8 @@ public class DictionaryFacilitatorForSuggest { return false; } final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale); - final Map<String, Dictionary> dictMap = dictionaries.mDictMap; - for (final Dictionary dictionary : dictMap.values()) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaries.getDict(dictType); // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and // would be immutable once it's finished initializing, but concretely a null test is // probably good enough for the time being. @@ -495,14 +528,22 @@ public class DictionaryFacilitatorForSuggest { return false; } - private int getMaxFrequency(final String word) { + private int getFrequencyInternal(final String word, + final boolean isGettingMaxFrequencyOfExactMatches) { if (TextUtils.isEmpty(word)) { return Dictionary.NOT_A_PROBABILITY; } - int maxFreq = -1; - final Map<String, Dictionary> dictMap = mDictionaries.mDictMap; - for (final Dictionary dictionary : dictMap.values()) { - final int tempFreq = dictionary.getFrequency(word); + int maxFreq = Dictionary.NOT_A_PROBABILITY; + final Dictionaries dictionaries = mDictionaries; + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaries.getDict(dictType); + if (dictionary == null) continue; + final int tempFreq; + if (isGettingMaxFrequencyOfExactMatches) { + tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); + } else { + tempFreq = dictionary.getFrequency(word); + } if (tempFreq >= maxFreq) { maxFreq = tempFreq; } @@ -510,6 +551,14 @@ public class DictionaryFacilitatorForSuggest { return maxFreq; } + public int getFrequency(final String word) { + return getFrequencyInternal(word, false /* isGettingMaxFrequencyOfExactMatches */); + } + + public int getMaxFrequencyOfExactMatches(final String word) { + return getFrequencyInternal(word, true /* isGettingMaxFrequencyOfExactMatches */); + } + public void clearUserHistoryDictionary() { final ExpandableBinaryDictionary userHistoryDict = mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); @@ -530,13 +579,26 @@ public class DictionaryFacilitatorForSuggest { personalizationDict.clear(); } - public void addMultipleDictionaryEntriesToPersonalizationDictionary( - final ArrayList<LanguageModelParam> languageModelParams, + public void addEntriesToPersonalizationDictionary( + final PersonalizationDataChunk personalizationDataChunk, + final SpacingAndPunctuations spacingAndPunctuations, final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) { final ExpandableBinaryDictionary personalizationDict = mDictionaries.getSubDict(Dictionary.TYPE_PERSONALIZATION); - if (personalizationDict == null || languageModelParams == null - || languageModelParams.isEmpty()) { + if (personalizationDict == null) { + if (callback != null) { + callback.onFinished(); + } + return; + } + final ArrayList<LanguageModelParam> languageModelParams = + LanguageModelParam.createLanguageModelParamsFrom( + personalizationDataChunk.mTokens, + personalizationDataChunk.mTimestampInSeconds, + this /* dictionaryFacilitator */, spacingAndPunctuations, + new DistracterFilterCheckingIsInDictionary( + mDistracterFilter, personalizationDict)); + if (languageModelParams == null || languageModelParams.isEmpty()) { if (callback != null) { callback.onFinished(); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index e09c309ea..59de4f82a 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -23,7 +23,6 @@ import android.content.res.Resources; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import java.io.File; @@ -55,7 +54,7 @@ public final class DictionaryFactory { createReadOnlyBinaryDictionary(context, locale)); } - final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList(); + final LinkedList<Dictionary> dictList = new LinkedList<>(); final ArrayList<AssetFileAddress> assetFileList = BinaryDictionaryGetter.getDictionaryFiles(locale, context); if (null != assetFileList) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 550db4a6c..b1966bffc 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -27,6 +27,7 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 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.LanguageModelParam; @@ -48,12 +49,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * queries in native code. This binary dictionary is written to internal storage. */ abstract public class ExpandableBinaryDictionary extends Dictionary { + private static final boolean DEBUG = false; /** Used for Log actions from this class */ private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName(); /** Whether to print debug output to log */ - private static boolean DEBUG = false; private static final boolean DBG_STRESS_TEST = false; private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; @@ -92,11 +93,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Indicates whether a task for reloading the dictionary has been scheduled. */ private final AtomicBoolean mIsReloading; - /** Indicates whether the current dictionary needs to be reloaded. */ - private boolean mNeedsToReload; + /** Indicates whether the current dictionary needs to be recreated. */ + private boolean mNeedsToRecreate; private final ReentrantReadWriteLock mLock; + private Map<String, String> mAdditionalAttributeMap = null; + /* A extension for a binary dictionary file. */ protected static final String DICT_FILE_EXTENSION = ".dict"; @@ -105,26 +108,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected abstract void loadInitialContentsLocked(); - /** - * Indicates that the source dictionary contents have changed and a rebuild of the binary file - * is required. If it returns false, the next reload will only read the current binary - * dictionary from file. - */ - protected abstract boolean haveContentsChanged(); - private boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { return formatVersion == FormatSpec.VERSION4; } private boolean needsToMigrateDictionary(final int formatVersion) { - // TODO: Check version. - return false; + // 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; } public boolean isValidDictionaryLocked() { return mBinaryDictionary.isValidDictionary(); } + // TODO: Remove and always enable beginning of sentence prediction. Currently, this is enabled + // only for ContextualDictionary. + protected boolean enableBeginningOfSentencePrediction() { + return false; + } + /** * Creates a new expandable binary dictionary. * @@ -145,7 +148,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mDictFile = getDictFile(context, dictName, dictFile); mBinaryDictionary = null; mIsReloading = new AtomicBoolean(); - mNeedsToReload = false; + mNeedsToRecreate = false; mLock = new ReentrantReadWriteLock(); } @@ -195,7 +198,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } protected Map<String, String> getHeaderAttributeMap() { - HashMap<String, String> attributeMap = new HashMap<String, String>(); + HashMap<String, String> attributeMap = new HashMap<>(); + if (mAdditionalAttributeMap != null) { + attributeMap.putAll(mAdditionalAttributeMap); + } attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, mDictName); attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, @@ -270,11 +276,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** - * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry. + * Adds unigram information of a word to the dictionary. May overwrite an existing entry. */ - public void addWordDynamically(final String word, final int frequency, + public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp) { + final boolean isBlacklisted, final int timestamp, + final DistracterFilter distracterFilter) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @Override @@ -282,24 +289,31 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { if (mBinaryDictionary == null) { return; } + if (distracterFilter.isDistracterToWordsInDictionaries( + PrevWordsInfo.EMPTY_PREV_WORDS_INFO, word, mLocale)) { + // The word is a distracter. + return; + } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addWordDynamicallyLocked(word, frequency, shortcutTarget, shortcutFreq, + addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, isNotAWord, isBlacklisted, timestamp); } }); } - protected void addWordDynamicallyLocked(final String word, final int frequency, + protected void addUnigramLocked(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, final boolean isBlacklisted, final int timestamp) { - mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq, - isNotAWord, isBlacklisted, timestamp); + if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq, + false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp)) { + Log.e(TAG, "Cannot add unigram entry. word: " + word); + } } /** - * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry. + * Adds n-gram information of a word to the dictionary. May overwrite an existing entry. */ - public void addBigramDynamically(final String word0, final String word1, + public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word, final int frequency, final int timestamp) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @@ -309,20 +323,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - addBigramDynamicallyLocked(word0, word1, frequency, timestamp); + addNgramEntryLocked(prevWordsInfo, word, frequency, timestamp); } }); } - protected void addBigramDynamicallyLocked(final String word0, final String word1, + protected void addNgramEntryLocked(final PrevWordsInfo prevWordsInfo, final String word, final int frequency, final int timestamp) { - mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp); + if (!mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp)) { + if (DEBUG) { + Log.i(TAG, "Cannot add n-gram entry."); + Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word); + } + } } /** - * Dynamically remove a word bigram in the dictionary. + * Dynamically remove the n-gram entry in the dictionary. */ - public void removeBigramDynamically(final String word0, final String word1) { + public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @Override @@ -331,7 +350,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - mBinaryDictionary.removeBigramWords(word0, word1); + if (!mBinaryDictionary.removeNgramEntry(prevWordsInfo, word)) { + if (DEBUG) { + Log.i(TAG, "Cannot remove n-gram entry."); + Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word); + } + } } }); } @@ -367,8 +391,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { reloadDictionaryIfRequired(); @@ -380,10 +404,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { if (mBinaryDictionary == null) { return null; } + if (composer.size() == 0 && prevWordsInfo.mIsBeginningOfSentence + && !enableBeginningOfSentencePrediction()) { + return null; + } final ArrayList<SuggestedWordInfo> suggestions = - mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, - proximityInfo, blockOffensiveWords, additionalFeaturesOptions, - sessionId, inOutLanguageWeight); + mBinaryDictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, + inOutLanguageWeight); if (mBinaryDictionary.isCorrupted()) { Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. " + "Remove and regenerate it."); @@ -402,16 +430,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { - return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, 0 /* sessionId */, inOutLanguageWeight); - } - - @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { reloadDictionaryIfRequired(); boolean lockAcquired = false; try { @@ -421,10 +440,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { if (mBinaryDictionary == null) { return false; } - return isValidWordLocked(word); + return isInDictionaryLocked(word); } } catch (final InterruptedException e) { - Log.e(TAG, "Interrupted tryLock() in isValidWord().", e); + Log.e(TAG, "Interrupted tryLock() in isInDictionary().", e); } finally { if (lockAcquired) { mLock.readLock().unlock(); @@ -433,14 +452,38 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return false; } - protected boolean isValidWordLocked(final String word) { + protected boolean isInDictionaryLocked(final String word) { if (mBinaryDictionary == null) return false; - return mBinaryDictionary.isValidWord(word); + return mBinaryDictionary.isInDictionary(word); } - protected boolean isValidBigramLocked(final String word1, final String word2) { + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + reloadDictionaryIfRequired(); + boolean lockAcquired = false; + try { + lockAcquired = mLock.readLock().tryLock( + TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS); + if (lockAcquired) { + if (mBinaryDictionary == null) { + return NOT_A_PROBABILITY; + } + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e); + } finally { + if (lockAcquired) { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + + protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) { if (mBinaryDictionary == null) return false; - return mBinaryDictionary.isValidBigram(word1, word2); + return mBinaryDictionary.isValidNgram(prevWordsInfo, word); } /** @@ -465,7 +508,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } if (mBinaryDictionary.isValidDictionary() && needsToMigrateDictionary(mBinaryDictionary.getFormatVersion())) { - mBinaryDictionary.migrateTo(DICTIONARY_FORMAT_VERSION); + if (!mBinaryDictionary.migrateTo(DICTIONARY_FORMAT_VERSION)) { + Log.e(TAG, "Dictionary migration failed: " + mDictName); + removeBinaryDictionaryLocked(); + } } } @@ -481,11 +527,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** - * Marks that the dictionary needs to be reloaded. + * Marks that the dictionary needs to be recreated. * */ - protected void setNeedsToReload() { - mNeedsToReload = true; + protected void setNeedsToRecreate() { + mNeedsToRecreate = true; } /** @@ -503,7 +549,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Returns whether a dictionary reload is required. */ private boolean isReloadRequired() { - return mBinaryDictionary == null || mNeedsToReload; + return mBinaryDictionary == null || mNeedsToRecreate; } /** @@ -515,25 +561,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public void run() { try { - // TODO: Quit checking contents in ExpandableBinaryDictionary. - if (!mDictFile.exists() || (mNeedsToReload && haveContentsChanged())) { + if (!mDictFile.exists() || mNeedsToRecreate) { // If the dictionary file does not exist or contents have been updated, // generate a new one. createNewDictionaryLocked(); } else if (mBinaryDictionary == null) { // Otherwise, load the existing dictionary. loadBinaryDictionaryLocked(); + if (mBinaryDictionary != null && !(isValidDictionaryLocked() + // TODO: remove the check below + && matchesExpectedBinaryDictFormatVersionForThisType( + mBinaryDictionary.getFormatVersion()))) { + // Binary dictionary or its format version is not valid. Regenerate + // the dictionary file. createNewDictionaryLocked will remove the + // existing files if appropriate. + createNewDictionaryLocked(); + } } - mNeedsToReload = false; - if (mBinaryDictionary != null && !(isValidDictionaryLocked() - // TODO: remove the check below - && matchesExpectedBinaryDictFormatVersionForThisType( - mBinaryDictionary.getFormatVersion()))) { - // Binary dictionary or its format version is not valid. Regenerate - // the dictionary file. writeBinaryDictionary will remove the - // existing files if appropriate. - createNewDictionaryLocked(); - } + mNeedsToRecreate = false; } finally { mIsReloading.set(false); } @@ -561,20 +606,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } - // TODO: Implement BinaryDictionary.isInDictionary(). - @UsedForTesting - public boolean isInUnderlyingBinaryDictionaryForTests(final String word) { - mLock.readLock().lock(); - try { - if (mBinaryDictionary != null && mDictType == Dictionary.TYPE_USER_HISTORY) { - return mBinaryDictionary.isValidWord(word); - } - return false; - } finally { - mLock.readLock().unlock(); - } - } - @UsedForTesting public void waitAllTasksForTests() { final CountDownLatch countDownLatch = new CountDownLatch(1); @@ -592,6 +623,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @UsedForTesting + public void clearAndFlushDictionaryWithAdditionalAttributes( + final Map<String, String> attributeMap) { + mAdditionalAttributeMap = attributeMap; + clear(); + } + public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { @@ -600,6 +637,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { Log.d(TAG, "Dump dictionary: " + mDictName); try { final DictionaryHeader header = mBinaryDictionary.getHeader(); + Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); Log.d(TAG, CombinedFormatUtils.formatAttributeMap( header.mDictionaryOptions.mAttributes)); } catch (final UnsupportedFormatException e) { diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 726b3d141..e1ae3dfe3 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,11 +16,13 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; + import android.text.InputType; import android.util.Log; import android.view.inputmethod.EditorInfo; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.StringUtils; @@ -36,16 +38,23 @@ public final class InputAttributes { final public String mTargetApplicationPackageName; final public boolean mInputTypeNoAutoCorrect; final public boolean mIsPasswordField; - final public boolean mIsSettingsSuggestionStripOn; + final public boolean mShouldShowSuggestions; final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final private int mInputType; + final private EditorInfo mEditorInfo; + final private String mPackageNameForPrivateImeOptions; - public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { + public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode, + final String packageNameForPrivateImeOptions) { + mEditorInfo = editorInfo; + mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions; mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null; final int inputType = null != editorInfo ? editorInfo.inputType : 0; final int inputClass = inputType & InputType.TYPE_MASK_CLASS; mInputType = inputType; + mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType) + || InputTypeUtils.isVisiblePasswordInputType(inputType); if (inputClass != InputType.TYPE_CLASS_TEXT) { // If we are not looking at a TYPE_CLASS_TEXT field, the following strange // cases may arise, so we do a couple sanity checks for them. If it's a @@ -61,8 +70,7 @@ public final class InputAttributes { Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); } - mIsPasswordField = false; - mIsSettingsSuggestionStripOn = false; + mShouldShowSuggestions = false; mInputTypeNoAutoCorrect = false; mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; @@ -79,17 +87,15 @@ public final class InputAttributes { final boolean flagAutoComplete = 0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); - mIsPasswordField = InputTypeUtils.isPasswordInputType(inputType) - || InputTypeUtils.isVisiblePasswordInputType(inputType); // TODO: Have a helper method in InputTypeUtils // Make sure that passwords are not displayed in {@link SuggestionStripView}. - final boolean noSuggestionStrip = mIsPasswordField + final boolean shouldSuppressSuggestions = mIsPasswordField || InputTypeUtils.isEmailVariation(variation) || InputType.TYPE_TEXT_VARIATION_URI == variation || InputType.TYPE_TEXT_VARIATION_FILTER == variation || flagNoSuggestions || flagAutoComplete; - mIsSettingsSuggestionStripOn = !noSuggestionStrip; + mShouldShowSuggestions = !shouldSuppressSuggestions; mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); @@ -113,6 +119,15 @@ public final class InputAttributes { return editorInfo.inputType == mInputType; } + public boolean hasNoMicrophoneKeyOption() { + @SuppressWarnings("deprecation") + final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( + null, NO_MICROPHONE_COMPAT, mEditorInfo); + final boolean noMicrophone = InputAttributes.inPrivateImeOptions( + mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo); + return noMicrophone || deprecatedNoMicrophone; + } + @SuppressWarnings("unused") private void dumpFlags(final int inputType) { final int inputClass = inputType & InputType.TYPE_MASK_CLASS; @@ -215,7 +230,7 @@ public final class InputAttributes { } private static String toFlagsString(final int flags) { - final ArrayList<String> flagsArray = CollectionUtils.newArrayList(); + final ArrayList<String> flagsArray = new ArrayList<>(); if (0 != (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS)) flagsArray.add("TYPE_TEXT_FLAG_NO_SUGGESTIONS"); if (0 != (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE)) @@ -243,7 +258,7 @@ public final class InputAttributes { mInputType, (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""), (mIsPasswordField ? " password" : ""), - (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""), + (mShouldShowSuggestions ? " shouldShowSuggestions" : ""), (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""), (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""), mTargetApplicationPackageName); diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java index ea7859e60..0801cfa88 100644 --- a/java/src/com/android/inputmethod/latin/InputView.java +++ b/java/src/com/android/inputmethod/latin/InputView.java @@ -23,12 +23,14 @@ import android.view.MotionEvent; import android.view.View; import android.widget.LinearLayout; +import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.suggestions.MoreSuggestionsView; import com.android.inputmethod.latin.suggestions.SuggestionStripView; public final class InputView extends LinearLayout { private final Rect mInputViewRect = new Rect(); + private MainKeyboardView mMainKeyboardView; private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder; private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler; private MotionEventForwarder<?, ?> mActiveForwarder; @@ -41,12 +43,11 @@ public final class InputView extends LinearLayout { protected void onFinishInflate() { final SuggestionStripView suggestionStripView = (SuggestionStripView)findViewById(R.id.suggestion_strip_view); - final MainKeyboardView mainKeyboardView = - (MainKeyboardView)findViewById(R.id.keyboard_view); + mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view); mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder( - mainKeyboardView, suggestionStripView); + mMainKeyboardView, suggestionStripView); mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler( - mainKeyboardView, suggestionStripView); + mMainKeyboardView, suggestionStripView); } public void setKeyboardTopPadding(final int keyboardTopPadding) { @@ -54,6 +55,17 @@ public final class InputView extends LinearLayout { } @Override + protected boolean dispatchHoverEvent(final MotionEvent event) { + if (AccessibilityUtils.getInstance().isTouchExplorationEnabled() + && mMainKeyboardView.isShowingMoreKeysPanel()) { + // With accessibility mode on, discard hover events while a more keys keyboard is shown. + // The {@link MoreKeysKeyboard} receives hover events directly from the platform. + return true; + } + return super.dispatchHoverEvent(event); + } + + @Override public boolean onInterceptTouchEvent(final MotionEvent me) { final Rect rect = mInputViewRect; getGlobalVisibleRect(rect); diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 232bf7407..8cbf8379b 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -48,7 +48,7 @@ public final class LastComposedWord { public final String mTypedWord; public final CharSequence mCommittedWord; public final String mSeparatorString; - public final String mPrevWord; + public final PrevWordsInfo mPrevWordsInfo; public final int mCapitalizedMode; public final InputPointers mInputPointers = new InputPointers(Constants.DICTIONARY_MAX_WORD_LENGTH); @@ -64,16 +64,16 @@ public final class LastComposedWord { public LastComposedWord(final ArrayList<Event> events, final InputPointers inputPointers, final String typedWord, final CharSequence committedWord, final String separatorString, - final String prevWord, final int capitalizedMode) { + final PrevWordsInfo prevWordsInfo, final int capitalizedMode) { if (inputPointers != null) { mInputPointers.copy(inputPointers); } mTypedWord = typedWord; - mEvents = new ArrayList<Event>(events); + mEvents = new ArrayList<>(events); mCommittedWord = committedWord; mSeparatorString = separatorString; mActive = true; - mPrevWord = prevWord; + mPrevWordsInfo = prevWordsInfo; mCapitalizedMode = capitalizedMode; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index f1b1b8db2..8b671a94b 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -28,7 +28,6 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; @@ -38,7 +37,6 @@ import android.net.ConnectivityManager; import android.os.Debug; import android.os.IBinder; import android.os.Message; -import android.preference.PreferenceManager; import android.text.InputType; import android.text.TextUtils; import android.util.Log; @@ -55,7 +53,6 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; -import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; @@ -84,18 +81,18 @@ 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.DialogUtils; -import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.StatsUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; -import com.android.inputmethod.research.ResearchLogger; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; @@ -104,7 +101,7 @@ import java.util.concurrent.TimeUnit; */ public class LatinIME extends InputMethodService implements KeyboardActionListener, SuggestionStripView.Listener, SuggestionStripViewAccessor, - DictionaryFacilitatorForSuggest.DictionaryInitializationListener, + DictionaryFacilitator.DictionaryInitializationListener, ImportantNoticeDialog.ImportantNoticeDialogListener { private static final String TAG = LatinIME.class.getSimpleName(); private static final boolean TRACE = false; @@ -123,12 +120,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final String SCHEME_PACKAGE = "package"; private final Settings mSettings; + private final DictionaryFacilitator mDictionaryFacilitator = + new DictionaryFacilitator(new DistracterFilterCheckingExactMatches(this /* context */)); private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, - this /* SuggestionStripViewAccessor */); + this /* SuggestionStripViewAccessor */, mDictionaryFacilitator); // We expect to have only one decoder in almost all cases, hence the default capacity of 1. // If it turns out we need several, it will get grown seamlessly. - final SparseArray<HardwareEventDecoder> mHardwareEventDecoders - = new SparseArray<HardwareEventDecoder>(1); + final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1); private View mExtractArea; private View mKeyPreviewBackingView; @@ -140,10 +138,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final SubtypeState mSubtypeState = new SubtypeState(); // Object for reacting to adding/removing a dictionary pack. - private BroadcastReceiver mDictionaryPackInstallReceiver = + private final BroadcastReceiver mDictionaryPackInstallReceiver = new DictionaryPackInstallBroadcastReceiver(this); - private BroadcastReceiver mDictionaryDumpBroadcastReceiver = + private final BroadcastReceiver mDictionaryDumpBroadcastReceiver = new DictionaryDumpBroadcastReceiver(this); private AlertDialog mOptionsDialog; @@ -168,6 +166,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private static final int ARG2_UNUSED = 0; + private static final int ARG1_FALSE = 0; + private static final int ARG1_TRUE = 1; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -215,7 +215,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_RESUME_SUGGESTIONS: latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( latinIme.mSettings.getCurrent(), - false /* includeResumedWordInSuggestions */); + msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */); break; case MSG_REOPEN_DICTIONARIES: latinIme.resetSuggest(); @@ -252,16 +252,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions() { + public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; } - if (!latinIme.mSettings.getCurrent().isSuggestionStripVisible()) { + if (!latinIme.mSettings.getCurrent() + .isCurrentOrientationAllowingSuggestionsPerUserSettings()) { return; } removeMessages(MSG_RESUME_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); + sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */), + mDelayUpdateSuggestions); } public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { @@ -481,6 +485,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen KeyboardSwitcher.init(this); AudioAndHapticFeedbackManager.init(this); AccessibilityUtils.init(this); + StatsUtils.init(this); super.onCreate(); @@ -491,12 +496,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen loadSettings(); resetSuggest(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().init(this, mKeyboardSwitcher); - ResearchLogger.getInstance().initDictionary( - mInputLogic.mSuggest.mDictionaryFacilitator); - } - // Register to receive ringer mode change and network state change. // Also receive installation and removal of a dictionary pack. final IntentFilter filter = new IntentFilter(); @@ -520,7 +519,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this); - StatsUtils.onCreateCompleted(this); + StatsUtils.onCreate(mSettings.getCurrent()); } // Has to be package-visible for unit tests @@ -528,7 +527,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen void loadSettings() { final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final EditorInfo editorInfo = getCurrentInputEditorInfo(); - final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); + final InputAttributes inputAttributes = new InputAttributes( + editorInfo, isFullscreenMode(), getPackageName()); mSettings.loadSettings(this, locale, inputAttributes); final SettingsValues currentSettingsValues = mSettings.getCurrent(); AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues); @@ -538,24 +538,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!mHandler.hasPendingReopenDictionaries()) { resetSuggestForLocale(locale); } + mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList( + true /* allowsImplicitlySelectedSubtypes */)); refreshPersonalizationDictionarySession(); - } - - private DistracterFilter createDistracterFilter() { - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - // TODO: Create Keyboard when mainKeyboardView is null. - // TODO: Figure out the most reasonable keyboard for the filter. Refer to the - // spellchecker's logic. - final Keyboard keyboard = (mainKeyboardView != null) ? - mainKeyboardView.getKeyboard() : null; - final DistracterFilter distracterFilter = new DistracterFilter(mInputLogic.mSuggest, - keyboard); - return distracterFilter; + StatsUtils.onLoadSettings(currentSettingsValues); } private void refreshPersonalizationDictionarySession() { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - mInputLogic.mSuggest.mDictionaryFacilitator; final boolean shouldKeepUserHistoryDictionaries; final boolean shouldKeepPersonalizationDictionaries; if (mSettings.getCurrent().mUsePersonalizedDicts) { @@ -570,16 +559,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!shouldKeepUserHistoryDictionaries) { // Remove user history dictionaries. PersonalizationHelper.removeAllUserHistoryDictionaries(this); - dictionaryFacilitator.clearUserHistoryDictionary(); + mDictionaryFacilitator.clearUserHistoryDictionary(); } if (!shouldKeepPersonalizationDictionaries) { // Remove personalization dictionaries. PersonalizationHelper.removeAllPersonalizationDictionaries(this); PersonalizationDictionarySessionRegistrar.resetAll(this); } else { - final DistracterFilter distracterFilter = createDistracterFilter(); - PersonalizationDictionarySessionRegistrar.init( - this, dictionaryFacilitator, distracterFilter); + PersonalizationDictionarySessionRegistrar.init(this, mDictionaryFacilitator); } } @@ -617,13 +604,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * @param locale the locale */ private void resetSuggestForLocale(final Locale locale) { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - mInputLogic.mSuggest.mDictionaryFacilitator; final SettingsValues settingsValues = mSettings.getCurrent(); - dictionaryFacilitator.resetDictionaries(this /* context */, locale, + mDictionaryFacilitator.resetDictionaries(this /* context */, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, false /* forceReloadMainDictionary */, this); - if (settingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { mInputLogic.mSuggest.setAutoCorrectionThreshold( settingsValues.mAutoCorrectionThreshold); } @@ -633,27 +618,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * Reset suggest by loading the main dictionary of the current locale. */ /* package private */ void resetSuggestMainDict() { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - mInputLogic.mSuggest.mDictionaryFacilitator; final SettingsValues settingsValues = mSettings.getCurrent(); - dictionaryFacilitator.resetDictionaries(this /* context */, - dictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict, + mDictionaryFacilitator.resetDictionaries(this /* context */, + mDictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, this); } @Override public void onDestroy() { - mInputLogic.mSuggest.mDictionaryFacilitator.closeDictionaries(); + mDictionaryFacilitator.closeDictionaries(); mSettings.onDestroy(); unregisterReceiver(mConnectivityAndRingerModeChangeReceiver); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().onDestroy(); - } unregisterReceiver(mDictionaryPackInstallReceiver); unregisterReceiver(mDictionaryDumpBroadcastReceiver); PersonalizationDictionarySessionRegistrar.close(this); - LatinImeLogger.commit(); - LatinImeLogger.onDestroy(); StatsUtils.onDestroy(); super.onDestroy(); } @@ -668,18 +646,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(final Configuration conf) { - // If orientation changed while predicting, commit the change final SettingsValues settingsValues = mSettings.getCurrent(); if (settingsValues.mDisplayOrientation != conf.orientation) { mHandler.startOrientationChanging(); - mInputLogic.mConnection.beginBatchEdit(); - mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); - mInputLogic.mConnection.finishComposingText(); - mInputLogic.mConnection.endBatchEdit(); + // If !isComposingWord, #commitTyped() is a no-op, but still, it's better to avoid + // the useless IPC of {begin,end}BatchEdit. + if (mInputLogic.mWordComposer.isComposingWord()) { + mInputLogic.mConnection.beginBatchEdit(); + // If we had a composition in progress, we need to commit the word so that the + // suggestionsSpan will be added. This will allow resuming on the same suggestions + // after rotation is finished. + mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); + mInputLogic.mConnection.endBatchEdit(); + } } - final DistracterFilter distracterFilter = createDistracterFilter(); PersonalizationDictionarySessionRegistrar.onConfigurationChanged(this, conf, - mInputLogic.mSuggest.mDictionaryFacilitator, distracterFilter); + mDictionaryFacilitator); super.onConfigurationChanged(conf); } @@ -698,9 +680,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (hasSuggestionStripView()) { mSuggestionStripView.setListener(this, view); } - if (LatinImeLogger.sVISUALDEBUG) { - mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); - } } @Override @@ -734,6 +713,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. mSubtypeSwitcher.onSubtypeChanged(subtype); + mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype)); loadKeyboard(); } @@ -772,10 +752,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } Log.i(TAG, "Starting input. Cursor position = " + editorInfo.initialSelStart + "," + editorInfo.initialSelEnd); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs); - } + // TODO: Consolidate these checks with {@link InputAttributes}. if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions); Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead"); @@ -785,7 +762,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead"); } - LatinImeLogger.onStartInputView(editorInfo); // In landscape mode, this method gets called without the input view being created. if (mainKeyboardView == null) { return; @@ -809,7 +785,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // The app calling setText() has the effect of clearing the composing // span, so we should reset our state unconditionally, even if restarting is true. - mInputLogic.startInput(restarting, editorInfo); + // We also tell the input logic about the combining rules for the current subtype, so + // it can adjust its combiners if needed. + mInputLogic.startInput(restarting, editorInfo, + mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype()); // Note: the following does a round-trip IPC on the main thread: be careful final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); @@ -835,7 +814,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best // effort to work around this bug. mInputLogic.mConnection.tryFixLyingCursorPosition(); - mHandler.postResumeSuggestions(); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); canReachInputConnection = true; } @@ -847,8 +826,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.closing(); currentSettingsValues = mSettings.getCurrent(); - if (currentSettingsValues.mCorrectionEnabled) { - suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold); + if (currentSettingsValues.mAutoCorrectionEnabled) { + suggest.setAutoCorrectionThreshold( + currentSettingsValues.mAutoCorrectionThreshold); } switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), @@ -877,7 +857,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( - suggest.mDictionaryFacilitator.hasInitializedMainDictionary()); + mDictionaryFacilitator.hasInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( @@ -902,7 +882,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void onFinishInputInternal() { super.onFinishInput(); - LatinImeLogger.commit(); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); if (mainKeyboardView != null) { mainKeyboardView.closing(); @@ -911,16 +890,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void onFinishInputViewInternal(final boolean finishingInput) { super.onFinishInputView(finishingInput); - mKeyboardSwitcher.onFinishInputView(); mKeyboardSwitcher.deallocateMemory(); // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestionStrip(); // Should do the following in onFinishInputInternal but until JB MR2 it's not called :( mInputLogic.finishInput(); - // Notify ResearchLogger - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput); - } } @Override @@ -934,11 +908,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", nss=" + newSelStart + ", nse=" + newSelEnd + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onUpdateSelection(oldSelStart, oldSelEnd, - oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, - composingSpanEnd, mInputLogic.mConnection); - } // If the keyboard is not visible, we don't need to do all the housekeeping work, as it // will be reset when the keyboard shows up anyway. @@ -999,13 +968,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void hideWindow() { - LatinImeLogger.commit(); mKeyboardSwitcher.onHideWindow(); - if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { - AccessibleKeyboardViewProxy.getInstance().onHideWindow(); - } - if (TRACE) Debug.stopMethodTracing(); if (isShowingOptionDialog()) { mOptionsDialog.dismiss(); @@ -1029,9 +993,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (applicationSpecifiedCompletions == null) { setNeutralSuggestionStrip(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onDisplayCompletions(null); - } return; } @@ -1042,10 +1003,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, false /* isPrediction */); // When in fullscreen mode, show completions generated by the application forcibly - setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions); - } + setSuggestedWords(suggestedWords); } private int getAdjustedBackingViewHeight() { @@ -1179,7 +1137,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { wordToEdit = word; } - mInputLogic.mSuggest.mDictionaryFacilitator.addWordToUserDictionary(wordToEdit); + mDictionaryFacilitator.addWordToUserDictionary(this /* context */, wordToEdit); } // Callback for the {@link SuggestionStripView}, to call when the important notice strip is @@ -1348,30 +1306,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Nothing to do so far. } - private boolean isSuggestionStripVisible() { - if (!hasSuggestionStripView()) { - return false; - } - if (mSuggestionStripView.isShowingAddToDictionaryHint()) { - return true; - } - final SettingsValues currentSettings = mSettings.getCurrent(); - if (null == currentSettings) { - return false; - } - if (ImportantNoticeUtils.shouldShowImportantNotice(this, - currentSettings.mInputAttributes)) { - return true; - } - if (!currentSettings.isSuggestionStripVisible()) { - return false; - } - if (currentSettings.isApplicationSpecifiedCompletionsOn()) { - return true; - } - return currentSettings.isSuggestionsRequested(); - } - public boolean hasSuggestionStripView() { return null != mSuggestionStripView; } @@ -1389,34 +1323,49 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.dismissAddToDictionaryHint(); } - // TODO[IL]: Define a clear interface for this - public void setSuggestedWords(final SuggestedWords suggestedWords, - final boolean isSuggestionStripVisible) { + private void setSuggestedWords(final SuggestedWords suggestedWords) { mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { return; } - mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); if (!onEvaluateInputViewShown()) { return; } - if (!isSuggestionStripVisible) { - mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE); + + final SettingsValues currentSettingsValues = mSettings.getCurrent(); + final boolean shouldShowImportantNotice = + ImportantNoticeUtils.shouldShowImportantNotice(this); + final boolean shouldShowSuggestionCandidates = + currentSettingsValues.mInputAttributes.mShouldShowSuggestions + && currentSettingsValues.isCurrentOrientationAllowingSuggestionsPerUserSettings(); + final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice + || currentSettingsValues.mShowsVoiceInputKey + || shouldShowSuggestionCandidates + || currentSettingsValues.isApplicationSpecifiedCompletionsOn(); + final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword + && !currentSettingsValues.mInputAttributes.mIsPasswordField; + mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode()); + if (!shouldShowSuggestionsStrip) { return; } - mSuggestionStripView.setVisibility(View.VISIBLE); - final SettingsValues currentSettings = mSettings.getCurrent(); - final boolean showSuggestions; - if (SuggestedWords.EMPTY == suggestedWords || suggestedWords.isPunctuationSuggestions() - || !currentSettings.isSuggestionsRequested()) { - showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( - currentSettings.mInputAttributes); - } else { - showSuggestions = true; + final boolean isEmptyApplicationSpecifiedCompletions = + currentSettingsValues.isApplicationSpecifiedCompletionsOn() + && suggestedWords.isEmpty(); + final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords) + || suggestedWords.isPunctuationSuggestions() + || isEmptyApplicationSpecifiedCompletions; + if (shouldShowImportantNotice && noSuggestionsToShow) { + if (mSuggestionStripView.maybeShowImportantNoticeTitle()) { + return; + } } - if (showSuggestions) { + + if (currentSettingsValues.isCurrentOrientationAllowingSuggestionsPerUserSettings() + // We should clear suggestions if there is no suggestion to show. + || noSuggestionsToShow + || currentSettingsValues.isApplicationSpecifiedCompletionsOn()) { mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } @@ -1430,35 +1379,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen callback.onGetSuggestedWords(SuggestedWords.EMPTY); return; } - // Get the word on which we should search the bigrams. If we are composing a word, it's - // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we - // should just skip whitespace if any, so 1. final SettingsValues currentSettings = mSettings.getCurrent(); final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues; - - if (DEBUG) { - if (mInputLogic.mWordComposer.isComposingWord() - || mInputLogic.mWordComposer.isBatchMode()) { - final String previousWord - = mInputLogic.mWordComposer.getPreviousWordForSuggestion(); - // TODO: this is for checking consistency with older versions. Remove this when - // we are confident this is stable. - // We're checking the previous word in the text field against the memorized previous - // word. If we are composing a word we should have the second word before the cursor - // memorized, otherwise we should have the first. - final CharSequence rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( - currentSettings.mSpacingAndPunctuations, - mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); - if (!TextUtils.equals(previousWord, rereadPrevWord)) { - throw new RuntimeException("Unexpected previous word: " - + previousWord + " <> " + rereadPrevWord); - } - } - } mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer, - mInputLogic.mWordComposer.getPreviousWordForSuggestion(), + mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion( + currentSettings.mSpacingAndPunctuations, + // Get the word on which we should search the bigrams. If we are composing + // a word, it's whatever is *before* the half-committed word in the buffer, + // hence 2; if we aren't, we should just skip whitespace if any, so 1. + mInputLogic.mWordComposer.isComposingWord() ? 2 : 1), keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, - currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId, + currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId, sequenceNumber, callback); } @@ -1478,7 +1409,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setNeutralSuggestionStrip(); } else { mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); - setSuggestedWords(suggestedWords, isSuggestionStripVisible()); + setSuggestedWords(suggestedWords); } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. @@ -1511,7 +1442,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues currentSettings = mSettings.getCurrent(); final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; - setSuggestedWords(neutralSuggestions, isSuggestionStripVisible()); + setSuggestedWords(neutralSuggestions); } // TODO: Make this private @@ -1596,18 +1527,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void onReleaseKey(final int primaryCode, final boolean withSliding) { mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); - - // If accessibility is on, ensure the user receives keyboard state updates. - if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - switch (primaryCode) { - case Constants.CODE_SHIFT: - AccessibleKeyboardViewProxy.getInstance().notifyShiftState(); - break; - case Constants.CODE_SWITCH_ALPHA_SYMBOL: - AccessibleKeyboardViewProxy.getInstance().notifySymbolsState(); - break; - } - } } private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) { @@ -1652,7 +1571,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // boolean onKeyMultiple(final int keyCode, final int count, final KeyEvent event); // receive ringer mode change and network state change. - private BroadcastReceiver mConnectivityAndRingerModeChangeReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mConnectivityAndRingerModeChangeReceiver = + new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); @@ -1747,15 +1667,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting /* package for test */ void waitForLoadingDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { - mInputLogic.mSuggest.mDictionaryFacilitator.waitForLoadingDictionariesForTesting( - timeout, unit); + mDictionaryFacilitator.waitForLoadingDictionariesForTesting(timeout, unit); } // DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly. @UsedForTesting /* package for test */ void replaceDictionariesForTest(final Locale locale) { final SettingsValues settingsValues = mSettings.getCurrent(); - mInputLogic.mSuggest.mDictionaryFacilitator.resetDictionaries(this, locale, + mDictionaryFacilitator.resetDictionaries(this, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, false /* forceReloadMainDictionary */, this /* listener */); } @@ -1763,17 +1682,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // DO NOT USE THIS for any other purpose than testing. @UsedForTesting /* package for test */ void clearPersonalizedDictionariesForTest() { - mInputLogic.mSuggest.mDictionaryFacilitator.clearUserHistoryDictionary(); - mInputLogic.mSuggest.mDictionaryFacilitator.clearPersonalizationDictionary(); + mDictionaryFacilitator.clearUserHistoryDictionary(); + mDictionaryFacilitator.clearPersonalizationDictionary(); + } + + @UsedForTesting + /* package for test */ List<InputMethodSubtype> getEnabledSubtypesForTest() { + return (mRichImm != null) ? mRichImm.getMyEnabledInputMethodSubtypeList( + true /* allowsImplicitlySelectedSubtypes */) : new ArrayList<InputMethodSubtype>(); } public void dumpDictionaryForDebug(final String dictName) { - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - mInputLogic.mSuggest.mDictionaryFacilitator; - if (dictionaryFacilitator.getLocale() == null) { + if (mDictionaryFacilitator.getLocale() == null) { resetSuggest(); } - mInputLogic.mSuggest.mDictionaryFacilitator.dumpDictionaryForDebug(dictName); + mDictionaryFacilitator.dumpDictionaryForDebug(dictName); } public void debugDumpStateAndCrashWithException(final String context) { diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index 3f2b0a3f4..8fd36b937 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -16,76 +16,12 @@ package com.android.inputmethod.latin; -import android.content.SharedPreferences; -import android.view.inputmethod.EditorInfo; +import android.content.Context; -import com.android.inputmethod.keyboard.Keyboard; +// TODO: Rename this class name to make it more relevant. +public final class LatinImeLogger { + public static final boolean sDBG = false; -public final class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener { - - public static boolean sDBG = false; - public static boolean sVISUALDEBUG = false; - public static boolean sUsabilityStudy = false; - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - } - - public static void init(LatinIME context) { - } - - public static void commit() { - } - - public static boolean getUsabilityStudyMode(final SharedPreferences prefs) { - return false; - } - - public static void onDestroy() { - } - - public static void logOnManualSuggestion( - String before, String after, int position, SuggestedWords suggestedWords) { - } - - public static void logOnAutoCorrectionForTyping( - String before, String after, int separatorCode) { - } - - public static void logOnAutoCorrectionForGeometric(String before, String after, - int separatorCode, InputPointers inputPointers) { - } - - public static void logOnAutoCorrectionCancelled() { - } - - public static void logOnDelete(int x, int y) { - } - - public static void logOnInputChar() { - } - - public static void logOnInputSeparator() { - } - - public static void logOnException(String metaData, Throwable e) { - } - - public static void logOnWarning(String warning) { - } - - public static void onStartInputView(EditorInfo editorInfo) { - } - - public static void onStartSuggestion(CharSequence previousWords) { - } - - public static void onAddSuggestedWord(String word, String sourceDictionaryId) { - } - - public static void onSetKeyboard(Keyboard kb) { - } - - public static void onPrintAllUsabilityStudyLogs() { + public static void init(Context context) { } } diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java new file mode 100644 index 000000000..42b311c69 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java @@ -0,0 +1,62 @@ +/* + * 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; + +/** + * Class to represent information of previous words. This class is used to add n-gram entries + * into binary dictionaries, to get predictions, and to get suggestions. + */ +// TODO: Support multiple previous words for n-gram. +public class PrevWordsInfo { + public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null); + public static final PrevWordsInfo BEGINNING_OF_SENTENCE = new PrevWordsInfo(); + + // The word immediately before the considered word. null means we don't have any context + // including the "beginning of sentence context" - we just don't know what to predict. + // An example of that is after a comma. + // For simplicity of implementation, this may also be null transiently after the WordComposer + // was reset and before starting a new composing word, but we should never be calling + // getSuggetions* in this situation. + // This is an empty string when mIsBeginningOfSentence is true. + public final String mPrevWord; + + // TODO: Have sentence separator. + // Whether the current context is beginning of sentence or not. This is true when composing at + // the beginning of an input field or composing a word after a sentence separator. + public final boolean mIsBeginningOfSentence; + + // Beginning of sentence. + public PrevWordsInfo() { + mPrevWord = ""; + mIsBeginningOfSentence = true; + } + + public PrevWordsInfo(final String prevWord) { + mPrevWord = prevWord; + mIsBeginningOfSentence = false; + } + + public boolean isValid() { + return mPrevWord != null; + } + + @Override + public String toString() { + return "PrevWord: " + mPrevWord + ", isBeginningOfSentence: " + + mIsBeginningOfSentence + "."; + } +} diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index 4911bcdf6..0fba37c8a 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -17,8 +17,6 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.internal.KeySpecParser; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; @@ -49,7 +47,7 @@ public final class PunctuationSuggestions extends SuggestedWords { */ public static PunctuationSuggestions newPunctuationSuggestions( final String[] punctuationSpecs) { - final ArrayList<SuggestedWordInfo> puncuationsList = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> puncuationsList = new ArrayList<>(); for (final String puncSpec : punctuationSpecs) { puncuationsList.add(newHardCodedWordInfo(puncSpec)); } diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index 9f61d6c37..e59ef7563 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -50,22 +50,14 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { - return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, 0 /* sessionId */, inOutLanguageWeight); - } - - @Override - public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final float[] inOutLanguageWeight) { if (mLock.readLock().tryLock()) { try { - return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions, inOutLanguageWeight); + return mBinaryDictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, + inOutLanguageWeight); } finally { mLock.readLock().unlock(); } @@ -74,10 +66,10 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { if (mLock.readLock().tryLock()) { try { - return mBinaryDictionary.isValidWord(word); + return mBinaryDictionary.isInDictionary(word); } finally { mLock.readLock().unlock(); } @@ -110,6 +102,18 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } finally { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + @Override public void close() { mLock.writeLock().lock(); try { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 606bb775e..96476b2ee 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -26,14 +26,12 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; -import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.SpannableStringUtils; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; -import com.android.inputmethod.research.ResearchLogger; import java.util.Arrays; import java.util.regex.Pattern; @@ -174,9 +172,6 @@ public final class RichInputConnection { } if (null != mIC && shouldFinishComposition) { mIC.finishComposingText(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_finishComposingText(); - } } return true; } @@ -223,9 +218,6 @@ public final class RichInputConnection { mComposingText.setLength(0); if (null != mIC) { mIC.finishComposingText(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_finishComposingText(); - } } } @@ -363,9 +355,6 @@ public final class RichInputConnection { } if (null != mIC) { mIC.deleteSurroundingText(beforeLength, afterLength); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_deleteSurroundingText(beforeLength, afterLength); - } } if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } @@ -374,9 +363,6 @@ public final class RichInputConnection { mIC = mParent.getCurrentInputConnection(); if (null != mIC) { mIC.performEditorAction(actionId); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_performEditorAction(actionId); - } } } @@ -429,9 +415,6 @@ public final class RichInputConnection { } if (null != mIC) { mIC.sendKeyEvent(keyEvent); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_sendKeyEvent(keyEvent); - } } } @@ -469,9 +452,6 @@ public final class RichInputConnection { // newCursorPosition != 1. if (null != mIC) { mIC.setComposingText(text, newCursorPosition); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_setComposingText(text, newCursorPosition); - } } if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } @@ -500,9 +480,6 @@ public final class RichInputConnection { if (!isIcValid) { return false; } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_setSelection(start, end); - } } return reloadTextCache(); } @@ -530,18 +507,17 @@ public final class RichInputConnection { mComposingText.setLength(0); if (null != mIC) { mIC.commitCompletion(completionInfo); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_commitCompletion(completionInfo); - } } if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } @SuppressWarnings("unused") - public String getNthPreviousWord(final SpacingAndPunctuations spacingAndPunctuations, - final int n) { + public PrevWordsInfo getPrevWordsInfoFromNthPreviousWord( + final SpacingAndPunctuations spacingAndPunctuations, final int n) { mIC = mParent.getCurrentInputConnection(); - if (null == mIC) return null; + if (null == mIC) { + return PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + } final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); if (DEBUG_PREVIOUS_TEXT && null != prev) { final int checkLength = LOOKBACK_CHARACTER_NUM - 1; @@ -561,46 +537,73 @@ public final class RichInputConnection { } } } - return getNthPreviousWord(prev, spacingAndPunctuations, n); + return getPrevWordsInfoFromNthPreviousWord(prev, spacingAndPunctuations, n); } private static boolean isSeparator(final int code, final int[] sortedSeparators) { return Arrays.binarySearch(sortedSeparators, code) >= 0; } - // Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor, - // n = 2 retrieves the word before that, and so on. This splits on whitespace only. + // Get information of the nth word before cursor. n = 1 retrieves the word immediately before + // the cursor, n = 2 retrieves the word before that, and so on. This splits on whitespace only. // Also, it won't return words that end in a separator (if the nth word before the cursor - // ends in a separator, it returns null). + // ends in a separator, it returns information representing beginning-of-sentence). // Example : // (n = 1) "abc def|" -> def // (n = 1) "abc def |" -> def - // (n = 1) "abc def. |" -> null - // (n = 1) "abc def . |" -> null + // (n = 1) "abc 'def|" -> 'def + // (n = 1) "abc def. |" -> beginning-of-sentence + // (n = 1) "abc def . |" -> beginning-of-sentence // (n = 2) "abc def|" -> abc // (n = 2) "abc def |" -> abc + // (n = 2) "abc 'def|" -> empty. The context is different from "abc def", but we cannot + // represent this situation using PrevWordsInfo. See TODO in the method. // (n = 2) "abc def. |" -> abc // (n = 2) "abc def . |" -> def - // (n = 2) "abc|" -> null - // (n = 2) "abc |" -> null - // (n = 2) "abc. def|" -> null - public static String getNthPreviousWord(final CharSequence prev, + // (n = 2) "abc|" -> beginning-of-sentence + // (n = 2) "abc |" -> beginning-of-sentence + // (n = 2) "abc. def|" -> beginning-of-sentence + public static PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(final CharSequence prev, final SpacingAndPunctuations spacingAndPunctuations, final int n) { - if (prev == null) return null; + if (prev == null) return PrevWordsInfo.EMPTY_PREV_WORDS_INFO; final String[] w = spaceRegex.split(prev); - // If we can't find n words, or we found an empty word, return null. - if (w.length < n) return null; + // Referring to the word after the nth word. + if ((n - 1) > 0 && (n - 1) <= w.length) { + final String wordFollowingTheNthPrevWord = w[w.length - n + 1]; + if (!wordFollowingTheNthPrevWord.isEmpty()) { + final char firstChar = wordFollowingTheNthPrevWord.charAt(0); + if (spacingAndPunctuations.isWordConnector(firstChar)) { + // The word following the n-th prev word is starting with a word connector. + // TODO: Return meaningful context for this case. + return PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + } + } + } + + // If we can't find n words, or we found an empty word, the context is + // beginning-of-sentence. + if (w.length < n) { + return PrevWordsInfo.BEGINNING_OF_SENTENCE; + } final String nthPrevWord = w[w.length - n]; final int length = nthPrevWord.length(); - if (length <= 0) return null; + if (length <= 0) { + return PrevWordsInfo.BEGINNING_OF_SENTENCE; + } - // If ends in a separator, return null + // If ends in a sentence separator, the context is beginning-of-sentence. final char lastChar = nthPrevWord.charAt(length - 1); + if (spacingAndPunctuations.isSentenceSeparator(lastChar)) { + return PrevWordsInfo.BEGINNING_OF_SENTENCE; + } + // If ends in a word separator or connector, the context is unclear. + // TODO: Return meaningful context for this case. if (spacingAndPunctuations.isWordSeparator(lastChar) - || spacingAndPunctuations.isWordConnector(lastChar)) return null; - - return nthPrevWord; + || spacingAndPunctuations.isWordConnector(lastChar)) { + return PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + } + return new PrevWordsInfo(nthPrevWord); } /** @@ -752,9 +755,6 @@ public final class RichInputConnection { deleteSurroundingText(2, 0); final String singleSpace = " "; commitText(singleSpace, 1); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_revertDoubleSpacePeriod(); - } return true; } @@ -777,9 +777,6 @@ public final class RichInputConnection { deleteSurroundingText(2, 0); final String text = " " + textBeforeCursor.subSequence(0, 1); commitText(text, 1); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.richInputConnection_revertSwapPunctuation(); - } return true; } @@ -894,4 +891,8 @@ public final class RichInputConnection { public boolean hasSelection() { return mExpectedSelEnd != mExpectedSelStart; } + + public boolean isCursorPositionKnown() { + return INVALID_CURSOR_POSITION != mExpectedSelStart; + } } diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 2b0be545e..7758ac78e 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -31,7 +31,6 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Collections; @@ -53,9 +52,9 @@ public final class RichInputMethodManager { private InputMethodManagerCompatWrapper mImmWrapper; private InputMethodInfoCache mInputMethodInfoCache; final HashMap<InputMethodInfo, List<InputMethodSubtype>> - mSubtypeListCacheWithImplicitlySelectedSubtypes = CollectionUtils.newHashMap(); + mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> - mSubtypeListCacheWithoutImplicitlySelectedSubtypes = CollectionUtils.newHashMap(); + mSubtypeListCacheWithoutImplicitlySelectedSubtypes = new HashMap<>(); private static final int INDEX_NOT_FOUND = -1; diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 021133945..a3d09565c 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -52,7 +52,6 @@ public final class SubtypeSwitcher { private /* final */ RichInputMethodManager mRichImm; private /* final */ Resources mResources; - private /* final */ ConnectivityManager mConnectivityManager; private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper = new LanguageOnSpacebarHelper(); @@ -111,10 +110,10 @@ public final class SubtypeSwitcher { } mResources = context.getResources(); mRichImm = RichInputMethodManager.getInstance(); - mConnectivityManager = (ConnectivityManager) context.getSystemService( + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); - final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); + final NetworkInfo info = connectivityManager.getActiveNetworkInfo(); mIsNetworkConnected = (info != null && info.isConnected()); onSubtypeChanged(getCurrentSubtype()); @@ -256,8 +255,7 @@ public final class SubtypeSwitcher { public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() { final Locale systemLocale = mResources.getConfiguration().locale; - final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = - new HashSet<InputMethodSubtype>(); + final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>(); final InputMethodManager inputMethodManager = mRichImm.getInputMethodManager(); final List<InputMethodInfo> enabledInputMethodInfoList = inputMethodManager.getEnabledInputMethodList(); @@ -327,4 +325,8 @@ public final class SubtypeSwitcher { + DUMMY_EMOJI_SUBTYPE); return DUMMY_EMOJI_SUBTYPE; } + + public String getCombiningRulesExtraValueOfCurrentSubtype() { + return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype()); + } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index db0a8a81c..1ba5d5ea6 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -18,13 +18,11 @@ package com.android.inputmethod.latin; import android.text.TextUtils; -import com.android.inputmethod.event.Event; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; @@ -53,11 +51,14 @@ public final class Suggest { private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; private static final boolean DBG = LatinImeLogger.sDBG; - public final DictionaryFacilitatorForSuggest mDictionaryFacilitator = - new DictionaryFacilitatorForSuggest(); + private final DictionaryFacilitator mDictionaryFacilitator; private float mAutoCorrectionThreshold; + public Suggest(final DictionaryFacilitator dictionaryFacilitator) { + mDictionaryFacilitator = dictionaryFacilitator; + } + public Locale getLocale() { return mDictionaryFacilitator.getLocale(); } @@ -71,17 +72,16 @@ public final class Suggest { } public void getSuggestedWords(final WordComposer wordComposer, - final String prevWordForBigram, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final boolean isCorrectionEnabled, final int[] additionalFeaturesOptions, final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { - LatinImeLogger.onStartSuggestion(prevWordForBigram); if (wordComposer.isBatchMode()) { - getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo, + getSuggestedWordsForBatchInput(wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, sessionId, sequenceNumber, callback); } else { - getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, + getSuggestedWordsForTypingInput(wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, sequenceNumber, callback); } @@ -90,29 +90,31 @@ public final class Suggest { // Retrieves suggestions for the typing input // and calls the callback function with the suggestions. private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, - final String prevWordForBigram, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final boolean isCorrectionEnabled, final int[] additionalFeaturesOptions, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { - final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); final String typedWord = wordComposer.getTypedWord(); + final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWord); final String consideredWord = trailingSingleQuotesCount > 0 ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) : typedWord; - LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED); final ArrayList<SuggestedWordInfo> rawSuggestions; if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { - rawSuggestions = CollectionUtils.newArrayList(); + rawSuggestions = new ArrayList<>(); } else { rawSuggestions = null; } final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, + wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, SESSION_TYPING, rawSuggestions); - final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); - final boolean isAllUpperCase = wordComposer.isAllUpperCase(); + final boolean isOnlyFirstCharCapitalized = wordComposer.isOnlyFirstCharCapitalized(); + // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized + // words is rarely done to switch to another fully-capitalized word, but usually to a + // normal, non-capitalized suggestion. + final boolean isAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed(); final String firstSuggestion; final String whitelistedWord; if (suggestionResults.isEmpty()) { @@ -120,9 +122,9 @@ public final class Suggest { } else { final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo( suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase, - isFirstCharCapitalized, trailingSingleQuotesCount); + isOnlyFirstCharCapitalized, trailingSingleQuotesCount); firstSuggestion = firstSuggestedWordInfo.mWord; - if (SuggestedWordInfo.KIND_WHITELIST != firstSuggestedWordInfo.mKind) { + if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { whitelistedWord = null; } else { whitelistedWord = firstSuggestion; @@ -140,7 +142,7 @@ public final class Suggest { final boolean allowsToBeAutoCorrected = (null != whitelistedWord && !whitelistedWord.equals(typedWord)) || (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord( - consideredWord, wordComposer.isFirstCharCapitalized()) + consideredWord, isOnlyFirstCharCapitalized) && !typedWord.equals(firstSuggestion)); final boolean hasAutoCorrection; @@ -153,7 +155,7 @@ public final class Suggest { || suggestionResults.isEmpty() || wordComposer.hasDigits() || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !mDictionaryFacilitator.hasInitializedMainDictionary() - || SuggestedWordInfo.KIND_SHORTCUT == suggestionResults.first().mKind) { + || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { // If we don't have a main dictionary, we never want to auto-correct. The reason for // this is, the user may have a contact whose name happens to match a valid word in // their language, and it will unexpectedly auto-correct. For example, if the user @@ -169,24 +171,18 @@ public final class Suggest { } final ArrayList<SuggestedWordInfo> suggestionsContainer = - CollectionUtils.newArrayList(suggestionResults); + new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); - if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { + if (isOnlyFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { for (int i = 0; i < suggestionsCount; ++i) { final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( - wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized, - trailingSingleQuotesCount); + wordInfo, suggestionResults.mLocale, isAllUpperCase, + isOnlyFirstCharCapitalized, trailingSingleQuotesCount); suggestionsContainer.set(i, transformedWordInfo); } } - for (int i = 0; i < suggestionsCount; ++i) { - final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); - LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), - wordInfo.mSourceDict.mDictType); - } - if (!TextUtils.isEmpty(typedWord)) { suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, @@ -215,25 +211,21 @@ public final class Suggest { // Retrieves suggestions for the batch input // and calls the callback function with the suggestions. private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, - final String prevWordForBigram, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final ArrayList<SuggestedWordInfo> rawSuggestions; if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { - rawSuggestions = CollectionUtils.newArrayList(); + rawSuggestions = new ArrayList<>(); } else { rawSuggestions = null; } final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, + wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, sessionId, rawSuggestions); - for (SuggestedWordInfo wordInfo : suggestionResults) { - LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType); - } - final ArrayList<SuggestedWordInfo> suggestionsContainer = - CollectionUtils.newArrayList(suggestionResults); + new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); final boolean isAllUpperCase = wordComposer.isAllUpperCase(); @@ -276,8 +268,7 @@ public final class Suggest { final SuggestedWordInfo typedWordInfo = suggestions.get(0); typedWordInfo.setDebugString("+"); final int suggestionsSize = suggestions.size(); - final ArrayList<SuggestedWordInfo> suggestionsList = - CollectionUtils.newArrayList(suggestionsSize); + final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(suggestionsSize); suggestionsList.add(typedWordInfo); // Note: i here is the index in mScores[], but the index in mSuggestions is one more // than i because we added the typed word to mSuggestions without touching mScores. @@ -301,11 +292,11 @@ public final class Suggest { /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo( final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, - final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { + final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) { final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); if (isAllUpperCase) { sb.append(wordInfo.mWord.toUpperCase(locale)); - } else if (isFirstCharCapitalized) { + } else if (isOnlyFirstCharCapitalized) { sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale)); } else { sb.append(wordInfo.mWord); @@ -318,7 +309,7 @@ public final class Suggest { for (int i = quotesToAppend - 1; i >= 0; --i) { sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); } - return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind, + return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKindAndFlags, wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord, wordInfo.mAutoCommitFirstWordConfidence); } diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index dc2c9fd0e..72461e17a 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import android.text.TextUtils; import android.view.inputmethod.CompletionInfo; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; @@ -34,8 +33,7 @@ public class SuggestedWords { // The maximum number of suggestions available. public static final int MAX_SUGGESTIONS = 18; - private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = - CollectionUtils.newArrayList(0); + private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); public static final SuggestedWords EMPTY = new SuggestedWords( EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false); @@ -165,7 +163,7 @@ public class SuggestedWords { public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions( final CompletionInfo[] infos) { - final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> result = new ArrayList<>(); for (final CompletionInfo info : infos) { if (null == info || null == info.getText()) { continue; @@ -179,8 +177,8 @@ public class SuggestedWords { // and replace it with what the user currently typed. public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions( final String typedWord, final SuggestedWords previousSuggestions) { - final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList(); - final HashSet<String> alreadySeen = CollectionUtils.newHashSet(); + final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(); + final HashSet<String> alreadySeen = new HashSet<>(); suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, @@ -209,7 +207,8 @@ public class SuggestedWords { public static final int NOT_AN_INDEX = -1; public static final int NOT_A_CONFIDENCE = -1; public static final int MAX_SCORE = Integer.MAX_VALUE; - public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind + + private static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind public static final int KIND_TYPED = 0; // What user typed public static final int KIND_CORRECTION = 1; // Simple correction/suggestion public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars) @@ -224,16 +223,16 @@ public class SuggestedWords { public static final int KIND_RESUMED = 9; public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction - public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; public static final int KIND_FLAG_EXACT_MATCH = 0x40000000; + public static final int KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = 0x20000000; public final String mWord; // The completion info from the application. Null for suggestions that don't come from // the application (including keyboard-computed ones, so this is almost always null) public final CompletionInfo mApplicationSpecifiedCompletionInfo; public final int mScore; - public final int mKind; // one of the KIND_* constants above + public final int mKindAndFlags; public final int mCodePointCount; public final Dictionary mSourceDict; // For auto-commit. This keeps track of the index inside the touch coordinates array @@ -249,18 +248,19 @@ public class SuggestedWords { * Create a new suggested word info. * @param word The string to suggest. * @param score A measure of how likely this suggestion is. - * @param kind The kind of suggestion, as one of the above KIND_* constants. + * @param kindAndFlags The kind of suggestion, as one of the above KIND_* constants with + * flags. * @param sourceDict What instance of Dictionary produced this suggestion. * @param indexOfTouchPointOfSecondWord See mIndexOfTouchPointOfSecondWord. * @param autoCommitFirstWordConfidence See mAutoCommitFirstWordConfidence. */ - public SuggestedWordInfo(final String word, final int score, final int kind, + public SuggestedWordInfo(final String word, final int score, final int kindAndFlags, final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord, final int autoCommitFirstWordConfidence) { mWord = word; mApplicationSpecifiedCompletionInfo = null; mScore = score; - mKind = kind; + mKindAndFlags = kindAndFlags; mSourceDict = sourceDict; mCodePointCount = StringUtils.codePointCount(mWord); mIndexOfTouchPointOfSecondWord = indexOfTouchPointOfSecondWord; @@ -276,7 +276,7 @@ public class SuggestedWords { mWord = applicationSpecifiedCompletion.getText().toString(); mApplicationSpecifiedCompletionInfo = applicationSpecifiedCompletion; mScore = SuggestedWordInfo.MAX_SCORE; - mKind = SuggestedWordInfo.KIND_APP_DEFINED; + mKindAndFlags = SuggestedWordInfo.KIND_APP_DEFINED; mSourceDict = Dictionary.DICTIONARY_APPLICATION_DEFINED; mCodePointCount = StringUtils.codePointCount(mWord); mIndexOfTouchPointOfSecondWord = SuggestedWordInfo.NOT_AN_INDEX; @@ -284,7 +284,27 @@ public class SuggestedWords { } public boolean isEligibleForAutoCommit() { - return (KIND_CORRECTION == mKind && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord); + return (isKindOf(KIND_CORRECTION) && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord); + } + + public int getKind() { + return (mKindAndFlags & KIND_MASK_KIND); + } + + public boolean isKindOf(final int kind) { + return getKind() == kind; + } + + public boolean isPossiblyOffensive() { + return (mKindAndFlags & KIND_FLAG_POSSIBLY_OFFENSIVE) != 0; + } + + public boolean isExactMatch() { + return (mKindAndFlags & KIND_FLAG_EXACT_MATCH) != 0; + } + + public boolean isExactMatchWithIntentionalOmission() { + return (mKindAndFlags & KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) != 0; } public void setDebugString(final String str) { @@ -337,11 +357,11 @@ public class SuggestedWords { // SuggestedWords is an immutable object, as much as possible. We must not just remove // words from the member ArrayList as some other parties may expect the object to never change. public SuggestedWords getSuggestedWordsExcludingTypedWord() { - final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); String typedWord = null; for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); - if (SuggestedWordInfo.KIND_TYPED != info.mKind) { + if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) { newSuggestions.add(info); } else { assert(null == typedWord); @@ -361,12 +381,12 @@ public class SuggestedWords { // we should only suggest replacements for this last word. // TODO: make this work with languages without spaces. public SuggestedWords getSuggestedWordsForLastWordOfPhraseGesture() { - final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); final int indexOfLastSpace = info.mWord.lastIndexOf(Constants.CODE_SPACE) + 1; final String lastWord = info.mWord.substring(indexOfLastSpace); - newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKind, + newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKindAndFlags, info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX, SuggestedWordInfo.NOT_A_CONFIDENCE)); } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 9d9ce0138..debaad13e 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -28,8 +28,8 @@ import android.provider.UserDictionary.Words; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.UserDictionaryCompatUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.io.File; @@ -51,43 +51,21 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // to auto-correct, so we set this to the highest frequency that won't, i.e. 14. private static final int USER_DICT_SHORTCUT_FREQUENCY = 14; - // TODO: use Words.SHORTCUT when we target JellyBean or above - final static String SHORTCUT = "shortcut"; - private static final String[] PROJECTION_QUERY; - static { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - PROJECTION_QUERY = new String[] { - Words.WORD, - SHORTCUT, - Words.FREQUENCY, - }; - } else { - PROJECTION_QUERY = new String[] { - Words.WORD, - Words.FREQUENCY, - }; - } - } + private static final String[] PROJECTION_QUERY_WITH_SHORTCUT = new String[] { + Words.WORD, + Words.SHORTCUT, + Words.FREQUENCY, + }; + private static final String[] PROJECTION_QUERY_WITHOUT_SHORTCUT = new String[] { + Words.WORD, + Words.FREQUENCY, + }; private static final String NAME = "userunigram"; private ContentObserver mObserver; final private String mLocale; final private boolean mAlsoUseMoreRestrictiveLocales; - final public boolean mEnabled; - - public UserBinaryDictionary(final Context context, final Locale locale) { - this(context, locale, false /* alsoUseMoreRestrictiveLocales */, null /* dictFile */); - } - - public UserBinaryDictionary(final Context context, final Locale locale, final File dictFile) { - this(context, locale, false /* alsoUseMoreRestrictiveLocales */, dictFile); - } - - public UserBinaryDictionary(final Context context, final Locale locale, - final boolean alsoUseMoreRestrictiveLocales, final File dictFile) { - this(context, locale, alsoUseMoreRestrictiveLocales, dictFile, NAME); - } protected UserBinaryDictionary(final Context context, final Locale locale, final boolean alsoUseMoreRestrictiveLocales, final File dictFile, final String name) { @@ -116,14 +94,20 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // devices. On older versions of the platform, the hook above will be called instead. @Override public void onChange(final boolean self, final Uri uri) { - setNeedsToReload(); + setNeedsToRecreate(); } }; cres.registerContentObserver(Words.CONTENT_URI, true, mObserver); - mEnabled = readIsEnabled(); reloadDictionaryIfRequired(); } + @UsedForTesting + public static UserBinaryDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix) { + return new UserBinaryDictionary(context, locale, false /* alsoUseMoreRestrictiveLocales */, + dictFile, dictNamePrefix + NAME); + } + @Override public synchronized void close() { if (mObserver != null) { @@ -182,10 +166,29 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } else { requestArguments = localeElements; } + final String requestString = request.toString(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + try { + addWordsFromProjectionLocked(PROJECTION_QUERY_WITH_SHORTCUT, requestString, + requestArguments); + } catch (IllegalArgumentException e) { + // This may happen on some non-compliant devices where the declared API is JB+ but + // the SHORTCUT column is not present for some reason. + addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString, + requestArguments); + } + } else { + addWordsFromProjectionLocked(PROJECTION_QUERY_WITHOUT_SHORTCUT, requestString, + requestArguments); + } + } + + private void addWordsFromProjectionLocked(final String[] query, String request, + final String[] requestArguments) throws IllegalArgumentException { Cursor cursor = null; try { cursor = mContext.getContentResolver().query( - Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); + Words.CONTENT_URI, query, request, requestArguments, null); addWordsLocked(cursor); } catch (final SQLiteException e) { Log.e(TAG, "SQLiteException in the remote User dictionary process.", e); @@ -198,8 +201,8 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } } - private boolean readIsEnabled() { - final ContentResolver cr = mContext.getContentResolver(); + public static boolean isEnabled(final Context context) { + final ContentResolver cr = context.getContentResolver(); final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI); if (client != null) { client.release(); @@ -212,18 +215,15 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { /** * Adds a word to the user dictionary and makes it persistent. * + * @param context the context + * @param locale the locale * @param word the word to add. If the word is capitalized, then the dictionary will * recognize it as a capitalized word when searched. */ - public synchronized void addWordToUserDictionary(final String word) { + public static void addWordToUserDictionary(final Context context, final Locale locale, + final String word) { // Update the user dictionary provider - final Locale locale; - if (USER_DICTIONARY_ALL_LANGUAGES == mLocale) { - locale = null; - } else { - locale = LocaleUtils.constructLocaleFromString(mLocale); - } - UserDictionaryCompatUtils.addWord(mContext, word, + UserDictionaryCompatUtils.addWord(context, word, HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY, null, locale); } @@ -245,7 +245,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { if (cursor == null) return; if (cursor.moveToFirst()) { final int indexWord = cursor.getColumnIndex(Words.WORD); - final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(SHORTCUT) : 0; + final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(Words.SHORTCUT) : 0; final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY); while (!cursor.isAfterLast()) { final String word = cursor.getString(indexWord); @@ -255,12 +255,12 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { // Safeguard against adding really long words. if (word.length() < MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); - addWordDynamicallyLocked(word, adjustedFrequency, null /* shortcutTarget */, + addUnigramLocked(word, adjustedFrequency, null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); - addWordDynamicallyLocked(shortcut, adjustedFrequency, word, + addUnigramLocked(shortcut, adjustedFrequency, word, USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); } @@ -269,9 +269,4 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } } } - - @Override - protected boolean haveContentsChanged() { - return true; - } } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index d755195f2..6ce1f85c5 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin; import com.android.inputmethod.event.CombinerChain; import com.android.inputmethod.event.Event; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.StringUtils; @@ -41,15 +40,11 @@ public final class WordComposer { public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7; private CombinerChain mCombinerChain; + private String mCombiningSpec; // Memory so that we don't uselessly recreate the combiner chain // The list of events that served to compose this string. private final ArrayList<Event> mEvents; private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH); - // The previous word (before the composing word). Used as context for suggestions. May be null - // after resetting and before starting a new composing word, or when there is no context like - // at the start of text for example. It can also be set to null externally when the user - // enters a separator that does not let bigrams across, like a period or a comma. - private String mPreviousWordForSuggestion; private String mAutoCorrection; private boolean mIsResumed; private boolean mIsBatchMode; @@ -74,23 +69,36 @@ public final class WordComposer { private int mCursorPositionWithinWord; /** - * Whether the user chose to capitalize the first char of the word. + * Whether the composing word has the only first char capitalized. */ - private boolean mIsFirstCharCapitalized; + private boolean mIsOnlyFirstCharCapitalized; public WordComposer() { - mCombinerChain = new CombinerChain(); - mEvents = CollectionUtils.newArrayList(); + mCombinerChain = new CombinerChain(""); + mEvents = new ArrayList<>(); mAutoCorrection = null; mIsResumed = false; mIsBatchMode = false; mCursorPositionWithinWord = 0; mRejectedBatchModeSuggestion = null; - mPreviousWordForSuggestion = null; refreshTypedWordCache(); } /** + * Restart the combiners, possibly with a new spec. + * @param combiningSpec The spec string for combining. This is found in the extra value. + */ + public void restartCombining(final String combiningSpec) { + final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec; + if (!nonNullCombiningSpec.equals(mCombiningSpec)) { + mCombinerChain = new CombinerChain( + mCombinerChain.getComposingWordWithCombiningFeedback().toString(), + CombinerChain.createCombiners(nonNullCombiningSpec)); + mCombiningSpec = nonNullCombiningSpec; + } + } + + /** * Clear out the keys registered so far. */ public void reset() { @@ -99,12 +107,11 @@ public final class WordComposer { mAutoCorrection = null; mCapsCount = 0; mDigitsCount = 0; - mIsFirstCharCapitalized = false; + mIsOnlyFirstCharCapitalized = false; mIsResumed = false; mIsBatchMode = false; mCursorPositionWithinWord = 0; mRejectedBatchModeSuggestion = null; - mPreviousWordForSuggestion = null; refreshTypedWordCache(); } @@ -133,8 +140,12 @@ public final class WordComposer { */ 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 = mTypedWordCache.length() - trailingSingleQuotesCount(); + final int lastIndex = typedWord.length() + - StringUtils.getTrailingSingleQuotesCount(typedWord); if (lastIndex <= 0) { // The string is empty or contains only single quotes. return 0; @@ -142,11 +153,11 @@ public final class WordComposer { // 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(mTypedWordCache, 0, lastIndex); + final int codePointSize = Character.codePointCount(typedWord, 0, lastIndex); if (codePointSize > destination.length) { return -1; } - return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0, + return StringUtils.copyCodePointsAndReturnCodePointCount(destination, typedWord, 0, lastIndex, true /* downCase */); } @@ -162,12 +173,6 @@ public final class WordComposer { return mInputPointers; } - private static boolean isFirstCharCapitalized(final int index, final int codePoint, - final boolean previous) { - if (index == 0) return Character.isUpperCase(codePoint); - return previous && !Character.isUpperCase(codePoint); - } - /** * Process an input event. * @@ -187,7 +192,7 @@ public final class WordComposer { mCursorPositionWithinWord = mCodePointSize; // We may have deleted the last one. if (0 == mCodePointSize) { - mIsFirstCharCapitalized = false; + mIsOnlyFirstCharCapitalized = false; } if (Constants.CODE_DELETE != event.mKeyCode) { if (newIndex < MAX_WORD_LENGTH) { @@ -199,8 +204,12 @@ public final class WordComposer { mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0); } } - mIsFirstCharCapitalized = isFirstCharCapitalized( - newIndex, primaryCode, mIsFirstCharCapitalized); + if (0 == newIndex) { + mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode); + } else { + mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized + && !Character.isUpperCase(primaryCode); + } if (Character.isUpperCase(primaryCode)) mCapsCount++; if (Character.isDigit(primaryCode)) mDigitsCount++; } @@ -280,11 +289,8 @@ public final class WordComposer { * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. * @param codePoints the code points to set as the composing word. * @param coordinates the x, y coordinates of the key in the CoordinateUtils format - * @param previousWord the previous word, to use as context for suggestions. Can be null if - * the context is nil (typically, at start of text). */ - public void setComposingWord(final int[] codePoints, final int[] coordinates, - final CharSequence previousWord) { + public void setComposingWord(final int[] codePoints, final int[] coordinates) { reset(); final int length = codePoints.length; for (int i = 0; i < length; ++i) { @@ -293,7 +299,6 @@ public final class WordComposer { CoordinateUtils.yFromArray(coordinates, i))); } mIsResumed = true; - mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -304,25 +309,13 @@ public final class WordComposer { return mTypedWordCache.toString(); } - public String getPreviousWordForSuggestion() { - return mPreviousWordForSuggestion; - } - /** - * Whether or not the user typed a capital letter as the first letter in the word + * Whether or not the user typed a capital letter as the first letter in the word, and no + * other letter is capitalized * @return capitalization preference */ - public boolean isFirstCharCapitalized() { - return mIsFirstCharCapitalized; - } - - public int trailingSingleQuotesCount() { - final int lastIndex = mTypedWordCache.length() - 1; - int i = lastIndex; - while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) { - --i; - } - return lastIndex - i; + public boolean isOnlyFirstCharCapitalized() { + return mIsOnlyFirstCharCapitalized; } /** @@ -358,7 +351,7 @@ public final class WordComposer { } /** - * Saves the caps mode and the previous word at the start of composing. + * Saves the caps mode at the start of composing. * * WordComposer needs to know about the caps mode for several reasons. The first is, we need * to know after the fact what the reason was, to register the correct form into the user @@ -367,12 +360,9 @@ public final class WordComposer { * Also, batch input needs to know about the current caps mode to display correctly * capitalized suggestions. * @param mode the mode at the time of start - * @param previousWord the previous word as context for suggestions. May be null if none. */ - public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, - final CharSequence previousWord) { + public void setCapitalizedModeAtStartComposingTime(final int mode) { mCapitalizedMode = mode; - mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -408,13 +398,13 @@ public final class WordComposer { // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. // committedWord should contain suggestion spans if applicable. public LastComposedWord commitWord(final int type, final CharSequence committedWord, - final String separatorString, final String prevWord) { + final String separatorString, final PrevWordsInfo prevWordsInfo) { // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate // the last composed word to ensure this does not happen. final LastComposedWord lastComposedWord = new LastComposedWord(mEvents, mInputPointers, mTypedWordCache.toString(), committedWord, separatorString, - prevWord, mCapitalizedMode); + prevWordsInfo, mCapitalizedMode); mInputPointers.reset(); if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) { @@ -423,11 +413,10 @@ public final class WordComposer { mCapsCount = 0; mDigitsCount = 0; mIsBatchMode = false; - mPreviousWordForSuggestion = committedWord.toString(); mCombinerChain.reset(); mEvents.clear(); mCodePointSize = 0; - mIsFirstCharCapitalized = false; + mIsOnlyFirstCharCapitalized = false; mCapitalizedMode = CAPS_MODE_OFF; refreshTypedWordCache(); mAutoCorrection = null; @@ -437,15 +426,7 @@ public final class WordComposer { return lastComposedWord; } - // Call this when the recorded previous word should be discarded. This is typically called - // when the user inputs a separator that's not whitespace (including the case of the - // double-space-to-period feature). - public void discardPreviousWordForSuggestion() { - mPreviousWordForSuggestion = null; - } - - public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord, - final String previousWord) { + public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) { mEvents.clear(); Collections.copy(mEvents, lastComposedWord.mEvents); mInputPointers.set(lastComposedWord.mInputPointers); @@ -456,7 +437,6 @@ public final class WordComposer { mCursorPositionWithinWord = mCodePointSize; mRejectedBatchModeSuggestion = null; mIsResumed = true; - mPreviousWordForSuggestion = previousWord; } public boolean isBatchMode() { diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java index 5ac806a0c..268fe9818 100644 --- a/java/src/com/android/inputmethod/latin/WordListInfo.java +++ b/java/src/com/android/inputmethod/latin/WordListInfo.java @@ -22,8 +22,10 @@ package com.android.inputmethod.latin; public final class WordListInfo { public final String mId; public final String mLocale; - public WordListInfo(final String id, final String locale) { + public final String mRawChecksum; + public WordListInfo(final String id, final String locale, final String rawChecksum) { mId = id; mLocale = locale; + mRawChecksum = rawChecksum; } } diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java index 139e73aa4..7071d8689 100644 --- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java @@ -27,7 +27,6 @@ import com.android.inputmethod.latin.BinaryDictionaryFileDumper; import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.makedict.DictionaryHeader; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.LocaleUtils; @@ -50,7 +49,7 @@ public class ExternalDictionaryGetterForDebug { private static String[] findDictionariesInTheDownloadedFolder() { final File[] files = new File(SOURCE_FOLDER).listFiles(); - final ArrayList<String> eligibleList = CollectionUtils.newArrayList(); + final ArrayList<String> eligibleList = new ArrayList<>(); for (File f : files) { final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f); if (null == header) continue; diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java index af899c040..972580298 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java @@ -21,13 +21,6 @@ public final class ProductionFlag { // This class is not publicly instantiable. } - public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS = false; - - // When false, USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG suggests that all guarded - // class-private DEBUG flags should be false, and any privacy controls should be enforced. - // USES_DEVELOPMENT_ONLY_DIAGNOSTICS must be false for any production build. - public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG = false; - public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; // When true, enable {@link InputMethodService#onUpdateCursor} callback with @@ -38,4 +31,7 @@ public final class ProductionFlag { // Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. public static final boolean INCLUDE_RAW_SUGGESTIONS = false; + + // When false, the metrics logging is not yet ready to be enabled. + public static final boolean IS_METRICS_LOGGING_SUPPORTED = false; } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index d2100d415..24cc1ef0d 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -32,29 +32,26 @@ import com.android.inputmethod.event.InputTransaction; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; +import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.InputPointers; import com.android.inputmethod.latin.LastComposedWord; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.RichInputConnection; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; -import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; import com.android.inputmethod.latin.utils.AsyncResultHolder; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; -import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; import java.util.TreeSet; @@ -78,7 +75,8 @@ public final class InputLogic { private int mSpaceState; // Never null public SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; - public final Suggest mSuggest = new Suggest(); + public final Suggest mSuggest; + private final DictionaryFacilitator mDictionaryFacilitator; public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; public final WordComposer mWordComposer; @@ -87,7 +85,7 @@ public final class InputLogic { private int mDeleteCount; private long mLastKeyTime; - public final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet(); + public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>(); // Keeps track of most recently inserted text (multi-character key) for reverting private String mEnteredText; @@ -97,13 +95,23 @@ public final class InputLogic { private boolean mIsAutoCorrectionIndicatorOn; private long mDoubleSpacePeriodCountdownStart; + /** + * Create a new instance of the input logic. + * @param latinIME the instance of the parent LatinIME. We should remove this when we can. + * @param suggestionStripViewAccessor an object to access the suggestion strip view. + * @param dictionaryFacilitator facilitator for getting suggestions and updating user history + * dictionary. + */ public InputLogic(final LatinIME latinIME, - final SuggestionStripViewAccessor suggestionStripViewAccessor) { + final SuggestionStripViewAccessor suggestionStripViewAccessor, + final DictionaryFacilitator dictionaryFacilitator) { mLatinIME = latinIME; mSuggestionStripViewAccessor = suggestionStripViewAccessor; mWordComposer = new WordComposer(); mConnection = new RichInputConnection(latinIME); mInputLogicHandler = InputLogicHandler.NULL_HANDLER; + mSuggest = new Suggest(dictionaryFacilitator); + mDictionaryFacilitator = dictionaryFacilitator; } /** @@ -117,13 +125,16 @@ public final class InputLogic { * * @param restarting whether input is starting in the same field as before. Unused for now. * @param editorInfo the editorInfo associated with the editor. + * @param combiningSpec the combining spec string for this subtype */ - public void startInput(final boolean restarting, final EditorInfo editorInfo) { + public void startInput(final boolean restarting, final EditorInfo editorInfo, + final String combiningSpec) { mEnteredText = null; + mWordComposer.restartCombining(combiningSpec); resetComposingState(true /* alsoResetLastComposedWord */); mDeleteCount = 0; mSpaceState = SpaceState.NONE; - mRecapitalizeStatus.deactivate(); + mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once mCurrentlyPressedHardwareKeys.clear(); mSuggestedWords = SuggestedWords.EMPTY; // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying @@ -138,6 +149,14 @@ public final class InputLogic { } /** + * Call this when the subtype changes. + * @param combiningSpec the spec string for the combining rules + */ + public void onSubtypeChanged(final String combiningSpec) { + mWordComposer.restartCombining(combiningSpec); + } + + /** * Clean up the input logic after input is finished. */ public void finishInput() { @@ -156,7 +175,7 @@ public final class InputLogic { final InputLogicHandler inputLogicHandler = mInputLogicHandler; mInputLogicHandler = InputLogicHandler.NULL_HANDLER; inputLogicHandler.destroy(); - mSuggest.mDictionaryFacilitator.closeDictionaries(); + mDictionaryFacilitator.closeDictionaries(); } /** @@ -179,19 +198,11 @@ public final class InputLogic { resetComposingState(true /* alsoResetLastComposedWord */); } handler.postUpdateSuggestionStrip(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS - && ResearchLogger.RESEARCH_KEY_OUTPUT_TEXT.equals(rawText)) { - ResearchLogger.getInstance().onResearchKeySelected(mLatinIME); - return; - } final String text = performSpecificTldProcessingOnTextInput(rawText); if (SpaceState.PHANTOM == mSpaceState) { promotePhantomSpace(settingsValues); } mConnection.commitText(text, 1); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onTextInput(text, false /* isBatchMode */); - } mConnection.endBatchEdit(); // Space state must be updated before calling updateShiftState mSpaceState = SpaceState.NONE; @@ -218,14 +229,8 @@ public final class InputLogic { // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && suggestedWords.isPunctuationSuggestions()) { // Word separators are suggested before the user inputs something. - // So, LatinImeLogger logs "" as a user's input. - LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. final Event event = Event.createPunctuationSuggestionPickedEvent(suggestionInfo); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, - false /* isBatchMode */, suggestedWords.mIsPrediction); - } return onCodeInput(settingsValues, event, keyboardShiftState, handler); } @@ -248,7 +253,7 @@ public final class InputLogic { // code path as for other kinds, use commitChosenWord, and do everything normally. We will // however need to reset the suggestion strip right away, because we know we can't take // the risk of calling commitCompletion twice because we don't know how the app will react. - if (SuggestedWordInfo.KIND_APP_DEFINED == suggestionInfo.mKind) { + if (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_APP_DEFINED)) { mSuggestedWords = SuggestedWords.EMPTY; mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); @@ -261,14 +266,8 @@ public final class InputLogic { // We need to log before we commit, because the word composer will store away the user // typed word. final String replacedWord = mWordComposer.getTypedWord(); - LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords); commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, - mWordComposer.isBatchMode(), suggestionInfo.mScore, - suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType); - } mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -278,18 +277,12 @@ public final class InputLogic { // We should show the "Touch again to save" hint if the user pressed the first entry // AND it's in none of our current dictionaries (main, user or otherwise). - final DictionaryFacilitatorForSuggest dictionaryFacilitator = - mSuggest.mDictionaryFacilitator; final boolean showingAddToDictionaryHint = - (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind - || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind) - && !dictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */); + (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_TYPED) + || suggestionInfo.isKindOf(SuggestedWordInfo.KIND_OOV_CORRECTION)) + && !mDictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */); - if (settingsValues.mIsInternal) { - LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } - if (showingAddToDictionaryHint && dictionaryFacilitator.isUserDictionaryEnabled()) { + if (showingAddToDictionaryHint && mDictionaryFacilitator.isUserDictionaryEnabled()) { mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. @@ -326,8 +319,16 @@ public final class InputLogic { || !mWordComposer.isComposingWord(); // safe to reset final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd); final int moveAmount = newSelStart - oldSelStart; - if (selectionChangedOrSafeToReset && (hasOrHadSelection - || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + // As an added small gift from the framework, it happens upon rotation when there + // is a selection that we get a wrong cursor position delivered to startInput() that + // does not get reflected in the oldSel{Start,End} parameters to the next call to + // onUpdateSelection. In this case, we may have set a composition, and when we're here + // we realize we shouldn't have. In theory, in this case, selectionChangedOrSafeToReset + // should be true, but that is if the framework had taken that wrong cursor position + // into account, which means we have to reset the entire composing state whenever there + // is or was a selection regardless of whether it changed or not. + if (hasOrHadSelection || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -352,10 +353,12 @@ public final class InputLogic { newSelStart, newSelEnd, false /* shouldFinishComposition */); } + // The cursor has been moved : we now accept to perform recapitalization + mRecapitalizeStatus.enable(); // We moved the cursor. If we are touching a word, we need to resume suggestion. - mLatinIME.mHandler.postResumeSuggestions(); - // Reset the last recapitalization. - mRecapitalizeStatus.deactivate(); + mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */); + // Stop the last recapitalization, if started. + mRecapitalizeStatus.stop(); return true; } @@ -376,16 +379,9 @@ public final class InputLogic { final int keyboardShiftMode, // TODO: remove this argument final LatinIME.UIHandler handler) { - // TODO: rework the following to not squash the keycode and the code point into the same - // var because it's confusing. Instead the switch() should handle this in a readable manner. - final int code = - Event.NOT_A_CODE_POINT == event.mCodePoint ? event.mKeyCode : event.mCodePoint; final InputTransaction inputTransaction = new InputTransaction(settingsValues, event, SystemClock.uptimeMillis(), mSpaceState, getActualCapsMode(settingsValues, keyboardShiftMode)); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onCodeInput(code, event.mX, event.mY); - } if (event.mKeyCode != Constants.CODE_DELETE || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) { mDeleteCount = 0; @@ -407,7 +403,6 @@ public final class InputLogic { switch (event.mKeyCode) { case Constants.CODE_DELETE: handleBackspace(inputTransaction); - LatinImeLogger.logOnDelete(event.mX, event.mY); break; case Constants.CODE_SHIFT: performRecapitalization(inputTransaction.mSettingsValues); @@ -513,12 +508,6 @@ public final class InputLogic { ++mAutoCommitSequenceNumber; mConnection.beginBatchEdit(); if (mWordComposer.isComposingWord()) { - if (settingsValues.mIsInternal) { - if (mWordComposer.isBatchMode()) { - LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ", - mWordComposer); - } - } if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the batch input at the current cursor position. @@ -555,11 +544,8 @@ public final class InputLogic { } } mConnection.endBatchEdit(); - mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( - getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), - // Prev word is 1st word before cursor - getNthPreviousWordForSuggestion( - settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); + mWordComposer.setCapitalizedModeAtStartComposingTime( + getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode())); } /* The sequence number member is only used in onUpdateBatchInput. It is increased each time @@ -588,16 +574,15 @@ public final class InputLogic { if (null != candidate && mSuggestedWords.mSequenceNumber >= mAutoCommitSequenceNumber) { if (candidate.mSourceDict.shouldAutoCommit(candidate)) { - final String[] commitParts = candidate.mWord.split(" ", 2); + final String[] commitParts = candidate.mWord.split(Constants.WORD_SEPARATOR, 2); batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord); promotePhantomSpace(settingsValues); mConnection.commitText(commitParts[0], 0); mSpaceState = SpaceState.PHANTOM; keyboardSwitcher.requestUpdatingShiftState( getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState()); - mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( - getActualCapsMode(settingsValues, - keyboardSwitcher.getKeyboardShiftMode()), commitParts[0]); + mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode( + settingsValues, keyboardSwitcher.getKeyboardShiftMode())); ++mAutoCommitSequenceNumber; } } @@ -657,19 +642,9 @@ public final class InputLogic { || Character.getType(codePoint) == Character.OTHER_SYMBOL) { didAutoCorrect = handleSeparator(inputTransaction, inputTransaction.mEvent.isSuggestionStripPress(), handler); - if (inputTransaction.mSettingsValues.mIsInternal) { - LatinImeLoggerUtils.onSeparator((char)codePoint, - inputTransaction.mEvent.mX, inputTransaction.mEvent.mY); - } } else { didAutoCorrect = false; if (SpaceState.PHANTOM == inputTransaction.mSpaceState) { - if (inputTransaction.mSettingsValues.mIsInternal) { - if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) { - LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ", - mWordComposer); - } - } if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. @@ -730,11 +705,10 @@ public final class InputLogic { (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations) || !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces)) { // Reset entirely the composing state anyway, then start composing a new word unless - // the character is a single quote or a dash. The idea here is, single quote and dash - // are not separators and they should be treated as normal characters, except in the - // first position where they should not start composing a word. - isComposingWord = (Constants.CODE_SINGLE_QUOTE != codePoint - && Constants.CODE_DASH != codePoint); + // the character is a word connector. The idea here is, word connectors are not + // separators and they should be treated as normal characters, except in the first + // position where they should not start composing a word. + isComposingWord = !settingsValues.mSpacingAndPunctuations.isWordConnector(codePoint); // Here we don't need to reset the last composed word. It will be reset // when we commit this one, if we ever do; if on the other hand we backspace // it entirely and resume suggestions on the previous word, we'd like to still @@ -745,11 +719,7 @@ public final class InputLogic { mWordComposer.processEvent(inputTransaction.mEvent); // If it's the first letter, make note of auto-caps state if (mWordComposer.isSingleLetter()) { - // We pass 1 to getPreviousWordForSuggestion because we were not composing a word - // yet, so the word we want is the 1st word before the cursor. - mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( - inputTransaction.mShiftState, getNthPreviousWordForSuggestion( - settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); + mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState); } mConnection.setComposingText(getTextWithUnderline( mWordComposer.getTypedWord()), 1); @@ -767,10 +737,6 @@ public final class InputLogic { mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); } inputTransaction.setRequiresUpdateSuggestions(); - if (settingsValues.mIsInternal) { - LatinImeLoggerUtils.onNonSeparator((char)codePoint, inputTransaction.mEvent.mX, - inputTransaction.mEvent.mY); - } } /** @@ -784,12 +750,13 @@ public final class InputLogic { // TODO: remove this argument final LatinIME.UIHandler handler) { final int codePoint = inputTransaction.mEvent.mCodePoint; + final SettingsValues settingsValues = inputTransaction.mSettingsValues; boolean didAutoCorrect = false; + final boolean wasComposingWord = mWordComposer.isComposingWord(); // We avoid sending spaces in languages without spaces if we were composing. final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint - && !inputTransaction.mSettingsValues.mSpacingAndPunctuations - .mCurrentLanguageHasSpaces - && mWordComposer.isComposingWord(); + && !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces + && wasComposingWord; if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the separator at the current cursor position. @@ -798,13 +765,13 @@ public final class InputLogic { } // isComposingWord() may have changed since we stored wasComposing if (mWordComposer.isComposingWord()) { - if (inputTransaction.mSettingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR : StringUtils.newSingleCodePointString(codePoint); - commitCurrentAutoCorrection(inputTransaction.mSettingsValues, separator, handler); + commitCurrentAutoCorrection(settingsValues, separator, handler); didAutoCorrect = true; } else { - commitTyped(inputTransaction.mSettingsValues, + commitTyped(settingsValues, StringUtils.newSingleCodePointString(codePoint)); } } @@ -821,38 +788,41 @@ public final class InputLogic { // Double quotes behave like they are usually preceded by space iff we are // not inside a double quote or after a digit. needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit; + } else if (settingsValues.mSpacingAndPunctuations.isClusteringSymbol(codePoint) + && settingsValues.mSpacingAndPunctuations.isClusteringSymbol( + mConnection.getCodePointBeforeCursor())) { + needsPrecedingSpace = false; } else { - needsPrecedingSpace = inputTransaction.mSettingsValues.isUsuallyPrecededBySpace( - codePoint); + needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint); } if (needsPrecedingSpace) { - promotePhantomSpace(inputTransaction.mSettingsValues); - } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleSeparator(codePoint, mWordComposer.isComposingWord()); + promotePhantomSpace(settingsValues); } if (!shouldAvoidSendingCode) { - sendKeyCodePoint(inputTransaction.mSettingsValues, codePoint); + sendKeyCodePoint(settingsValues, codePoint); } if (Constants.CODE_SPACE == codePoint) { if (maybeDoubleSpacePeriod(inputTransaction)) { inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + inputTransaction.setRequiresUpdateSuggestions(); mSpaceState = SpaceState.DOUBLE; } else if (!mSuggestedWords.isPunctuationSuggestions()) { mSpaceState = SpaceState.WEAK; } startDoubleSpacePeriodCountdown(inputTransaction); - inputTransaction.setRequiresUpdateSuggestions(); + if (wasComposingWord) { + inputTransaction.setRequiresUpdateSuggestions(); + } } else { if (swapWeakSpace) { swapSwapperAndSpace(inputTransaction); mSpaceState = SpaceState.SWAP_PUNCTUATION; } else if ((SpaceState.PHANTOM == inputTransaction.mSpaceState - && inputTransaction.mSettingsValues.isUsuallyFollowedBySpace(codePoint)) + && settingsValues.isUsuallyFollowedBySpace(codePoint)) || (Constants.CODE_DOUBLE_QUOTE == codePoint && isInsideDoubleQuoteOrAfterDigit)) { // If we are in phantom space state, and the user presses a separator, we want to @@ -907,23 +877,20 @@ public final class InputLogic { } if (mWordComposer.isComposingWord()) { if (mWordComposer.isBatchMode()) { - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final String word = mWordComposer.getTypedWord(); - ResearchLogger.latinIME_handleBackspace_batch(word, 1); - } final String rejectedSuggestion = mWordComposer.getTypedWord(); mWordComposer.reset(); mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion); } else { mWordComposer.processEvent(inputTransaction.mEvent); } - mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + if (mWordComposer.isComposingWord()) { + mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + } else { + mConnection.commitText("", 1); + } inputTransaction.setRequiresUpdateSuggestions(); } else { if (mLastComposedWord.canRevertCommit()) { - if (inputTransaction.mSettingsValues.mIsInternal) { - LatinImeLoggerUtils.onAutoCorrectionCancellation(); - } revertCommit(inputTransaction); return; } @@ -932,9 +899,6 @@ public final class InputLogic { // This is triggered on backspace after a key that inputs multiple characters, // like the smiley key or the .com key. mConnection.deleteSurroundingText(mEnteredText.length(), 0); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace_cancelTextInput(mEnteredText); - } mEnteredText = null; // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false. // In addition we know that spaceState is false, and that we should not be @@ -946,6 +910,9 @@ public final class InputLogic { if (mConnection.revertDoubleSpacePeriod()) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) + inputTransaction.setRequiresUpdateSuggestions(); + mWordComposer.setCapitalizedModeAtStartComposingTime( + WordComposer.CAPS_MODE_OFF); return; } } else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { @@ -964,10 +931,6 @@ public final class InputLogic { mConnection.setSelection(mConnection.getExpectedSelectionEnd(), mConnection.getExpectedSelectionEnd()); mConnection.deleteSurroundingText(numCharsDeleted, 0); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace(numCharsDeleted, - false /* shouldUncommitLogUnit */); - } } else { // There is no selection, just delete one character. if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) { @@ -1002,10 +965,6 @@ public final class InputLogic { final int lengthToDelete = Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1; mConnection.deleteSurroundingText(lengthToDelete, 0); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace(lengthToDelete, - true /* shouldUncommitLogUnit */); - } if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) { final int codePointBeforeCursorToDeleteAgain = mConnection.getCodePointBeforeCursor(); @@ -1013,21 +972,18 @@ public final class InputLogic { final int lengthToDeleteAgain = Character.isSupplementaryCodePoint( codePointBeforeCursorToDeleteAgain) ? 2 : 1; mConnection.deleteSurroundingText(lengthToDeleteAgain, 0); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain, - true /* shouldUncommitLogUnit */); - } } } } } - if (inputTransaction.mSettingsValues.isSuggestionStripVisible() + if (inputTransaction.mSettingsValues + .isCurrentOrientationAllowingSuggestionsPerUserSettings() && inputTransaction.mSettingsValues.mSpacingAndPunctuations .mCurrentLanguageHasSpaces && !mConnection.isCursorFollowedByWordCharacter( inputTransaction.mSettingsValues.mSpacingAndPunctuations)) { restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues, - true /* includeResumedWordInSuggestions */); + true /* shouldIncludeResumedWordInSuggestions */); } } } @@ -1053,9 +1009,6 @@ public final class InputLogic { mConnection.deleteSurroundingText(2, 0); final String text = lastTwo.charAt(1) + " "; mConnection.commitText(text, 1); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text); - } inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); } } @@ -1139,11 +1092,6 @@ public final class InputLogic { final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations .mSentenceSeparatorAndSpace; mConnection.commitText(textToInsert, 1); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert, - false /* isBatchMode */); - } - mWordComposer.discardPreviousWordForSuggestion(); return true; } return false; @@ -1180,18 +1128,24 @@ public final class InputLogic { * @param settingsValues The current settings values. */ private void performRecapitalization(final SettingsValues settingsValues) { - if (!mConnection.hasSelection()) { - return; // No selection + if (!mConnection.hasSelection() || !mRecapitalizeStatus.mIsEnabled()) { + return; // No selection or recapitalize is disabled for now + } + final int selectionStart = mConnection.getExpectedSelectionStart(); + final int selectionEnd = mConnection.getExpectedSelectionEnd(); + final int numCharsSelected = selectionEnd - selectionStart; + if (numCharsSelected > Constants.MAX_CHARACTERS_FOR_RECAPITALIZATION) { + // We bail out if we have too many characters for performance reasons. We don't want + // to suck possibly multiple-megabyte data. + return; } - // If we have a recapitalize in progress, use it; otherwise, create a new one. - if (!mRecapitalizeStatus.isActive() - || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(), - mConnection.getExpectedSelectionEnd())) { + // If we have a recapitalize in progress, use it; otherwise, start a new one. + if (!mRecapitalizeStatus.isStarted() + || !mRecapitalizeStatus.isSetAt(selectionStart, selectionEnd)) { final CharSequence selectedText = mConnection.getSelectedText(0 /* flags, 0 for no styles */); if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection - mRecapitalizeStatus.initialize(mConnection.getExpectedSelectionStart(), - mConnection.getExpectedSelectionEnd(), selectedText.toString(), + mRecapitalizeStatus.start(selectionStart, selectionEnd, selectedText.toString(), settingsValues.mLocale, settingsValues.mSpacingAndPunctuations.mSortedWordSeparators); // We trim leading and trailing whitespace. @@ -1199,30 +1153,27 @@ public final class InputLogic { } mConnection.finishComposingText(); mRecapitalizeStatus.rotate(); - final int numCharsDeleted = mConnection.getExpectedSelectionEnd() - - mConnection.getExpectedSelectionStart(); - mConnection.setSelection(mConnection.getExpectedSelectionEnd(), - mConnection.getExpectedSelectionEnd()); - mConnection.deleteSurroundingText(numCharsDeleted, 0); + mConnection.setSelection(selectionEnd, selectionEnd); + mConnection.deleteSurroundingText(numCharsSelected, 0); mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0); mConnection.setSelection(mRecapitalizeStatus.getNewCursorStart(), mRecapitalizeStatus.getNewCursorEnd()); } private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues, - final String suggestion, final String prevWord) { + final String suggestion, final PrevWordsInfo prevWordsInfo) { // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. - if (!settingsValues.mCorrectionEnabled) return; + if (!settingsValues.mAutoCorrectionEnabled) return; if (TextUtils.isEmpty(suggestion)) return; final boolean wasAutoCapitalized = mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps(); final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds( System.currentTimeMillis()); - mSuggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized, prevWord, - timeStampInSeconds); + mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized, + prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive); } public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) { @@ -1240,7 +1191,7 @@ public final class InputLogic { return; } - final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>(); + final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>(); mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override @@ -1271,12 +1222,12 @@ public final class InputLogic { * do nothing. * * @param settingsValues the current values of the settings. - * @param includeResumedWordInSuggestions whether to include the word on which we resume + * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume * suggestions in the suggestion list. */ // TODO: make this private. public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues, - final boolean includeResumedWordInSuggestions) { + final boolean shouldIncludeResumedWordInSuggestions) { // HACK: We may want to special-case some apps that exhibit bad behavior in case of // recorrection. This is a temporary, stopgap measure that will be removed later. // TODO: remove this. @@ -1300,9 +1251,7 @@ public final class InputLogic { final int expectedCursorPosition = mConnection.getExpectedSelectionStart(); if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) { // Show predictions. - mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( - WordComposer.CAPS_MODE_OFF, - getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations, 1)); + mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF); mLatinIME.mHandler.postUpdateSuggestionStrip(); return; } @@ -1319,9 +1268,9 @@ public final class InputLogic { // we just do not resume because it's safer. final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor(); if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; - final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); final String typedWord = range.mWord.toString(); - if (includeResumedWordInSuggestions) { + if (shouldIncludeResumedWordInSuggestions) { suggestions.add(new SuggestedWordInfo(typedWord, SuggestedWords.MAX_SUGGESTIONS + 1, SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, @@ -1347,20 +1296,22 @@ public final class InputLogic { } } final int[] codePoints = StringUtils.toCodePointArray(typedWord); + // We want the previous word for suggestion. If we have chars in the word + // before the cursor, then we want the word before that, hence 2; otherwise, + // we want the word immediately before the cursor, hence 1. + final PrevWordsInfo prevWordsInfo = getPrevWordsInfoFromNthPreviousWordForSuggestion( + settingsValues.mSpacingAndPunctuations, + 0 == numberOfCharsInWordBeforeCursor ? 1 : 2); mWordComposer.setComposingWord(codePoints, - mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), - getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations, - // We want the previous word for suggestion. If we have chars in the word - // before the cursor, then we want the word before that, hence 2; otherwise, - // we want the word immediately before the cursor, hence 1. - 0 == numberOfCharsInWordBeforeCursor ? 1 : 2)); + mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); mWordComposer.setCursorPositionWithinWord( typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); - if (suggestions.isEmpty()) { - // We come here if there weren't any suggestion spans on this word. We will try to - // compute suggestions for it instead. + if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) { + // If there weren't any suggestion spans on this word, suggestions#size() will be 1 + // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we + // have no useful suggestions, so we will try to compute some for it instead. mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override @@ -1368,7 +1319,7 @@ public final class InputLogic { final SuggestedWords suggestedWordsIncludingTypedWord) { final SuggestedWords suggestedWords; if (suggestedWordsIncludingTypedWord.size() > 1 - && !includeResumedWordInSuggestions) { + && !shouldIncludeResumedWordInSuggestions) { // We were able to compute new suggestions for this word. // Remove the typed word, since we don't want to display it in this // case. The #getSuggestedWordsExcludingTypedWord() method sets @@ -1408,7 +1359,7 @@ public final class InputLogic { * @param inputTransaction The transaction in progress. */ private void revertCommit(final InputTransaction inputTransaction) { - final String previousWord = mLastComposedWord.mPrevWord; + final PrevWordsInfo prevWordsInfo = mLastComposedWord.mPrevWordsInfo; final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; final CharSequence committedWord = mLastComposedWord.mCommittedWord; final String committedWordString = committedWord.toString(); @@ -1430,9 +1381,8 @@ public final class InputLogic { } } mConnection.deleteSurroundingText(deleteLength, 0); - if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { - mSuggest.mDictionaryFacilitator.cancelAddingUserHistory( - previousWord, committedWordString); + if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && !TextUtils.isEmpty(committedWord)) { + mDictionaryFacilitator.cancelAddingUserHistory(prevWordsInfo, committedWordString); } final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; final SpannableString textToCommit = new SpannableString(stringToCommit); @@ -1442,7 +1392,7 @@ public final class InputLogic { committedWord.length(), Object.class); final int lastCharIndex = textToCommit.length() - 1; // We will collect all suggestions in the following array. - final ArrayList<String> suggestions = CollectionUtils.newArrayList(); + final ArrayList<String> suggestions = new ArrayList<>(); // First, add the committed word to the list of suggestions. suggestions.add(committedWordString); for (final Object span : spans) { @@ -1481,20 +1431,10 @@ public final class InputLogic { // with the typed word, so we need to resume suggestions right away. final int[] codePoints = StringUtils.toCodePointArray(stringToCommit); mWordComposer.setComposingWord(codePoints, - mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), previousWord); + mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); mConnection.setComposingText(textToCommit, 1); } - if (inputTransaction.mSettingsValues.mIsInternal) { - LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_revertCommit(committedWord.toString(), - originallyTypedWord.toString(), - mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString); - } - // Don't restart suggestion yet. We'll restart if the user deletes the - // separator. + // Don't restart suggestion yet. We'll restart if the user deletes the separator. mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; // We have a separator between the word and the cursor: we should show predictions. inputTransaction.setRequiresUpdateSuggestions(); @@ -1546,7 +1486,7 @@ public final class InputLogic { } public int getCurrentRecapitalizeState() { - if (!mRecapitalizeStatus.isActive() + if (!mRecapitalizeStatus.isStarted() || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd())) { // Not recapitalizing at the moment @@ -1563,21 +1503,24 @@ public final class InputLogic { } /** - * Get the nth previous word before the cursor as context for the suggestion process. + * Get information fo previous words from the nth previous word before the cursor as context + * for the suggestion process. * @param spacingAndPunctuations the current spacing and punctuations settings. * @param nthPreviousWord reverse index of the word to get (1-indexed) - * @return the nth previous word before the cursor. + * @return the information of previous words */ // TODO: Make this private - public CharSequence getNthPreviousWordForSuggestion( + public PrevWordsInfo getPrevWordsInfoFromNthPreviousWordForSuggestion( final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) { if (spacingAndPunctuations.mCurrentLanguageHasSpaces) { // If we are typing in a language with spaces we can just look up the previous - // word from textview. - return mConnection.getNthPreviousWord(spacingAndPunctuations, nthPreviousWord); + // word information from textview. + return mConnection.getPrevWordsInfoFromNthPreviousWord( + spacingAndPunctuations, nthPreviousWord); } else { - return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null - : mLastComposedWord.mCommittedWord; + return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? + PrevWordsInfo.BEGINNING_OF_SENTENCE : + new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString()); } } @@ -1755,9 +1698,6 @@ public final class InputLogic { */ // TODO: replace these two parameters with an InputTransaction private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) { - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_sendKeyCodePoint(codePoint); - } // TODO: Remove this special handling of digit letters. // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. if (codePoint >= '0' && codePoint <= '9') { @@ -1789,9 +1729,6 @@ public final class InputLogic { if (settingsValues.shouldInsertSpacesAutomatically() && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces && !mConnection.textBeforeCursorLooksLikeURL()) { - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_promotePhantomSpace(); - } sendKeyCodePoint(settingsValues, Constants.CODE_SPACE); } } @@ -1833,9 +1770,6 @@ public final class InputLogic { mConnection.setComposingText(batchInputText, 1); } mConnection.endBatchEdit(); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords); - } // Space state must be updated before calling updateShiftState mSpaceState = SpaceState.PHANTOM; keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues), @@ -1862,9 +1796,6 @@ public final class InputLogic { if (!mWordComposer.isComposingWord()) return; final String typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode()); - } commitChosenWord(settingsValues, typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString); } @@ -1904,15 +1835,6 @@ public final class InputLogic { throw new RuntimeException("We have an auto-correction but the typed word " + "is empty? Impossible! I must commit suicide."); } - if (settingsValues.mIsInternal) { - LatinImeLoggerUtils.onAutoCorrection( - typedWord, autoCorrection, separator, mWordComposer); - } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final SuggestedWords suggestedWords = mSuggestedWords; - ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, - separator, mWordComposer.isBatchMode(), suggestedWords); - } commitChosenWord(settingsValues, autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator); if (!typedWord.equals(autoCorrection)) { @@ -1943,33 +1865,19 @@ public final class InputLogic { final CharSequence chosenWordWithSuggestions = SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, suggestedWords); - mConnection.commitText(chosenWordWithSuggestions, 1); - // TODO: we pass 2 here, but would it be better to move this above and pass 1 instead? - final String prevWord = mConnection.getNthPreviousWord( + // Use the 2nd previous word as the previous word because the 1st previous word is the word + // to be committed. + final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord( settingsValues.mSpacingAndPunctuations, 2); + mConnection.commitText(chosenWordWithSuggestions, 1); // Add the word to the user history dictionary - performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWord); + performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo); // TODO: figure out here if this is an auto-correct or if the best word is actually // what user typed. Note: currently this is done much later in // LastComposedWord#didCommitTypedWord by string equality of the remembered // strings. mLastComposedWord = mWordComposer.commitWord(commitType, - chosenWordWithSuggestions, separatorString, prevWord); - final boolean shouldDiscardPreviousWordForSuggestion; - if (0 == StringUtils.codePointCount(separatorString)) { - // Separator is 0-length, we can keep the previous word for suggestion. Either this - // was a manual pick or the language has no spaces in which case we want to keep the - // previous word, or it was the keyboard closing or the cursor moving in which case it - // will be reset anyway. - shouldDiscardPreviousWordForSuggestion = false; - } else { - // Otherwise, we discard if the separator contains any non-whitespace. - shouldDiscardPreviousWordForSuggestion = - !StringUtils.containsOnlyWhitespace(separatorString); - } - if (shouldDiscardPreviousWordForSuggestion) { - mWordComposer.discardPreviousWordForSuggestion(); - } + chosenWordWithSuggestions, separatorString, prevWordsInfo); } /** @@ -1989,9 +1897,11 @@ public final class InputLogic { final boolean tryResumeSuggestions, final int remainingTries, // TODO: remove these arguments final LatinIME.UIHandler handler) { + final boolean shouldFinishComposition = mConnection.hasSelection() + || !mConnection.isCursorPositionKnown(); if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess( mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), - false /* shouldFinishComposition */)) { + shouldFinishComposition)) { if (0 < remainingTries) { handler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return false; @@ -2001,7 +1911,9 @@ public final class InputLogic { } mConnection.tryFixLyingCursorPosition(); if (tryResumeSuggestions) { - handler.postResumeSuggestions(); + // This is triggered when starting input anew, so we want to include the resumed + // word in suggestions. + handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); } return true; } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index f25503488..a2ae74b20 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -186,10 +186,17 @@ public final class FormatSpec { // From version 4 on, we use version * 100 + revision as a version number. That allows // us to change the format during development while having testing devices remove // older files with each upgrade, while still having a readable versioning scheme. + // When we bump up the dictionary format version, we should update + // ExpandableDictionary.needsToMigrateDictionary() and + // ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType(). public static final int VERSION2 = 2; - public static final int VERSION4 = 401; + // 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; + static final int MAXIMUM_SUPPORTED_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/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index 853392200..31cb59756 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CombinedFormatUtils; import com.android.inputmethod.latin.utils.StringUtils; @@ -35,6 +34,8 @@ public final class WordProperty implements Comparable<WordProperty> { public final ProbabilityInfo mProbabilityInfo; public final ArrayList<WeightedString> mShortcutTargets; public final ArrayList<WeightedString> mBigrams; + // TODO: Support mIsBeginningOfSentence. + public final boolean mIsBeginningOfSentence; public final boolean mIsNotAWord; public final boolean mIsBlacklistEntry; public final boolean mHasShortcuts; @@ -51,6 +52,7 @@ public final class WordProperty implements Comparable<WordProperty> { mProbabilityInfo = probabilityInfo; mShortcutTargets = shortcutTargets; mBigrams = bigrams; + mIsBeginningOfSentence = false; mIsNotAWord = isNotAWord; mIsBlacklistEntry = isBlacklistEntry; mHasBigrams = bigrams != null && !bigrams.isEmpty(); @@ -75,8 +77,9 @@ public final class WordProperty implements Comparable<WordProperty> { final ArrayList<Integer> shortcutProbabilities) { mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); - mShortcutTargets = CollectionUtils.newArrayList(); - mBigrams = CollectionUtils.newArrayList(); + mShortcutTargets = new ArrayList<>(); + mBigrams = new ArrayList<>(); + mIsBeginningOfSentence = false; mIsNotAWord = isNotAWord; mIsBlacklistEntry = isBlacklisted; mHasShortcuts = hasShortcuts; diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java index a446672cb..ab3ef964e 100644 --- a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java +++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java @@ -35,7 +35,7 @@ public class AccountUtils { } public static List<String> getDeviceAccountsEmailAddresses(final Context context) { - final ArrayList<String> retval = new ArrayList<String>(); + final ArrayList<String> retval = new ArrayList<>(); for (final Account account : getAccounts(context)) { final String name = account.name; if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) { @@ -54,7 +54,7 @@ public class AccountUtils { */ public static List<String> getDeviceAccountsWithDomain( final Context context, final String domain) { - final ArrayList<String> retval = new ArrayList<String>(); + final ArrayList<String> retval = new ArrayList<>(); final String atDomain = "@" + domain.toLowerCase(Locale.ROOT); for (final Account account : getAccounts(context)) { if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) { diff --git a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java new file mode 100644 index 000000000..a96018fe9 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java @@ -0,0 +1,59 @@ +/* + * 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.personalization; + +import android.content.Context; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.ExpandableBinaryDictionary; + +import java.io.File; +import java.util.Locale; + +public class ContextualDictionary extends ExpandableBinaryDictionary { + /* package */ static final String NAME = ContextualDictionary.class.getSimpleName(); + + private ContextualDictionary(final Context context, final Locale locale, + final File dictFile) { + super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_CONTEXTUAL, + dictFile); + // Always reset the contents. + clear(); + } + + @UsedForTesting + public static ContextualDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix) { + return new ContextualDictionary(context, locale, dictFile); + } + + @Override + protected boolean enableBeginningOfSentencePrediction() { + return true; + } + + @Override + public boolean isValidWord(final String word) { + // Strings out of this dictionary should not be considered existing words. + return false; + } + + @Override + protected void loadInitialContentsLocked() { + } +} diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 352288f8b..1ba7b366f 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -18,15 +18,11 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.makedict.DictionaryHeader; -import com.android.inputmethod.latin.utils.LanguageModelParam; import java.io.File; -import java.util.ArrayList; import java.util.Locale; import java.util.Map; @@ -35,7 +31,6 @@ import java.util.Map; * model. */ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary { - private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName(); private static final boolean DBG_DUMP_ON_CLOSE = false; /** Any pair being typed or picked */ @@ -47,8 +42,6 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB /** The locale for this dictionary. */ public final Locale mLocale; - private Map<String, String> mAdditionalAttributeMap = null; - protected DecayingExpandableBinaryDictionaryBase(final Context context, final String dictName, final Locale locale, final String dictionaryType, final File dictFile) { @@ -72,9 +65,6 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB @Override protected Map<String, String> getHeaderAttributeMap() { final Map<String, String> attributeMap = super.getHeaderAttributeMap(); - if (mAdditionalAttributeMap != null) { - attributeMap.putAll(mAdditionalAttributeMap); - } attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY, DictionaryHeader.ATTRIBUTE_VALUE_TRUE); attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY, @@ -83,23 +73,17 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB } @Override - protected boolean haveContentsChanged() { - return false; - } - - @Override protected void loadInitialContentsLocked() { // No initial contents. } - @UsedForTesting - public void clearAndFlushDictionaryWithAdditionalAttributes( - final Map<String, String> attributeMap) { - mAdditionalAttributeMap = attributeMap; - clear(); - } - /* package */ void runGCIfRequired() { runGCIfRequired(false /* mindsBlockByGC */); } + + @Override + public boolean isValidWord(final String word) { + // Strings out of this dictionary should not be considered existing words. + return false; + } } diff --git a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java index de2744f29..221bb9a8f 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java +++ b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java @@ -61,6 +61,7 @@ public class DictionaryDecayBroadcastReciever extends BroadcastReceiver { final String action = intent.getAction(); if (action.equals(DICTIONARY_DECAY_INTENT_ACTION)) { PersonalizationHelper.runGCOnAllOpenedUserHistoryDictionaries(); + PersonalizationHelper.runGCOnAllOpenedPersonalizationDictionaries(); } } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java new file mode 100644 index 000000000..9d72de8c5 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java @@ -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. + */ + +package com.android.inputmethod.latin.personalization; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class PersonalizationDataChunk { + public final boolean mInputByUser; + public final List<String> mTokens; + public final int mTimestampInSeconds; + public final String mPackageName; + public final Locale mlocale = null; + + public PersonalizationDataChunk(boolean inputByUser, final List<String> tokens, + final int timestampInSeconds, final String packageName) { + mInputByUser = inputByUser; + mTokens = Collections.unmodifiableList(tokens); + mTimestampInSeconds = timestampInSeconds; + mPackageName = packageName; + } +} diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index 4afd5b4c9..f2ad22ac7 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Dictionary; import java.io.File; @@ -26,19 +27,15 @@ import java.util.Locale; public class PersonalizationDictionary extends DecayingExpandableBinaryDictionaryBase { /* package */ static final String NAME = PersonalizationDictionary.class.getSimpleName(); + // TODO: Make this constructor private /* package */ PersonalizationDictionary(final Context context, final Locale locale) { - this(context, locale, null /* dictFile */); + super(context, getDictName(NAME, locale, null /* dictFile */), locale, + Dictionary.TYPE_PERSONALIZATION, null /* dictFile */); } - public PersonalizationDictionary(final Context context, final Locale locale, - final File dictFile) { - super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_PERSONALIZATION, - dictFile); - } - - @Override - public boolean isValidWord(final String word) { - // Strings out of this dictionary should not be considered existing words. - return false; + @UsedForTesting + public static PersonalizationDictionary getDictionary(final Context context, + final Locale locale, final File dictFile, final String dictNamePrefix) { + return PersonalizationHelper.getPersonalizationDictionary(context, locale); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java index 9bef7a198..450644032 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java @@ -19,18 +19,15 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; import android.content.res.Configuration; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; -import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.DictionaryFacilitator; public class PersonalizationDictionarySessionRegistrar { public static void init(final Context context, - final DictionaryFacilitatorForSuggest dictionaryFacilitator, - final DistracterFilter distracterFilter) { + final DictionaryFacilitator dictionaryFacilitator) { } public static void onConfigurationChanged(final Context context, final Configuration conf, - final DictionaryFacilitatorForSuggest dictionaryFacilitator, - final DistracterFilter distracterFilter) { + final DictionaryFacilitator dictionaryFacilitator) { } public static void onUpdateData(final Context context, final String type) { diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index 7c43182bc..aac40940b 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -16,13 +16,11 @@ package com.android.inputmethod.latin.personalization; -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.FileUtils; - import android.content.Context; import android.util.Log; +import com.android.inputmethod.latin.utils.FileUtils; + import java.io.File; import java.io.FilenameFilter; import java.lang.ref.SoftReference; @@ -34,9 +32,9 @@ public class PersonalizationHelper { private static final String TAG = PersonalizationHelper.class.getSimpleName(); private static final boolean DEBUG = false; private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> - sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap(); + sLangUserHistoryDictCache = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>> - sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap(); + sLangPersonalizationDictCache = new ConcurrentHashMap<>(); public static UserHistoryDictionary getUserHistoryDictionary( final Context context, final Locale locale) { @@ -55,8 +53,7 @@ public class PersonalizationHelper { } } final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale); - sLangUserHistoryDictCache.put(localeStr, - new SoftReference<UserHistoryDictionary>(dict)); + sLangUserHistoryDictCache.put(localeStr, new SoftReference<>(dict)); return dict; } } @@ -66,8 +63,8 @@ public class PersonalizationHelper { if (TimeUnit.MILLISECONDS.toSeconds( DictionaryDecayBroadcastReciever.DICTIONARY_DECAY_INTERVAL) < currentTimestamp - sCurrentTimestampForTesting) { - // TODO: Run GC for both PersonalizationDictionary and UserHistoryDictionary. runGCOnAllOpenedUserHistoryDictionaries(); + runGCOnAllOpenedPersonalizationDictionaries(); } } @@ -75,7 +72,6 @@ public class PersonalizationHelper { runGCOnAllDictionariesIfRequired(sLangUserHistoryDictCache); } - @UsedForTesting public static void runGCOnAllOpenedPersonalizationDictionaries() { runGCOnAllDictionariesIfRequired(sLangPersonalizationDictCache); } @@ -110,8 +106,7 @@ public class PersonalizationHelper { } } final PersonalizationDictionary dict = new PersonalizationDictionary(context, locale); - sLangPersonalizationDictCache.put( - localeStr, new SoftReference<PersonalizationDictionary>(dict)); + sLangPersonalizationDictCache.put(localeStr, new SoftReference<>(dict)); return dict; } } @@ -140,11 +135,13 @@ public class PersonalizationHelper { } } dictionaryMap.clear(); - if (!FileUtils.deleteFilteredFiles( - context.getFilesDir(), new DictFilter(dictNamePrefix))) { + final File filesDir = context.getFilesDir(); + if (filesDir == null) { + Log.e(TAG, "context.getFilesDir() returned null."); + } + if (!FileUtils.deleteFilteredFiles(filesDir, new DictFilter(dictNamePrefix))) { Log.e(TAG, "Cannot remove all existing dictionary files. filesDir: " - + context.getFilesDir().getAbsolutePath() + ", dictNamePrefix: " - + dictNamePrefix); + + filesDir.getAbsolutePath() + ", dictNamePrefix: " + dictNamePrefix); } } } diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index 8a29c354d..3916fc24c 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -18,9 +18,12 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; +import com.android.inputmethod.latin.PrevWordsInfo; +import com.android.inputmethod.latin.utils.DistracterFilter; import java.io.File; import java.util.Locale; @@ -32,46 +35,47 @@ import java.util.Locale; public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase { /* package */ static final String NAME = UserHistoryDictionary.class.getSimpleName(); + // TODO: Make this constructor private /* package */ UserHistoryDictionary(final Context context, final Locale locale) { - this(context, locale, null /* dictFile */); + super(context, getDictName(NAME, locale, null /* dictFile */), locale, + Dictionary.TYPE_USER_HISTORY, null /* dictFile */); } - public UserHistoryDictionary(final Context context, final Locale locale, - final File dictFile) { - super(context, getDictName(NAME, locale, dictFile), locale, Dictionary.TYPE_USER_HISTORY, - dictFile); - } - - @Override - public boolean isValidWord(final String word) { - // Strings out of this dictionary should not be considered existing words. - return false; + @UsedForTesting + public static UserHistoryDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix) { + return PersonalizationHelper.getUserHistoryDictionary(context, locale); } /** - * Pair will be added to the user history dictionary. + * Add a word to the user history dictionary. * - * The first word may be null. That means we don't know the context, in other words, - * it's only a unigram. The first word may also be an empty string : this means start - * context, as in beginning of a sentence for example. - * The second word may not be null (a NullPointerException would be thrown). + * @param userHistoryDictionary the user history dictionary + * @param prevWordsInfo the information of previous words + * @param word the word the user inputted + * @param isValid whether the word is valid or not + * @param timestamp the timestamp when the word has been inputted + * @param distracterFilter the filter to check whether the word is a distracter */ public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary, - final String word0, final String word1, final boolean isValid, final int timestamp) { - if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || - (word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { + final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid, + final int timestamp, final DistracterFilter distracterFilter) { + final String prevWord = prevWordsInfo.mPrevWord; + if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || + (prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { return; } final int frequency = isValid ? FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS; - userHistoryDictionary.addWordDynamically(word1, frequency, null /* shortcutTarget */, - 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, timestamp); + userHistoryDictionary.addUnigramEntryWithCheckingDistracter(word, frequency, + null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */, + false /* isBlacklisted */, timestamp, distracterFilter); // Do not insert a word as a bigram of itself - if (word1.equals(word0)) { + if (word.equals(prevWord)) { return; } - if (null != word0) { - userHistoryDictionary.addBigramDynamically(word0, word1, frequency, timestamp); + if (null != prevWord) { + userHistoryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp); } } } diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java index 39977e76f..31fa86774 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java @@ -47,7 +47,6 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -101,7 +100,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { super(context, android.R.layout.simple_spinner_item); setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet(); + final TreeSet<SubtypeLocaleItem> items = new TreeSet<>(); final InputMethodInfo imi = RichInputMethodManager.getInstance() .getInputMethodInfoOfThisIme(); final int count = imi.getSubtypeCount(); @@ -369,7 +368,6 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { mSubtype = (InputMethodSubtype)source.readParcelable(null); } - @SuppressWarnings("hiding") public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { @Override @@ -516,8 +514,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { localeString, keyboardLayoutSetName); } - private AlertDialog createDialog( - @SuppressWarnings("unused") final SubtypePreference subtypePref) { + private AlertDialog createDialog(final SubtypePreference subtypePref) { final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) @@ -555,7 +552,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment { private InputMethodSubtype[] getSubtypes() { final PreferenceGroup group = getPreferenceScreen(); - final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList(); + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); final int count = group.getPreferenceCount(); for (int i = 0; i < count; i++) { final Preference pref = group.getPreference(i); diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index c4c1234fc..845ddb377 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -25,11 +25,12 @@ import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceFragment; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver; -import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug; import com.android.inputmethod.latin.utils.ApplicationUtils; @@ -40,8 +41,6 @@ public final class DebugSettings extends PreferenceFragment public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; - public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - public static final String PREF_STATISTICS_LOGGING = "enable_logging"; public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = "pref_key_preview_show_up_start_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = @@ -51,18 +50,14 @@ public final class DebugSettings extends PreferenceFragment public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = "pref_key_preview_dismiss_duration"; private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary"; - private static final String PREF_DUMP_CONTACTS_DICT = "dump_contacts_dict"; - private static final String PREF_DUMP_USER_DICT = "dump_user_dict"; - private static final String PREF_DUMP_USER_HISTORY_DICT = "dump_user_history_dict"; - private static final String PREF_DUMP_PERSONALIZATION_DICT = "dump_personalization_dict"; + private static final String PREF_KEY_DUMP_DICTS = "pref_key_dump_dictionaries"; + private static final String PREF_KEY_DUMP_DICT_PREFIX = "pref_key_dump_dictionaries"; + private static final String DICT_NAME_KEY_FOR_EXTRAS = "dict_name"; public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview"; public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; - private static final boolean SHOW_STATISTICS_LOGGING = false; - private boolean mServiceNeedsRestart = false; private CheckBoxPreference mDebugMode; - private CheckBoxPreference mStatisticsLoggingPref; @Override public void onCreate(Bundle icicle) { @@ -71,21 +66,6 @@ public final class DebugSettings extends PreferenceFragment SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); prefs.registerOnSharedPreferenceChangeListener(this); - final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE); - if (usabilityStudyPref instanceof CheckBoxPreference) { - final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref; - checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, - LatinImeLogger.getUsabilityStudyMode(prefs))); - checkbox.setSummary(R.string.settings_warning_researcher_mode); - } - final Preference statisticsLoggingPref = findPreference(PREF_STATISTICS_LOGGING); - if (statisticsLoggingPref instanceof CheckBoxPreference) { - mStatisticsLoggingPref = (CheckBoxPreference) statisticsLoggingPref; - if (!SHOW_STATISTICS_LOGGING) { - getPreferenceScreen().removePreference(statisticsLoggingPref); - } - } - final PreferenceScreen readExternalDictionary = (PreferenceScreen) findPreference(PREF_READ_EXTERNAL_DICTIONARY); if (null != readExternalDictionary) { @@ -101,16 +81,18 @@ public final class DebugSettings extends PreferenceFragment }); } + final PreferenceGroup dictDumpPreferenceGroup = + (PreferenceGroup)findPreference(PREF_KEY_DUMP_DICTS); final OnPreferenceClickListener dictDumpPrefClickListener = new DictDumpPrefClickListener(this); - findPreference(PREF_DUMP_CONTACTS_DICT).setOnPreferenceClickListener( - dictDumpPrefClickListener); - findPreference(PREF_DUMP_USER_DICT).setOnPreferenceClickListener( - dictDumpPrefClickListener); - findPreference(PREF_DUMP_USER_HISTORY_DICT).setOnPreferenceClickListener( - dictDumpPrefClickListener); - findPreference(PREF_DUMP_PERSONALIZATION_DICT).setOnPreferenceClickListener( - dictDumpPrefClickListener); + for (final String dictName : DictionaryFacilitator.DICT_TYPE_TO_CLASS.keySet()) { + final Preference preference = new Preference(getActivity()); + preference.setKey(PREF_KEY_DUMP_DICT_PREFIX + dictName); + preference.setTitle("Dump " + dictName + " dictionary"); + preference.setOnPreferenceClickListener(dictDumpPrefClickListener); + preference.getExtras().putString(DICT_NAME_KEY_FOR_EXTRAS, dictName); + dictDumpPreferenceGroup.addPreference(preference); + } final Resources res = getResources(); setupKeyLongpressTimeoutSettings(prefs, res); setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_DURATION, @@ -138,18 +120,7 @@ public final class DebugSettings extends PreferenceFragment @Override public boolean onPreferenceClick(final Preference arg0) { - final String dictName; - if (arg0.getKey().equals(PREF_DUMP_CONTACTS_DICT)) { - dictName = Dictionary.TYPE_CONTACTS; - } else if (arg0.getKey().equals(PREF_DUMP_USER_DICT)) { - dictName = Dictionary.TYPE_USER; - } else if (arg0.getKey().equals(PREF_DUMP_USER_HISTORY_DICT)) { - dictName = Dictionary.TYPE_USER_HISTORY; - } else if (arg0.getKey().equals(PREF_DUMP_PERSONALIZATION_DICT)) { - dictName = Dictionary.TYPE_PERSONALIZATION; - } else { - dictName = null; - } + final String dictName = arg0.getExtras().getString(DICT_NAME_KEY_FOR_EXTRAS); if (dictName != null) { final Intent intent = new Intent(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); @@ -163,27 +134,22 @@ public final class DebugSettings extends PreferenceFragment @Override public void onStop() { super.onStop(); - if (mServiceNeedsRestart) Process.killProcess(Process.myPid()); + if (mServiceNeedsRestart) { + Process.killProcess(Process.myPid()); + } } @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key.equals(PREF_DEBUG_MODE)) { - if (mDebugMode != null) { - mDebugMode.setChecked(prefs.getBoolean(PREF_DEBUG_MODE, false)); - final boolean checked = mDebugMode.isChecked(); - if (mStatisticsLoggingPref != null) { - if (checked) { - getPreferenceScreen().addPreference(mStatisticsLoggingPref); - } else { - getPreferenceScreen().removePreference(mStatisticsLoggingPref); - } - } - updateDebugMode(); - mServiceNeedsRestart = true; - } - } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { + if (key.equals(PREF_DEBUG_MODE) && mDebugMode != null) { + mDebugMode.setChecked(prefs.getBoolean(PREF_DEBUG_MODE, false)); + updateDebugMode(); mServiceNeedsRestart = true; + return; + } + if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { + mServiceNeedsRestart = true; + return; } } diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java index cd726c969..04a2ee3ce 100644 --- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java +++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java @@ -20,7 +20,8 @@ public class NativeSuggestOptions { // Need to update suggest_options.h when you add, remove or reorder options. private static final int IS_GESTURE = 0; private static final int USE_FULL_EDIT_DISTANCE = 1; - private static final int OPTIONS_SIZE = 2; + private static final int BLOCK_OFFENSIVE_WORDS = 2; + private static final int OPTIONS_SIZE = 3; private final int[] mOptions = new int[OPTIONS_SIZE + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE]; @@ -33,6 +34,10 @@ public class NativeSuggestOptions { setBooleanOption(USE_FULL_EDIT_DISTANCE, value); } + public void setBlockOffensiveWords(final boolean value) { + setBooleanOption(BLOCK_OFFENSIVE_WORDS, value); + } + public void setAdditionalFeaturesOptions(final int[] additionalOptions) { if (additionalOptions == null) { return; diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index a3aae8cb3..235847799 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.res.Resources; +import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; @@ -60,11 +61,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang "pref_key_use_double_space_period"; public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = "pref_key_block_potentially_offensive"; + public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS = + (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + || (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT + && Build.VERSION.CODENAME.equals("REL")); public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = "pref_show_language_switch_key"; public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = "pref_include_other_imes_in_language_switch_list"; - public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916"; + public static final String PREF_KEYBOARD_THEME = "pref_keyboard_theme"; public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles"; // TODO: consolidate key preview dismiss delay with the key preview animation parameters. public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = @@ -87,6 +92,8 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_DEBUG_SETTINGS = "debug_settings"; public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal"; + public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging"; + // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead. // This is being used only for the backward compatibility. private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY = @@ -325,10 +332,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION)); } - public static boolean readUsabilityStudyMode(final SharedPreferences prefs) { - return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); - } - public static float readKeyPreviewAnimationScale(final SharedPreferences prefs, final String prefKey, final float defaultValue) { final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 22cbd204c..5eb0377c7 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -37,6 +37,7 @@ import android.util.Log; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; +import com.android.inputmethod.keyboard.KeyboardTheme; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -58,7 +59,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false; private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS = DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS - || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */; + || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { final Preference preference = findPreference(preferenceKey); @@ -151,10 +152,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment miscSettings.removePreference(aboutSettings); } } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - // The about screen contains items that may be confusing in development-only versions. - miscSettings.removePreference(aboutSettings); - } final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); @@ -168,6 +165,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment removePreference(Settings.PREF_VIBRATE_ON, generalSettings); removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings); } + if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) { + removePreference( + Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, advancedSettings); + removePreference( + Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, advancedSettings); + } + // TODO: consolidate key preview dismiss delay with the key preview animation parameters. if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { @@ -198,9 +202,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedSettings); } - setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, - Settings.readShowsLanguageSwitchKey(prefs)); - final PreferenceGroup textCorrectionGroup = (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS); final PreferenceScreen dictionaryLink = @@ -212,6 +213,20 @@ public final class SettingsFragment extends InputMethodSettingsFragment textCorrectionGroup.removePreference(dictionaryLink); } + if (ProductionFlag.IS_METRICS_LOGGING_SUPPORTED) { + final Preference enableMetricsLogging = + findPreference(Settings.PREF_ENABLE_METRICS_LOGGING); + if (enableMetricsLogging != null) { + final int applicationLabelRes = context.getApplicationInfo().labelRes; + final String applicationName = res.getString(applicationLabelRes); + final String enableMetricsLoggingTitle = res.getString( + R.string.enable_metrics_logging, applicationName); + enableMetricsLogging.setTitle(enableMetricsLoggingTitle); + } + } else { + removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, textCorrectionGroup); + } + final Preference editPersonalDictionary = findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY); final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent(); @@ -253,11 +268,31 @@ public final class SettingsFragment extends InputMethodSettingsFragment } updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT); + final ListPreference keyboardThemePref = (ListPreference)findPreference( + Settings.PREF_KEYBOARD_THEME); + if (keyboardThemePref != null) { + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); + final String value = Integer.toString(keyboardTheme.mThemeId); + final CharSequence entries[] = keyboardThemePref.getEntries(); + final int entryIndex = keyboardThemePref.findIndexOfValue(value); + keyboardThemePref.setSummary(entryIndex < 0 ? null : entries[entryIndex]); + keyboardThemePref.setValue(value); + } updateCustomInputStylesSummary(prefs, res); } @Override + public void onPause() { + super.onPause(); + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final ListPreference keyboardThemePref = (ListPreference)findPreference( + Settings.PREF_KEYBOARD_THEME); + if (keyboardThemePref != null) { + KeyboardTheme.saveKeyboardThemeId(keyboardThemePref.getValue(), prefs); + } + } + + @Override public void onDestroy() { getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( this); @@ -278,16 +313,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment if (key.equals(Settings.PREF_POPUP_ON)) { setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, Settings.readKeyPreviewPopupEnabled(prefs, res)); - } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) { - setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, - Settings.readShowsLanguageSwitchKey(prefs)); } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); } ensureConsistencyOfAutoCorrectionSettings(); updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME); refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index dde50ccaf..44104019b 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -28,6 +28,7 @@ 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; @@ -73,6 +74,7 @@ public final class SettingsValues { public final boolean mPhraseGestureEnabled; public final int mKeyLongpressTimeout; public final Locale mLocale; + public final boolean mEnableMetricsLogging; // From the input box public final InputAttributes mInputAttributes; @@ -83,7 +85,8 @@ public final class SettingsValues { public final int mKeyPreviewPopupDismissDelay; private final boolean mAutoCorrectEnabled; public final float mAutoCorrectionThreshold; - public final boolean mCorrectionEnabled; + // TODO: Rename this to mAutoCorrectionEnabledPerUserSettings. + public final boolean mAutoCorrectionEnabled; public final int mSuggestionVisibility; public final int mDisplayOrientation; private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds; @@ -108,7 +111,8 @@ public final class SettingsValues { // Store the input attributes if (null == inputAttributes) { - mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); + mInputAttributes = new InputAttributes( + null, false /* isFullscreenMode */, context.getPackageName()); } else { mInputAttributes = inputAttributes; } @@ -120,13 +124,18 @@ public final class SettingsValues { mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); mSlidingKeyInputPreviewEnabled = prefs.getBoolean( DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); - mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res); + mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) + && !mInputAttributes.mIsPasswordField + && !mInputAttributes.hasNoMicrophoneKeyOption() + && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); - mIncludesOtherImesInLanguageSwitchList = prefs.getBoolean( - Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false); - mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs); + mIncludesOtherImesInLanguageSwitchList = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS + ? prefs.getBoolean(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false) + : true /* forcibly */; + mShowsLanguageSwitchKey = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS + ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */; mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true); @@ -134,7 +143,7 @@ public final class SettingsValues { mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res); mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout); - + mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true); // Compute other readable settings mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res); mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); @@ -147,7 +156,7 @@ public final class SettingsValues { mGestureFloatingPreviewTextEnabled = prefs.getBoolean( Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); - mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; + mAutoCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; final String showSuggestionsSetting = prefs.getString( Settings.PREF_SHOW_SUGGESTIONS_SETTING, res.getString(R.string.prefs_suggestion_visibility_default_value)); @@ -170,7 +179,7 @@ public final class SettingsValues { ResourceUtils.getFloatFromFraction( res, R.fraction.config_key_preview_dismiss_end_scale)); mDisplayOrientation = res.getConfiguration().orientation; - mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>(); + mAppWorkarounds = new AsyncResultHolder<>(); final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo( mInputAttributes.mTargetApplicationPackageName); if (null != packageInfo) { @@ -185,12 +194,14 @@ public final class SettingsValues { return mInputAttributes.mApplicationSpecifiedCompletionOn; } + // TODO: Rename this to needsToLookupSuggestions(). public boolean isSuggestionsRequested() { - return mInputAttributes.mIsSettingsSuggestionStripOn - && (mCorrectionEnabled || isSuggestionStripVisible()); + return mInputAttributes.mShouldShowSuggestions + && (mAutoCorrectionEnabled + || isCurrentOrientationAllowingSuggestionsPerUserSettings()); } - public boolean isSuggestionStripVisible() { + public boolean isCurrentOrientationAllowingSuggestionsPerUserSettings() { return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE) || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT); @@ -205,7 +216,8 @@ public final class SettingsValues { } public boolean isWordCodePoint(final int code) { - return Character.isLetter(code) || isWordConnector(code); + return Character.isLetter(code) || isWordConnector(code) + || Character.COMBINING_SPACING_MARK == Character.getType(code); } public boolean isUsuallyPrecededBySpace(final int code) { @@ -313,18 +325,18 @@ public final class SettingsValues { private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs, final Resources res) { - if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) { - // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to - // {@link Settings#PREF_VOICE_INPUT_KEY}. + // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to + // {@link Settings#PREF_VOICE_INPUT_KEY}. + if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { final String voiceModeMain = res.getString(R.string.voice_mode_main); final String voiceMode = prefs.getString( Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode); - prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply(); - } - // Remove the obsolete preference if exists. - if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { - prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply(); + prefs.edit() + .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey) + // Remove the obsolete preference if exists. + .remove(Settings.PREF_VOICE_MODE_OBSOLETE) + .apply(); } return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true); } @@ -385,8 +397,8 @@ public final class SettingsValues { sb.append("" + mAutoCorrectEnabled); sb.append("\n mAutoCorrectionThreshold = "); sb.append("" + mAutoCorrectionThreshold); - sb.append("\n mCorrectionEnabled = "); - sb.append("" + mCorrectionEnabled); + sb.append("\n mAutoCorrectionEnabled = "); + sb.append("" + mAutoCorrectionEnabled); sb.append("\n mSuggestionVisibility = "); sb.append("" + mSuggestionVisibility); sb.append("\n mDisplayOrientation = "); diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 796921f71..b8d2a2248 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -30,6 +30,7 @@ import java.util.Locale; public final class SpacingAndPunctuations { private final int[] mSortedSymbolsPrecededBySpace; private final int[] mSortedSymbolsFollowedBySpace; + private final int[] mSortedSymbolsClusteringTogether; private final int[] mSortedWordConnectors; public final int[] mSortedWordSeparators; public final PunctuationSuggestions mSuggestPuncList; @@ -46,6 +47,8 @@ public final class SpacingAndPunctuations { // To be able to binary search the code point. See {@link #isUsuallyFollowedBySpace(int)}. mSortedSymbolsFollowedBySpace = StringUtils.toSortedCodePointArray( res.getString(R.string.symbols_followed_by_space)); + mSortedSymbolsClusteringTogether = StringUtils.toSortedCodePointArray( + res.getString(R.string.symbols_clustering_together)); // To be able to binary search the code point. See {@link #isWordConnector(int)}. mSortedWordConnectors = StringUtils.toSortedCodePointArray( res.getString(R.string.symbols_word_connectors)); @@ -85,6 +88,10 @@ public final class SpacingAndPunctuations { return Arrays.binarySearch(mSortedSymbolsFollowedBySpace, code) >= 0; } + public boolean isClusteringSymbol(final int code) { + return Arrays.binarySearch(mSortedSymbolsClusteringTogether, code) >= 0; + } + public boolean isSentenceSeparator(final int code) { return code == mSentenceSeparator; } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java index 974dfddd3..73d25f6aa 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java @@ -21,13 +21,13 @@ import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; +import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; public final class SetupStartIndicatorView extends LinearLayout { @@ -96,13 +96,13 @@ public final class SetupStartIndicatorView extends LinearLayout { @Override protected void onDraw(final Canvas canvas) { super.onDraw(canvas); - final int layoutDirection = ViewCompatUtils.getLayoutDirection(this); + final int layoutDirection = ViewCompat.getLayoutDirection(this); final int width = getWidth(); final int height = getHeight(); final float halfHeight = height / 2.0f; final Path path = mIndicatorPath; path.rewind(); - if (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) { + if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) { // Left arrow path.moveTo(width, 0.0f); path.lineTo(0.0f, halfHeight); diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java index c909507c6..6734e61b8 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java @@ -20,10 +20,10 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; +import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.view.View; -import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; public final class SetupStepIndicatorView extends View { @@ -38,12 +38,12 @@ public final class SetupStepIndicatorView extends View { } public void setIndicatorPosition(final int stepPos, final int totalStepNum) { - final int layoutDirection = ViewCompatUtils.getLayoutDirection(this); + final int layoutDirection = ViewCompat.getLayoutDirection(this); // The indicator position is the center of the partition that is equally divided into // the total step number. final float partionWidth = 1.0f / totalStepNum; final float pos = stepPos * partionWidth + partionWidth / 2.0f; - mXRatio = (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos; + mXRatio = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos; invalidate(); } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java index 5072fabd6..bcac05a6a 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -37,7 +37,6 @@ import com.android.inputmethod.compat.TextViewCompatUtils; import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.settings.SettingsActivity; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import java.util.ArrayList; @@ -482,7 +481,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL static final class SetupStepGroup { private final SetupStepIndicatorView mIndicatorView; - private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList(); + private final ArrayList<SetupStep> mGroup = new ArrayList<>(); public SetupStepGroup(final SetupStepIndicatorView indicatorView) { mIndicatorView = indicatorView; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 65ebcf5f1..8d495646d 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -27,7 +27,6 @@ import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SuggestionsInfo; import com.android.inputmethod.keyboard.KeyboardLayoutSet; -import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.ContactsBinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryCollection; @@ -77,7 +76,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService private final Object mUseContactsLock = new Object(); private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList = - CollectionUtils.newHashSet(); + new HashSet<>(); public static final int SCRIPT_LATIN = 0; public static final int SCRIPT_CYRILLIC = 1; @@ -94,7 +93,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService // proximity to pass to the dictionary descent algorithm. // IMPORTANT: this only contains languages - do not write countries in there. // Only the language is searched from the map. - mLanguageToScript = CollectionUtils.newTreeMap(); + mLanguageToScript = new TreeMap<>(); mLanguageToScript.put("cs", SCRIPT_LATIN); mLanguageToScript.put("da", SCRIPT_LATIN); mLanguageToScript.put("de", SCRIPT_LATIN); @@ -255,7 +254,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService mOriginalText = originalText; mRecommendedThreshold = recommendedThreshold; mMaxLength = maxLength; - mSuggestions = CollectionUtils.newArrayList(maxLength + 1); + mSuggestions = new ArrayList<>(maxLength + 1); mScores = new int[mMaxLength]; } @@ -441,8 +440,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService } } dictionaryCollection.addDictionary(mContactsDictionary); - mDictionaryCollectionsList.add( - new WeakReference<DictionaryCollection>(dictionaryCollection)); + mDictionaryCollectionsList.add(new WeakReference<>(dictionaryCollection)); } return new DictAndKeyboard(dictionaryCollection, keyboardLayoutSet); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index ddda52d71..55274cfe2 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.spellcheck; +import android.content.res.Resources; import android.os.Binder; import android.text.TextUtils; import android.util.Log; @@ -23,17 +24,21 @@ import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.PrevWordsInfo; import java.util.ArrayList; +import java.util.Locale; public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName(); private static final boolean DBG = false; private final static String[] EMPTY_STRING_ARRAY = new String[0]; + private final Resources mResources; + private SentenceLevelAdapter mSentenceLevelAdapter; public AndroidSpellCheckerSession(AndroidSpellCheckerService service) { super(service); + mResources = service.getResources(); } private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti, @@ -43,10 +48,9 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck return null; } final int N = ssi.getSuggestionsCount(); - final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList(); - final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList(); - final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = - CollectionUtils.newArrayList(); + final ArrayList<Integer> additionalOffsets = new ArrayList<>(); + final ArrayList<Integer> additionalLengths = new ArrayList<>(); + final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = new ArrayList<>(); String currentWord = null; for (int i = 0; i < N; ++i) { final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i); @@ -57,7 +61,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck final int offset = ssi.getOffsetAt(i); final int length = ssi.getLengthAt(i); final String subText = typedText.substring(offset, offset + length); - final String prevWord = currentWord; + final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(currentWord); currentWord = subText; if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) { continue; @@ -73,7 +77,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck if (TextUtils.isEmpty(splitText)) { continue; } - if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) { + if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWordsInfo) == null) { continue; } final int newLength = splitText.length(); @@ -116,8 +120,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck @Override public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { - final SentenceSuggestionsInfo[] retval = - super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit); + final SentenceSuggestionsInfo[] retval = splitAndSuggest(textInfos, suggestionsLimit); if (retval == null || retval.length != textInfos.length) { return retval; } @@ -131,6 +134,58 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck return retval; } + /** + * Get sentence suggestions for specified texts in an array of TextInfo. This is taken from + * SpellCheckerService#onGetSentenceSuggestionsMultiple that we can't use because it's + * using private variables. + * The default implementation splits the input text to words and returns + * {@link SentenceSuggestionsInfo} which contains suggestions for each word. + * This function will run on the incoming IPC thread. + * So, this is not called on the main thread, + * but will be called in series on another thread. + * @param textInfos an array of the text metadata + * @param suggestionsLimit the maximum number of suggestions to be returned + * @return an array of {@link SentenceSuggestionsInfo} returned by + * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)} + */ + private SentenceSuggestionsInfo[] splitAndSuggest(TextInfo[] textInfos, int suggestionsLimit) { + if (textInfos == null || textInfos.length == 0) { + return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; + } + SentenceLevelAdapter sentenceLevelAdapter; + synchronized(this) { + sentenceLevelAdapter = mSentenceLevelAdapter; + if (sentenceLevelAdapter == null) { + final String localeStr = getLocale(); + if (!TextUtils.isEmpty(localeStr)) { + sentenceLevelAdapter = new SentenceLevelAdapter(mResources, + new Locale(localeStr)); + mSentenceLevelAdapter = sentenceLevelAdapter; + } + } + } + if (sentenceLevelAdapter == null) { + return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS; + } + final int infosSize = textInfos.length; + final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize]; + for (int i = 0; i < infosSize; ++i) { + final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams = + sentenceLevelAdapter.getSplitWords(textInfos[i]); + final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems = + textInfoParams.mItems; + final int itemsSize = mItems.size(); + final TextInfo[] splitTextInfos = new TextInfo[itemsSize]; + for (int j = 0; j < itemsSize; ++j) { + splitTextInfos[j] = mItems.get(j).mTextInfo; + } + retval[i] = SentenceLevelAdapter.reconstructSuggestions( + textInfoParams, onGetSuggestionsMultiple( + splitTextInfos, suggestionsLimit, true)); + } + return retval; + } + @Override public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { @@ -148,7 +203,8 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck } else { prevWord = null; } - retval[i] = onGetSuggestionsInternal(textInfos[i], prevWord, suggestionsLimit); + final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(prevWord); + retval[i] = onGetSuggestionsInternal(textInfos[i], prevWordsInfo, suggestionsLimit); retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 69d092751..54eebe399 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -28,9 +28,9 @@ import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; import com.android.inputmethod.compat.SuggestionsInfoCompatUtils; -import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer; @@ -68,29 +68,29 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { private static final char CHAR_DELIMITER = '\uFFFC'; private static final int MAX_CACHE_SIZE = 50; private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache = - new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE); + new LruCache<>(MAX_CACHE_SIZE); // TODO: Support n-gram input - private static String generateKey(String query, String prevWord) { - if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWord)) { + private static String generateKey(final String query, final PrevWordsInfo prevWordsInfo) { + if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWordsInfo.mPrevWord)) { return query; } - return query + CHAR_DELIMITER + prevWord; + return query + CHAR_DELIMITER + prevWordsInfo.mPrevWord; } - // TODO: Support n-gram input - public SuggestionsParams getSuggestionsFromCache(String query, String prevWord) { - return mUnigramSuggestionsInfoCache.get(generateKey(query, prevWord)); + public SuggestionsParams getSuggestionsFromCache(String query, + final PrevWordsInfo prevWordsInfo) { + return mUnigramSuggestionsInfoCache.get(generateKey(query, prevWordsInfo)); } - // TODO: Support n-gram input public void putSuggestionsToCache( - String query, String prevWord, String[] suggestions, int flags) { + final String query, final PrevWordsInfo prevWordsInfo, + final String[] suggestions, final int flags) { if (suggestions == null || TextUtils.isEmpty(query)) { return; } mUnigramSuggestionsInfoCache.put( - generateKey(query, prevWord), new SuggestionsParams(suggestions, flags)); + generateKey(query, prevWordsInfo), new SuggestionsParams(suggestions, flags)); } public void clearCache() { @@ -259,11 +259,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { } protected SuggestionsInfo onGetSuggestionsInternal( - final TextInfo textInfo, final String prevWord, final int suggestionsLimit) { + final TextInfo textInfo, final PrevWordsInfo prevWordsInfo, + final int suggestionsLimit) { try { final String inText = textInfo.getText(); final SuggestionsParams cachedSuggestionsParams = - mSuggestionsCache.getSuggestionsFromCache(inText, prevWord); + mSuggestionsCache.getSuggestionsFromCache(inText, prevWordsInfo); if (cachedSuggestionsParams != null) { if (DBG) { Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags); @@ -281,6 +282,22 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { return AndroidSpellCheckerService.getNotInDictEmptySuggestions( false /* reportAsTypo */); } + if (CHECKABILITY_CONTAINS_PERIOD == checkability) { + final String[] splitText = inText.split(Constants.REGEXP_PERIOD); + boolean allWordsAreValid = true; + for (final String word : splitText) { + if (!dictInfo.mDictionary.isValidWord(word)) { + allWordsAreValid = false; + break; + } + } + if (allWordsAreValid) { + return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO + | SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS, + new String[] { + TextUtils.join(Constants.STRING_SPACE, splitText) }); + } + } return dictInfo.mDictionary.isValidWord(inText) ? AndroidSpellCheckerService.getInDictEmptySuggestions() : AndroidSpellCheckerService.getNotInDictEmptySuggestions( @@ -322,12 +339,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { } else { coordinates = dictInfo.mKeyboard.getCoordinates(codePoints); } - composer.setComposingWord(codePoints, coordinates, null /* previousWord */); + composer.setComposingWord(codePoints, coordinates); // TODO: make a spell checker option to block offensive words or not final ArrayList<SuggestedWordInfo> suggestions = - dictInfo.mDictionary.getSuggestions(composer, prevWord, + dictInfo.mDictionary.getSuggestions(composer, prevWordsInfo, dictInfo.getProximityInfo(), true /* blockOffensiveWords */, - null /* additionalFeaturesOptions */, + null /* additionalFeaturesOptions */, 0 /* sessionId */, null /* inOutLanguageWeight */); if (suggestions != null) { for (final SuggestedWordInfo suggestion : suggestions) { @@ -369,7 +386,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS() : 0); final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions); - mSuggestionsCache.putSuggestionsToCache(text, prevWord, result.mSuggestions, flags); + mSuggestionsCache.putSuggestionsToCache(text, prevWordsInfo, result.mSuggestions, + flags); return retval; } catch (RuntimeException e) { // Don't kill the keyboard if there is a bug in the spell checker diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java index 1ffe50681..b33739fc1 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java @@ -16,11 +16,11 @@ package com.android.inputmethod.latin.spellcheck; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.Dictionary; /** * A container for a Dictionary and a Keyboard. @@ -28,19 +28,15 @@ import com.android.inputmethod.keyboard.ProximityInfo; public final class DictAndKeyboard { public final Dictionary mDictionary; public final Keyboard mKeyboard; - private final Keyboard mManualShiftedKeyboard; public DictAndKeyboard( final Dictionary dictionary, final KeyboardLayoutSet keyboardLayoutSet) { mDictionary = dictionary; if (keyboardLayoutSet == null) { mKeyboard = null; - mManualShiftedKeyboard = null; return; } mKeyboard = keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); - mManualShiftedKeyboard = - keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED); } public ProximityInfo getProximityInfo() { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index c99264347..cc52a3e0f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -20,9 +20,9 @@ import android.util.Log; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; import java.util.Locale; @@ -46,19 +46,19 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> { private final Locale mLocale; private int mSize; private volatile boolean mClosed; - final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList(); + final static ArrayList<SuggestedWordInfo> noSuggestions = new ArrayList<>(); private final static DictAndKeyboard dummyDict = new DictAndKeyboard( new Dictionary(Dictionary.TYPE_MAIN) { // TODO: this dummy dictionary should be a singleton in the Dictionary class. @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { + final int sessionId, final float[] inOutLanguageWeight) { return noSuggestions; } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { // This is never called. However if for some strange reason it ever gets // called, returning true is less destructive (it will not underline the // word in red). diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java new file mode 100644 index 000000000..13352f39e --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java @@ -0,0 +1,185 @@ +/* + * 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.spellcheck; + +import android.content.res.Resources; +import android.view.textservice.SentenceSuggestionsInfo; +import android.view.textservice.SuggestionsInfo; +import android.view.textservice.TextInfo; + +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; +import com.android.inputmethod.latin.utils.RunInLocale; + +import java.util.ArrayList; +import java.util.Locale; + +/** + * This code is mostly lifted directly from android.service.textservice.SpellCheckerService in + * the framework; maybe that should be protected instead, so that implementers don't have to + * rewrite everything for any small change. + */ +public class SentenceLevelAdapter { + public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS = + new SentenceSuggestionsInfo[] {}; + private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null); + /** + * Container for split TextInfo parameters + */ + public static class SentenceWordItem { + public final TextInfo mTextInfo; + public final int mStart; + public final int mLength; + public SentenceWordItem(TextInfo ti, int start, int end) { + mTextInfo = ti; + mStart = start; + mLength = end - start; + } + } + + /** + * Container for originally queried TextInfo and parameters + */ + public static class SentenceTextInfoParams { + final TextInfo mOriginalTextInfo; + final ArrayList<SentenceWordItem> mItems; + final int mSize; + public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) { + mOriginalTextInfo = ti; + mItems = items; + mSize = items.size(); + } + } + + private static class WordIterator { + private final SpacingAndPunctuations mSpacingAndPunctuations; + public WordIterator(final Resources res, final Locale locale) { + final RunInLocale<SpacingAndPunctuations> job + = new RunInLocale<SpacingAndPunctuations>() { + @Override + protected SpacingAndPunctuations job(final Resources res) { + return new SpacingAndPunctuations(res); + } + }; + mSpacingAndPunctuations = job.runInLocale(res, locale); + } + + public int getEndOfWord(final CharSequence sequence, int index) { + final int length = sequence.length(); + index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1); + while (index < length) { + final int codePoint = Character.codePointAt(sequence, index); + if (mSpacingAndPunctuations.isWordSeparator(codePoint)) { + // If it's a period, we want to stop here only if it's followed by another + // word separator. In all other cases we stop here. + if (Constants.CODE_PERIOD == codePoint) { + final int indexOfNextCodePoint = + index + Character.charCount(Constants.CODE_PERIOD); + if (indexOfNextCodePoint < length + && mSpacingAndPunctuations.isWordSeparator( + Character.codePointAt(sequence, indexOfNextCodePoint))) { + return index; + } + } else { + return index; + } + } + index += Character.charCount(codePoint); + } + return index; + } + + public int getBeginningOfNextWord(final CharSequence sequence, int index) { + final int length = sequence.length(); + if (index >= length) { + return -1; + } + index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1); + while (index < length) { + final int codePoint = Character.codePointAt(sequence, index); + if (!mSpacingAndPunctuations.isWordSeparator(codePoint)) { + return index; + } + index += Character.charCount(codePoint); + } + return -1; + } + } + + private final WordIterator mWordIterator; + public SentenceLevelAdapter(final Resources res, final Locale locale) { + mWordIterator = new WordIterator(res, locale); + } + + public SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) { + final WordIterator wordIterator = mWordIterator; + final CharSequence originalText = originalTextInfo.getText(); + final int cookie = originalTextInfo.getCookie(); + final int start = -1; + final int end = originalText.length(); + final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>(); + int wordStart = wordIterator.getBeginningOfNextWord(originalText, start); + int wordEnd = wordIterator.getEndOfWord(originalText, wordStart); + while (wordStart <= end && wordEnd != -1 && wordStart != -1) { + if (wordEnd >= start && wordEnd > wordStart) { + final String query = originalText.subSequence(wordStart, wordEnd).toString(); + final TextInfo ti = new TextInfo(query, cookie, query.hashCode()); + wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd)); + } + wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd); + if (wordStart == -1) { + break; + } + wordEnd = wordIterator.getEndOfWord(originalText, wordStart); + } + return new SentenceTextInfoParams(originalTextInfo, wordItems); + } + + public static SentenceSuggestionsInfo reconstructSuggestions( + SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) { + if (results == null || results.length == 0) { + return null; + } + if (originalTextInfoParams == null) { + return null; + } + final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie(); + final int originalSequence = + originalTextInfoParams.mOriginalTextInfo.getSequence(); + + final int querySize = originalTextInfoParams.mSize; + final int[] offsets = new int[querySize]; + final int[] lengths = new int[querySize]; + final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize]; + for (int i = 0; i < querySize; ++i) { + final SentenceWordItem item = originalTextInfoParams.mItems.get(i); + SuggestionsInfo result = null; + for (int j = 0; j < results.length; ++j) { + final SuggestionsInfo cur = results[j]; + if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) { + result = cur; + result.setCookieAndSequence(originalCookie, originalSequence); + break; + } + } + offsets[i] = item.mStart; + lengths[i] = item.mLength; + reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO; + } + return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths); + } +} diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java index a694bf47d..a6437bac3 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java @@ -20,6 +20,7 @@ import android.content.Context; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.ContactsBinaryDictionary; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; @@ -36,19 +37,19 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, - final String prevWordForBigrams, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { + final int sessionId, final float[] inOutLanguageWeight) { synchronized (mLock) { - return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions, inOutLanguageWeight); + return super.getSuggestions(codes, prevWordsInfo, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, inOutLanguageWeight); } } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { synchronized (mLock) { - return super.isValidWord(word); + return super.isInDictionary(word); } } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java index 1a6dd5818..8c9d5d681 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.spellcheck; import android.content.Context; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.UserBinaryDictionary; import com.android.inputmethod.latin.WordComposer; @@ -41,19 +42,19 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes, - final String prevWordForBigrams, final ProximityInfo proximityInfo, + final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final float[] inOutLanguageWeight) { + final int sessionId, final float[] inOutLanguageWeight) { synchronized (mLock) { - return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions, inOutLanguageWeight); + return super.getSuggestions(codes, prevWordsInfo, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId, inOutLanguageWeight); } } @Override - public boolean isValidWord(final String word) { + public boolean isInDictionary(final String word) { synchronized (mLock) { - return super.isValidWord(word); + return super.isInDictionary(word); } } } diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 5a325ea82..346aea34a 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -23,24 +23,17 @@ import android.graphics.drawable.Drawable; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.TypefaceUtils; public final class MoreSuggestions extends Keyboard { - public static final int SUGGESTION_CODE_BASE = 1024; - public final SuggestedWords mSuggestedWords; - public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter { - public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info); - } - MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) { super(params); mSuggestedWords = suggestedWords; @@ -178,7 +171,7 @@ public final class MoreSuggestions extends Keyboard { } } - private static boolean isIndexSubjectToAutoCorrection(final SuggestedWords suggestedWords, + static boolean isIndexSubjectToAutoCorrection(final SuggestedWords suggestedWords, final int index) { return suggestedWords.mWillAutoCorrect && index == SuggestedWords.INDEX_OF_AUTO_CORRECTION; } @@ -226,11 +219,7 @@ public final class MoreSuggestions extends Keyboard { word = mSuggestedWords.getLabel(index); info = mSuggestedWords.getDebugString(index); } - final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE; - final Key key = new Key(word, KeyboardIconsSet.ICON_UNDEFINED, - indexInMoreSuggestions, null /* outputText */, info, 0 /* labelFlags */, - Key.BACKGROUND_TYPE_NORMAL, x, y, width, params.mDefaultRowHeight, - params.mHorizontalGap, params.mVerticalGap); + final Key key = new MoreSuggestionKey(word, info, index, params); params.markAsEdgeKey(key, index); params.onAddKey(key); final int columnNumber = params.getColumnNumber(index); @@ -245,6 +234,19 @@ public final class MoreSuggestions extends Keyboard { } } + static final class MoreSuggestionKey extends Key { + public final int mSuggestedWordIndex; + + public MoreSuggestionKey(final String word, final String info, final int index, + final MoreSuggestionsParam params) { + super(word /* label */, KeyboardIconsSet.ICON_UNDEFINED, Constants.CODE_OUTPUT_TEXT, + word /* outputText */, info, 0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL, + params.getX(index), params.getY(index), params.getWidth(index), + params.mDefaultRowHeight, params.mHorizontalGap, params.mVerticalGap); + mSuggestedWordIndex = index; + } + } + private static final class Divider extends Key.Spacer { private final Drawable mIcon; diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index 549ff0d9d..528d500d2 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -20,11 +20,16 @@ import android.content.Context; import android.util.AttributeSet; import android.util.Log; +import com.android.inputmethod.accessibility.AccessibilityUtils; +import com.android.inputmethod.accessibility.MoreSuggestionsAccessibilityDelegate; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.MoreKeysKeyboardView; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionKey; /** * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting @@ -33,6 +38,10 @@ import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestions public final class MoreSuggestionsView extends MoreKeysKeyboardView { private static final String TAG = MoreSuggestionsView.class.getSimpleName(); + public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter { + public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info); + } + public MoreSuggestionsView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.moreKeysKeyboardViewStyle); } @@ -43,6 +52,26 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView { } @Override + public void setKeyboard(final Keyboard keyboard) { + super.setKeyboard(keyboard); + // With accessibility mode off, {@link #mAccessibilityDelegate} is set to null at the + // above {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call. + // With accessibility mode on, {@link #mAccessibilityDelegate} is set to a + // {@link MoreKeysKeyboardAccessibilityDelegate} object at the above + // {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call. And the object has to be + // overwritten by a {@link MoreSuggestionsAccessibilityDelegate} object here. + if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + if (!(mAccessibilityDelegate instanceof MoreSuggestionsAccessibilityDelegate)) { + mAccessibilityDelegate = new MoreSuggestionsAccessibilityDelegate( + this, mKeyDetector); + mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_suggestions); + mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_suggestions); + } + mAccessibilityDelegate.setKeyboard(keyboard); + } + } + + @Override protected int getDefaultCoordX() { final MoreSuggestions pane = (MoreSuggestions)getKeyboard(); return pane.mOccupiedWidth / 2; @@ -59,7 +88,12 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView { } @Override - public void onCodeInput(final int code, final int x, final int y) { + protected void onKeyInput(final Key key, final int x, final int y) { + if (!(key instanceof MoreSuggestionKey)) { + Log.e(TAG, "Expected key is MoreSuggestionKey, but found " + + key.getClass().getName()); + return; + } final Keyboard keyboard = getKeyboard(); if (!(keyboard instanceof MoreSuggestions)) { Log.e(TAG, "Expected keyboard is MoreSuggestions, but found " @@ -67,7 +101,7 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView { return; } final SuggestedWords suggestedWords = ((MoreSuggestions)keyboard).mSuggestedWords; - final int index = code - MoreSuggestions.SUGGESTION_CODE_BASE; + final int index = ((MoreSuggestionKey)key).mSuggestedWordIndex; if (index < 0 || index >= suggestedWords.size()) { Log.e(TAG, "Selected suggestion has an illegal index: " + index); return; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 1d84bb59f..19b48f081 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -309,9 +309,8 @@ final class SuggestionStripLayoutHelper { setupWordViewsTextAndColor(suggestedWords, mSuggestionsCountInStrip); final TextView centerWordView = mWordViews.get(mCenterPositionInStrip); - final int availableStripWidth = placerView.getWidth() - - placerView.getPaddingRight() - placerView.getPaddingLeft(); - final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, availableStripWidth); + final int stripWidth = stripView.getWidth(); + final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, stripWidth); final int countInStrip; if (suggestedWords.size() == 1 || getTextScaleX(centerWordView.getText(), centerWidth, centerWordView.getPaint()) < MIN_TEXT_XSCALE) { @@ -319,11 +318,11 @@ final class SuggestionStripLayoutHelper { // by consolidating all slots in the strip. countInStrip = 1; mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); - layoutWord(mCenterPositionInStrip, availableStripWidth - mPadding); + layoutWord(mCenterPositionInStrip, stripWidth - mPadding); stripView.addView(centerWordView); setLayoutWeight(centerWordView, 1.0f, ViewGroup.LayoutParams.MATCH_PARENT); if (SuggestionStripView.DBG) { - layoutDebugInfo(mCenterPositionInStrip, placerView, availableStripWidth); + layoutDebugInfo(mCenterPositionInStrip, placerView, stripWidth); } } else { countInStrip = mSuggestionsCountInStrip; @@ -337,7 +336,7 @@ final class SuggestionStripLayoutHelper { x += divider.getMeasuredWidth(); } - final int width = getSuggestionWidth(positionInStrip, availableStripWidth); + final int width = getSuggestionWidth(positionInStrip, stripWidth); final TextView wordView = layoutWord(positionInStrip, width); stripView.addView(wordView); setLayoutWeight(wordView, getSuggestionWeight(positionInStrip), @@ -380,9 +379,9 @@ final class SuggestionStripLayoutHelper { } else { wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); } - - // Disable this suggestion if the suggestion is null or empty. - wordView.setEnabled(!TextUtils.isEmpty(word)); + // {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack. + // Use a simple {@link String} to avoid the issue. + wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString()); final CharSequence text = getEllipsizedText(word, width, wordView.getPaint()); final float scaleX = getTextScaleX(word, width, wordView.getPaint()); wordView.setText(text); // TextView.setText() resets text scale x to 1.0. @@ -425,7 +424,9 @@ final class SuggestionStripLayoutHelper { final int countInStrip) { // Clear all suggestions first for (int positionInStrip = 0; positionInStrip < countInStrip; ++positionInStrip) { - mWordViews.get(positionInStrip).setText(null); + final TextView wordView = mWordViews.get(positionInStrip); + wordView.setText(null); + wordView.setTag(null); // Make this inactive for touches in {@link #layoutWord(int,int)}. if (SuggestionStripView.DBG) { mDebugInfoViews.get(positionInStrip).setText(null); @@ -459,14 +460,15 @@ final class SuggestionStripLayoutHelper { } final TextView wordView = mWordViews.get(positionInStrip); - wordView.setEnabled(true); - wordView.setTextColor(mColorAutoCorrect); + final String punctuation = punctuationSuggestions.getLabel(positionInStrip); // {@link TextView#getTag()} is used to get the index in suggestedWords at // {@link SuggestionStripView#onClick(View)}. wordView.setTag(positionInStrip); - wordView.setText(punctuationSuggestions.getLabel(positionInStrip)); + wordView.setText(punctuation); + wordView.setContentDescription(punctuation); wordView.setTextScaleX(1.0f); wordView.setCompoundDrawables(null, null, null, null); + wordView.setTextColor(mColorAutoCorrect); stripView.addView(wordView); setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight); } @@ -474,8 +476,8 @@ final class SuggestionStripLayoutHelper { return countInStrip; } - public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip, - final int stripWidth) { + public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) { + final int stripWidth = addToDictionaryStrip.getWidth(); final int width = stripWidth - mDividerWidth - mPadding * 2; final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index a0793b133..3be933ff7 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -18,7 +18,9 @@ package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.drawable.Drawable; import android.support.v4.view.ViewCompat; import android.text.TextUtils; import android.util.AttributeSet; @@ -31,6 +33,7 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewParent; +import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.TextView; @@ -39,17 +42,14 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; -import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; @@ -59,12 +59,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void addWordToUserDictionary(String word); public void showImportantNoticeContents(); public void pickSuggestionManually(int index, SuggestedWordInfo word); + public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat); } static final boolean DBG = LatinImeLogger.sDBG; private static final float DEBUG_INFO_TEXT_SIZE_IN_DIP = 6.0f; private final ViewGroup mSuggestionsStrip; + private final ImageButton mVoiceKey; private final ViewGroup mAddToDictionaryStrip; private final View mImportantNoticeStrip; MainKeyboardView mMainKeyboardView; @@ -73,9 +75,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final MoreSuggestionsView mMoreSuggestionsView; private final MoreSuggestions.Builder mMoreSuggestionsBuilder; - private final ArrayList<TextView> mWordViews = CollectionUtils.newArrayList(); - private final ArrayList<TextView> mDebugInfoViews = CollectionUtils.newArrayList(); - private final ArrayList<View> mDividerViews = CollectionUtils.newArrayList(); + private final ArrayList<TextView> mWordViews = new ArrayList<>(); + private final ArrayList<TextView> mDebugInfoViews = new ArrayList<>(); + private final ArrayList<View> mDividerViews = new ArrayList<>(); Listener mListener; private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; @@ -85,12 +87,15 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final StripVisibilityGroup mStripVisibilityGroup; private static class StripVisibilityGroup { + private final View mSuggestionStripView; private final View mSuggestionsStrip; private final View mAddToDictionaryStrip; private final View mImportantNoticeStrip; - public StripVisibilityGroup(final View suggestionsStrip, final View addToDictionaryStrip, + public StripVisibilityGroup(final View suggestionStripView, + final ViewGroup suggestionsStrip, final ViewGroup addToDictionaryStrip, final View importantNoticeStrip) { + mSuggestionStripView = suggestionStripView; mSuggestionsStrip = suggestionsStrip; mAddToDictionaryStrip = addToDictionaryStrip; mImportantNoticeStrip = importantNoticeStrip; @@ -100,6 +105,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void setLayoutDirection(final boolean isRtlLanguage) { final int layoutDirection = isRtlLanguage ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR; + ViewCompat.setLayoutDirection(mSuggestionStripView, layoutDirection); ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection); ViewCompat.setLayoutDirection(mAddToDictionaryStrip, layoutDirection); ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection); @@ -145,10 +151,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick inflater.inflate(R.layout.suggestions_strip, this); mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip); + mVoiceKey = (ImageButton)findViewById(R.id.suggestions_strip_voice_key); mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip); mImportantNoticeStrip = findViewById(R.id.important_notice_strip); - mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mAddToDictionaryStrip, - mImportantNoticeStrip); + mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip, + mAddToDictionaryStrip, mImportantNoticeStrip); for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) { final TextView word = new TextView(context, null, R.attr.suggestionWordStyle); @@ -156,7 +163,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick word.setOnLongClickListener(this); mWordViews.add(word); final View divider = inflater.inflate(R.layout.suggestion_divider, null); - divider.setOnClickListener(this); mDividerViews.add(divider); final TextView info = new TextView(context, null, R.attr.suggestionWordStyle); info.setTextColor(Color.WHITE); @@ -177,6 +183,13 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick R.dimen.config_more_suggestions_modal_tolerance); mMoreSuggestionsSlidingDetector = new GestureDetector( context, mMoreSuggestionsSlidingListener); + + final TypedArray keyboardAttr = context.obtainStyledAttributes(attrs, + R.styleable.Keyboard, defStyle, R.style.SuggestionStripView); + final Drawable iconVoice = keyboardAttr.getDrawable(R.styleable.Keyboard_iconShortcutKey); + keyboardAttr.recycle(); + mVoiceKey.setImageDrawable(iconVoice); + mVoiceKey.setOnClickListener(this); } /** @@ -188,15 +201,19 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mMainKeyboardView = (MainKeyboardView)inputView.findViewById(R.id.keyboard_view); } + public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) { + final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE); + setVisibility(visibility); + final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); + mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE); + } + public void setSuggestions(final SuggestedWords suggestedWords, final boolean isRtlLanguage) { clear(); mStripVisibilityGroup.setLayoutDirection(isRtlLanguage); mSuggestedWords = suggestedWords; mSuggestionsCountInStrip = mLayoutHelper.layoutAndReturnSuggestionCountInStrip( mSuggestedWords, mSuggestionsStrip, this); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords); - } mStripVisibilityGroup.showSuggestionsStrip(); } @@ -209,7 +226,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } public void showAddToDictionaryHint(final String word) { - mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, getWidth()); + mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip); // {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word // will be extracted at {@link #onClick(View)}. mAddToDictionaryStrip.setTag(word); @@ -228,8 +245,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // This method checks if we should show the important notice (checks on permanent storage if // it has been shown once already or not, and if in the setup wizard). If applicable, it shows // the notice. In all cases, it returns true if it was shown, false otherwise. - public boolean maybeShowImportantNoticeTitle(final InputAttributes inputAttributes) { - if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), inputAttributes)) { + public boolean maybeShowImportantNoticeTitle() { + if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext())) { return false; } if (getWidth() <= 0) { @@ -411,10 +428,18 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick @Override public void onClick(final View view) { + AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback( + Constants.CODE_UNSPECIFIED, this); if (view == mImportantNoticeStrip) { mListener.showImportantNoticeContents(); return; } + if (view == mVoiceKey) { + mListener.onCodeInput(Constants.CODE_SHORTCUT, + Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, + false /* isKeyRepeat */); + return; + } final Object tag = view.getTag(); // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}. if (tag instanceof String) { @@ -448,7 +473,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // Called by the framework when the size is known. Show the important notice if applicable. // This may be overriden by showing suggestions later, if applicable. if (oldw <= 0 && w > 0) { - maybeShowImportantNoticeTitle(Settings.getInstance().getCurrent().mInputAttributes); + maybeShowImportantNoticeTitle(); } } } diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index 21426d1eb..eda81940f 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -167,7 +167,9 @@ public class UserDictionaryAddWordContents { // should not insert, because either A. the word exists with no shortcut, in which // case the exact same thing we want to insert is already there, or B. the word // exists with at least one shortcut, in which case it has priority on our word. - if (hasWord(newWord, context)) return CODE_ALREADY_PRESENT; + if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) { + return CODE_ALREADY_PRESENT; + } // Disallow duplicates. If the same word with no shortcut is defined, remove it; if // the same word with the same shortcut is defined, remove it; but we don't mind if @@ -256,7 +258,7 @@ public class UserDictionaryAddWordContents { // The system locale should be inside. We want it at the 2nd spot. locales.remove(systemLocale); // system locale may not be null locales.remove(""); // Remove the empty string if it's there - final ArrayList<LocaleRenderer> localesList = new ArrayList<LocaleRenderer>(); + final ArrayList<LocaleRenderer> localesList = new ArrayList<>(); // Add the passed locale, then the system locale at the top of the list. Add an // "all languages" entry at the bottom of the list. addLocaleDisplayNameToList(activity, localesList, mLocale); diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java index 4fc132f68..163443036 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java @@ -134,8 +134,8 @@ public class UserDictionaryAddWordFragment extends Fragment final Spinner localeSpinner = (Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale); - final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<LocaleRenderer>(getActivity(), - android.R.layout.simple_spinner_item, localesList); + final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<>( + getActivity(), android.R.layout.simple_spinner_item, localesList); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); localeSpinner.setAdapter(adapter); localeSpinner.setOnItemSelectedListener(this); diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java index 97a924d7b..624783a70 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java @@ -56,7 +56,7 @@ public class UserDictionaryList extends PreferenceFragment { final Cursor cursor = activity.getContentResolver().query(UserDictionary.Words.CONTENT_URI, new String[] { UserDictionary.Words.LOCALE }, null, null, null); - final TreeSet<String> localeSet = new TreeSet<String>(); + final TreeSet<String> localeSet = new TreeSet<>(); if (null == cursor) { // The user dictionary service is not present or disabled. Return null. return null; diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 2bb30a2ba..3ca7c7e1c 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -95,8 +95,7 @@ public final class AdditionalSubtypeUtils { return EMPTY_SUBTYPE_ARRAY; } final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); - final ArrayList<InputMethodSubtype> subtypesList = - CollectionUtils.newArrayList(prefSubtypeArray.length); + final ArrayList<InputMethodSubtype> subtypesList = new ArrayList<>(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java index 22b9b77d2..34ee2152a 100644 --- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java @@ -16,12 +16,11 @@ package com.android.inputmethod.latin.utils; -import com.android.inputmethod.latin.BinaryDictionary; +import android.util.Log; + import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import android.util.Log; - public final class AutoCorrectionUtils { private static final boolean DBG = LatinImeLogger.sDBG; private static final String TAG = AutoCorrectionUtils.class.getSimpleName(); @@ -36,7 +35,9 @@ public final class AutoCorrectionUtils { final float autoCorrectionThreshold) { if (null != suggestion) { // Shortlist a whitelisted word - if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true; + if (suggestion.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { + return true; + } final int autoCorrectionSuggestionScore = suggestion.mScore; // TODO: when the normalized score of the first suggestion is nearly equals to // the normalized score of the second suggestion, behave less aggressive. diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index 702688f93..936219332 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -62,6 +62,22 @@ public final class CapsModeUtils { } /** + * Helper method to find out if a code point is starting punctuation. + * + * This include the Unicode START_PUNCTUATION category, but also some other symbols that are + * starting, like the inverted question mark or the double quote. + * + * @param codePoint the code point + * @return true if it's starting punctuation, false otherwise. + */ + private static boolean isStartPunctuation(final int codePoint) { + return (codePoint == Constants.CODE_DOUBLE_QUOTE || codePoint == Constants.CODE_SINGLE_QUOTE + || codePoint == Constants.CODE_INVERTED_QUESTION_MARK + || codePoint == Constants.CODE_INVERTED_EXCLAMATION_MARK + || Character.getType(codePoint) == Character.START_PUNCTUATION); + } + + /** * Determine what caps mode should be in effect at the current offset in * the text. Only the mode bits set in <var>reqModes</var> will be * checked. Note that the caps mode flags here are explicitly defined @@ -115,8 +131,7 @@ public final class CapsModeUtils { } else { for (i = cs.length(); i > 0; i--) { final char c = cs.charAt(i - 1); - if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE - && Character.getType(c) != Character.START_PUNCTUATION) { + if (!isStartPunctuation(c)) { break; } } @@ -210,11 +225,14 @@ public final class CapsModeUtils { // We found out that we have a period. We need to determine if this is a full stop or // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation - // looks like (\w\.){2,} + // looks like (\w\.){2,}. Moreover, in German, you put periods after digits for dates + // and some other things, and in German specifically we need to not go into autocaps after + // a whitespace-digits-period sequence. // To find out, we will have a simple state machine with the following states : - // START, WORD, PERIOD, ABBREVIATION + // START, WORD, PERIOD, ABBREVIATION, NUMBER // On START : (just before the first period) // letter => WORD + // digit => NUMBER if German; end with caps otherwise // whitespace => end with no caps (it was a stand-alone period) // otherwise => end with caps (several periods/symbols in a row) // On WORD : (within the word just before the first period) @@ -228,6 +246,11 @@ public final class CapsModeUtils { // letter => LETTER // period => PERIOD // otherwise => end with no caps (it was an abbreviation) + // On NUMBER : (period immediately preceded by one or more digits) + // digit => NUMBER + // letter => LETTER (promote to word) + // otherwise => end with no caps (it was a whitespace-digits-period sequence, + // or a punctuation-digits-period sequence like "11.11.") // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This // should capitalize. @@ -235,6 +258,7 @@ public final class CapsModeUtils { final int WORD = 1; final int PERIOD = 2; final int LETTER = 3; + final int NUMBER = 4; final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES) & reqModes; final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes; @@ -247,6 +271,8 @@ public final class CapsModeUtils { state = WORD; } else if (Character.isWhitespace(c)) { return noCaps; + } else if (Character.isDigit(c) && spacingAndPunctuations.mUsesGermanRules) { + state = NUMBER; } else { return caps; } @@ -275,6 +301,15 @@ public final class CapsModeUtils { } else { return noCaps; } + break; + case NUMBER: + if (Character.isLetter(c)) { + state = WORD; + } else if (Character.isDigit(c)) { + state = NUMBER; + } else { + return noCaps; + } } } // Here we arrived at the start of the line. This should behave exactly like whitespace. diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java index bbfa0f091..e3aef29ba 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java @@ -16,93 +16,21 @@ package com.android.inputmethod.latin.utils; -import android.util.SparseArray; - -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; import java.util.Map; import java.util.TreeMap; -import java.util.TreeSet; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; public final class CollectionUtils { private CollectionUtils() { // This utility class is not publicly instantiable. } - public static <K,V> HashMap<K,V> newHashMap() { - return new HashMap<K,V>(); - } - - public static <K, V> WeakHashMap<K, V> newWeakHashMap() { - return new WeakHashMap<K, V>(); - } - - public static <K,V> TreeMap<K,V> newTreeMap() { - return new TreeMap<K,V>(); - } - public static <K, V> Map<K,V> newSynchronizedTreeMap() { - final TreeMap<K,V> treeMap = newTreeMap(); + final TreeMap<K,V> treeMap = new TreeMap<>(); return Collections.synchronizedMap(treeMap); } - public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() { - return new ConcurrentHashMap<K,V>(); - } - - public static <E> HashSet<E> newHashSet() { - return new HashSet<E>(); - } - - public static <E> TreeSet<E> newTreeSet() { - return new TreeSet<E>(); - } - - public static <E> ArrayList<E> newArrayList() { - return new ArrayList<E>(); - } - - public static <E> ArrayList<E> newArrayList(final int initialCapacity) { - return new ArrayList<E>(initialCapacity); - } - - public static <E> ArrayList<E> newArrayList(final Collection<E> collection) { - return new ArrayList<E>(collection); - } - - public static <E> LinkedList<E> newLinkedList() { - return new LinkedList<E>(); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() { - return new CopyOnWriteArrayList<E>(); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList( - final Collection<E> collection) { - return new CopyOnWriteArrayList<E>(collection); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) { - return new CopyOnWriteArrayList<E>(array); - } - - public static <E> ArrayDeque<E> newArrayDeque() { - return new ArrayDeque<E>(); - } - - public static <E> SparseArray<E> newSparseArray() { - return new SparseArray<E>(); - } - public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) { if (array == null) { throw new NullPointerException(); @@ -111,7 +39,7 @@ public final class CollectionUtils { throw new IllegalArgumentException(); } - final ArrayList<E> list = newArrayList(end - start); + final ArrayList<E> list = new ArrayList<>(end - start); for (int i = start; i < end; i++) { list.add(array[i]); } diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java index b18a1d83b..a21a1373b 100644 --- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java @@ -209,7 +209,7 @@ public final class CsvUtils { @UsedForTesting public static String[] split(final int splitFlags, final String line) throws CsvParseException { final boolean trimSpaces = (splitFlags & SPLIT_FLAGS_TRIM_SPACES) != 0; - final ArrayList<String> fields = CollectionUtils.newArrayList(); + final ArrayList<String> fields = new ArrayList<>(); final int length = line.length(); int start = 0; do { diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 315913e2f..d76ea10c0 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -336,7 +336,7 @@ public class DictionaryInfoUtils { public static ArrayList<DictionaryInfo> getCurrentDictionaryFileNameAndVersionInfo( final Context context) { - final ArrayList<DictionaryInfo> dictList = CollectionUtils.newArrayList(); + final ArrayList<DictionaryInfo> dictList = new ArrayList<>(); // Retrieve downloaded dictionaries final File[] directoryList = getCachedDirectoryList(context); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java index f2a1e524d..787e4a59d 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java @@ -16,33 +16,43 @@ package com.android.inputmethod.latin.utils; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.Suggest; +import java.util.List; +import java.util.Locale; -/** - * This class is used to prevent distracters/misspellings being added to personalization - * or user history dictionaries - */ -public class DistracterFilter { - private final Suggest mSuggest; - private final Keyboard mKeyboard; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.PrevWordsInfo; +public interface DistracterFilter { /** - * Create a DistracterFilter instance. + * Determine whether a word is a distracter to words in dictionaries. * - * @param suggest an instance of Suggest which will be used to obtain a list of suggestions - * for a potential distracter/misspelling - * @param keyboard the keyboard that is currently being used. This information is needed - * when calling mSuggest.getSuggestedWords(...) to obtain a list of suggestions. + * @param prevWordsInfo the information of previous words. + * @param testedWord the word that will be tested to see whether it is a distracter to words + * in dictionaries. + * @param locale the locale of word. + * @return true if testedWord is a distracter, otherwise false. */ - public DistracterFilter(final Suggest suggest, final Keyboard keyboard) { - mSuggest = suggest; - mKeyboard = keyboard; - } - - public boolean isDistractorToWordsInDictionaries(final String prevWord, - final String targetWord) { - // TODO: to be implemented - return false; - } + public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, + final String testedWord, final Locale locale); + + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes); + + public void close(); + + public static final DistracterFilter EMPTY_DISTRACTER_FILTER = new DistracterFilter() { + @Override + public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo, + String testedWord, Locale locale) { + return false; + } + + @Override + public void close() { + } + + @Override + public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) { + } + }; } diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java new file mode 100644 index 000000000..0ee6236b1 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java @@ -0,0 +1,129 @@ +/* + * 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.utils; + +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +import android.content.Context; +import android.util.Log; +import android.util.LruCache; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.PrevWordsInfo; + +/** + * This class is used to prevent distracters being added to personalization + * or user history dictionaries + */ +public class DistracterFilterCheckingExactMatches implements DistracterFilter { + private static final String TAG = DistracterFilterCheckingExactMatches.class.getSimpleName(); + private static final boolean DEBUG = false; + + private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; + private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; + + private final Context mContext; + private final DictionaryFacilitator mDictionaryFacilitator; + private final LruCache<String, Boolean> mDistractersCache; + private final Object mLock = new Object(); + + /** + * Create a DistracterFilter instance. + * + * @param context the context. + */ + public DistracterFilterCheckingExactMatches(final Context context) { + mContext = context; + mDictionaryFacilitator = new DictionaryFacilitator(); + mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); + } + + @Override + public void close() { + mDictionaryFacilitator.closeDictionaries(); + } + + @Override + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { + } + + private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { + mDictionaryFacilitator.resetDictionaries(mContext, newlocale, + false /* useContactsDict */, false /* usePersonalizedDicts */, + false /* forceReloadMainDictionary */, null /* listener */); + mDictionaryFacilitator.waitForLoadingMainDictionary( + TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); + } + + /** + * Determine whether a word is a distracter to words in dictionaries. + * + * @param prevWordsInfo the information of previous words. Not used for now. + * @param testedWord the word that will be tested to see whether it is a distracter to words + * in dictionaries. + * @param locale the locale of word. + * @return true if testedWord is a distracter, otherwise false. + */ + @Override + public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, + final String testedWord, final Locale locale) { + if (locale == null) { + return false; + } + if (!locale.equals(mDictionaryFacilitator.getLocale())) { + synchronized (mLock) { + // Reset dictionaries for the locale. + try { + mDistractersCache.evictAll(); + loadDictionariesForLocale(locale); + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", + e); + return false; + } + } + } + + final Boolean isCachedDistracter = mDistractersCache.get(testedWord); + if (isCachedDistracter != null && isCachedDistracter) { + if (DEBUG) { + Log.d(TAG, "testedWord: " + testedWord); + Log.d(TAG, "isDistracter: true (cache hit)"); + } + return true; + } + // The tested word is a distracter when there is a word that is exact matched to the tested + // word and its probability is higher than the tested word's probability. + final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final boolean isDistracter = perfectMatchFreq < exactMatchFreq; + if (DEBUG) { + Log.d(TAG, "testedWord: " + testedWord); + Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); + Log.d(TAG, "exactMatchFreq: " + exactMatchFreq); + Log.d(TAG, "isDistracter: " + isDistracter); + } + if (isDistracter) { + // Add the word to the cache. + mDistractersCache.put(testedWord, Boolean.TRUE); + } + return isDistracter; + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java new file mode 100644 index 000000000..4ad4ba784 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java @@ -0,0 +1,59 @@ +/* + * 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.utils; + +import java.util.List; +import java.util.Locale; + +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.PrevWordsInfo; + +public class DistracterFilterCheckingIsInDictionary implements DistracterFilter { + private final DistracterFilter mDistracterFilter; + private final Dictionary mDictionary; + + public DistracterFilterCheckingIsInDictionary(final DistracterFilter distracterFilter, + final Dictionary dictionary) { + mDistracterFilter = distracterFilter; + mDictionary = dictionary; + } + + @Override + public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo, + String testedWord, Locale locale) { + if (mDictionary.isInDictionary(testedWord)) { + // This filter treats entries that are already in the dictionary as non-distracters + // because they have passed the filtering in the past. + return false; + } else { + return mDistracterFilter.isDistracterToWordsInDictionaries( + prevWordsInfo, testedWord, locale); + } + } + + @Override + public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) { + // Do nothing. + } + + @Override + public void close() { + // Do nothing. + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java index ed502ed3d..61da1b789 100644 --- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java @@ -19,22 +19,42 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; /** * Utilities to manage executors. */ public class ExecutorUtils { - private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> - sExecutorMap = CollectionUtils.newConcurrentHashMap(); + private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = + new ConcurrentHashMap<>(); + + private static class ThreadFactoryWithId implements ThreadFactory { + private final String mId; + + public ThreadFactoryWithId(final String id) { + mId = id; + } + + @Override + public Thread newThread(final Runnable r) { + return new Thread(r, "Executor - " + mId); + } + } + /** - * Gets the executor for the given dictionary name. + * Gets the executor for the given id. */ - public static PrioritizedSerialExecutor getExecutor(final String dictName) { - PrioritizedSerialExecutor executor = sExecutorMap.get(dictName); + public static ExecutorService getExecutor(final String id) { + ExecutorService executor = sExecutorMap.get(id); if (executor == null) { synchronized(sExecutorMap) { - executor = new PrioritizedSerialExecutor(); - sExecutorMap.put(dictName, executor); + executor = sExecutorMap.get(id); + if (executor == null) { + executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id)); + sExecutorMap.put(id, executor); + } } } return executor; @@ -46,7 +66,7 @@ public class ExecutorUtils { @UsedForTesting public static void shutdownAllExecutors() { synchronized(sExecutorMap) { - for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) { + for (final ExecutorService executor : sExecutorMap.values()) { executor.execute(new Runnable() { @Override public void run() { diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java index ee2b97b2a..e300bd1d3 100644 --- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java @@ -26,12 +26,11 @@ import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragmen import com.android.inputmethod.latin.userdictionary.UserDictionaryList; import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker; import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; -import com.android.inputmethod.research.FeedbackFragment; import java.util.HashSet; public class FragmentUtils { - private static final HashSet<String> sLatinImeFragments = new HashSet<String>(); + private static final HashSet<String> sLatinImeFragments = new HashSet<>(); static { sLatinImeFragments.add(DictionarySettingsFragment.class.getName()); sLatinImeFragments.add(AboutPreferences.class.getName()); @@ -43,7 +42,6 @@ public class FragmentUtils { sLatinImeFragments.add(UserDictionaryList.class.getName()); sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName()); sLatinImeFragments.add(UserDictionarySettings.class.getName()); - sLatinImeFragments.add(FeedbackFragment.class.getName()); } public static boolean isValidFragment(String fragmentName) { diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java index 7d937a9d2..8b7077879 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -23,7 +23,6 @@ import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; public final class ImportantNoticeUtils { @@ -78,14 +77,7 @@ public final class ImportantNoticeUtils { return getCurrentImportantNoticeVersion(context) > lastVersion; } - public static boolean shouldShowImportantNotice(final Context context, - final InputAttributes inputAttributes) { - if (inputAttributes == null || inputAttributes.mIsPasswordField) { - return false; - } - if (isInSystemSetupWizard(context)) { - return false; - } + public static boolean shouldShowImportantNotice(final Context context) { if (!hasNewImportantNotice(context)) { return false; } @@ -93,6 +85,9 @@ public final class ImportantNoticeUtils { if (TextUtils.isEmpty(importantNoticeTitle)) { return false; } + if (isInSystemSetupWizard(context)) { + return false; + } return true; } diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java index 764ef72ce..6dd8d9711 100644 --- a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java @@ -37,7 +37,7 @@ public final class JsonUtils { private static final String EMPTY_STRING = ""; public static List<Object> jsonStrToList(final String s) { - final ArrayList<Object> list = CollectionUtils.newArrayList(); + final ArrayList<Object> list = new ArrayList<>(); final JsonReader reader = new JsonReader(new StringReader(s)); try { reader.beginArray(); diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java index 5ce977d5e..4248bebf6 100644 --- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java +++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java @@ -19,10 +19,12 @@ package com.android.inputmethod.latin.utils; import android.util.Log; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.util.ArrayList; +import java.util.List; import java.util.Locale; // Note: this class is used as a parameter type of a native method. You should be careful when you @@ -78,13 +80,13 @@ public final class LanguageModelParam { // Process a list of words and return a list of {@link LanguageModelParam} objects. public static ArrayList<LanguageModelParam> createLanguageModelParamsFrom( - final ArrayList<String> tokens, final int timestamp, - final DictionaryFacilitatorForSuggest dictionaryFacilitator, - final SpacingAndPunctuations spacingAndPunctuations) { - final ArrayList<LanguageModelParam> languageModelParams = - CollectionUtils.newArrayList(); + final List<String> tokens, final int timestamp, + final DictionaryFacilitator dictionaryFacilitator, + final SpacingAndPunctuations spacingAndPunctuations, + final DistracterFilter distracterFilter) { + final ArrayList<LanguageModelParam> languageModelParams = new ArrayList<>(); final int N = tokens.size(); - String prevWord = null; + PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; for (int i = 0; i < N; ++i) { final String tempWord = tokens.get(i); if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) { @@ -101,7 +103,7 @@ public final class LanguageModelParam { + tempWord + "\""); } // Sentence terminator found. Split. - prevWord = null; + prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; continue; } if (DEBUG_TOKEN) { @@ -109,56 +111,63 @@ public final class LanguageModelParam { } final LanguageModelParam languageModelParam = detectWhetherVaildWordOrNotAndGetLanguageModelParam( - prevWord, tempWord, timestamp, dictionaryFacilitator); + prevWordsInfo, tempWord, timestamp, dictionaryFacilitator, + distracterFilter); if (languageModelParam == null) { continue; } languageModelParams.add(languageModelParam); - prevWord = languageModelParam.mTargetWord; + prevWordsInfo = new PrevWordsInfo(languageModelParam.mTargetWord); } return languageModelParams; } private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam( - final String prevWord, final String targetWord, final int timestamp, - final DictionaryFacilitatorForSuggest dictionaryFacilitator) { + final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp, + final DictionaryFacilitator dictionaryFacilitator, + final DistracterFilter distracterFilter) { final Locale locale = dictionaryFacilitator.getLocale(); if (locale == null) { return null; } - if (!dictionaryFacilitator.isValidWord(targetWord, true /* ignoreCase */)) { - // OOV word. - return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, - false /* isValidWord */, locale); - } if (dictionaryFacilitator.isValidWord(targetWord, false /* ignoreCase */)) { - return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, - true /* isValidWord */, locale); + return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp, + true /* isValidWord */, locale, distracterFilter); } + final String lowerCaseTargetWord = targetWord.toLowerCase(locale); if (dictionaryFacilitator.isValidWord(lowerCaseTargetWord, false /* ignoreCase */)) { // Add the lower-cased word. - return createAndGetLanguageModelParamOfWord(prevWord, lowerCaseTargetWord, - timestamp, true /* isValidWord */, locale); + return createAndGetLanguageModelParamOfWord(prevWordsInfo, lowerCaseTargetWord, + timestamp, true /* isValidWord */, locale, distracterFilter); } + // Treat the word as an OOV word. - return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, - false /* isValidWord */, locale); + return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp, + false /* isValidWord */, locale, distracterFilter); } private static LanguageModelParam createAndGetLanguageModelParamOfWord( - final String prevWord, final String targetWord, final int timestamp, - final boolean isValidWord, final Locale locale) { + final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp, + final boolean isValidWord, final Locale locale, + final DistracterFilter distracterFilter) { final String word; if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST - && prevWord == null && !isValidWord) { + && prevWordsInfo.mPrevWord == null && !isValidWord) { word = targetWord.toLowerCase(locale); } else { word = targetWord; } + // Check whether the word is a distracter to words in the dictionaries. + if (distracterFilter.isDistracterToWordsInDictionaries(prevWordsInfo, word, locale)) { + if (DEBUG) { + Log.d(TAG, "The word (" + word + ") is a distracter. Skip this word."); + } + return null; + } final int unigramProbability = isValidWord ? UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD; - if (prevWord == null) { + if (prevWordsInfo.mPrevWord == null) { if (DEBUG) { Log.d(TAG, "--- add unigram: current(" + (isValidWord ? "Valid" : "OOV") + ") = " + word); @@ -166,12 +175,12 @@ public final class LanguageModelParam { return new LanguageModelParam(word, unigramProbability, timestamp); } if (DEBUG) { - Log.d(TAG, "--- add bigram: prev = " + prevWord + ", current(" + Log.d(TAG, "--- add bigram: prev = " + prevWordsInfo.mPrevWord + ", current(" + (isValidWord ? "Valid" : "OOV") + ") = " + word); } final int bigramProbability = isValidWord ? BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD; - return new LanguageModelParam(prevWord, word, unigramProbability, + return new LanguageModelParam(prevWordsInfo.mPrevWord, word, unigramProbability, bigramProbability, timestamp); } } diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java deleted file mode 100644 index d14ba508b..000000000 --- a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.text.TextUtils; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.WordComposer; - -public final class LatinImeLoggerUtils { - private LatinImeLoggerUtils() { - // This utility class is not publicly instantiable. - } - - public static void onNonSeparator(final char code, final int x, final int y) { - UserLogRingCharBuffer.getInstance().push(code, x, y); - LatinImeLogger.logOnInputChar(); - } - - public static void onSeparator(final int code, final int x, final int y) { - // Helper method to log a single code point separator - // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils - onSeparator(StringUtils.newSingleCodePointString(code), x, y); - } - - public static void onSeparator(final String separator, final int x, final int y) { - final int length = separator.length(); - for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { - int codePoint = Character.codePointAt(separator, i); - // TODO: accept code points - UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); - } - LatinImeLogger.logOnInputSeparator(); - } - - public static void onAutoCorrection(final String typedWord, final String correctedWord, - final String separatorString, final WordComposer wordComposer) { - final boolean isBatchMode = wordComposer.isBatchMode(); - if (!isBatchMode && TextUtils.isEmpty(typedWord)) { - return; - } - // TODO: this fails when the separator is more than 1 code point long, but - // the backend can't handle it yet. The only case when this happens is with - // smileys and other multi-character keys. - final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE - : separatorString.codePointAt(0); - if (!isBatchMode) { - LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); - } else { - if (!TextUtils.isEmpty(correctedWord)) { - // We must make sure that InputPointer contains only the relative timestamps, - // not actual timestamps. - LatinImeLogger.logOnAutoCorrectionForGeometric( - "", correctedWord, codePoint, wordComposer.getInputPointers()); - } - } - } - - public static void onAutoCorrectionCancellation() { - LatinImeLogger.logOnAutoCorrectionCancelled(); - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java index 8469c87b0..dd6fac671 100644 --- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java +++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java @@ -33,7 +33,7 @@ public class LeakGuardHandlerWrapper<T> extends Handler { if (ownerInstance == null) { throw new NullPointerException("ownerInstance is null"); } - mOwnerInstanceRef = new WeakReference<T>(ownerInstance); + mOwnerInstanceRef = new WeakReference<>(ownerInstance); } public T getOwnerInstance() { diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java index 0c55484b4..c519a0de6 100644 --- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java @@ -159,7 +159,7 @@ public final class LocaleUtils { return LOCALE_MATCH <= level; } - private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap(); + private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); /** * Creates a locale from a string specification. diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java deleted file mode 100644 index bf38abc95..000000000 --- a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import com.android.inputmethod.annotations.UsedForTesting; - -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * An object that executes submitted tasks using a thread. - */ -public class PrioritizedSerialExecutor { - public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName(); - - private final Object mLock = new Object(); - - private final Queue<Runnable> mTasks; - private final Queue<Runnable> mPrioritizedTasks; - private boolean mIsShutdown; - private final ThreadPoolExecutor mThreadPoolExecutor; - - // The task which is running now. - private Runnable mActive; - - public PrioritizedSerialExecutor() { - mTasks = new ConcurrentLinkedQueue<Runnable>(); - mPrioritizedTasks = new ConcurrentLinkedQueue<Runnable>(); - mIsShutdown = false; - mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, - 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1)); - } - - /** - * Enqueues the given task into the task queue. - * @param r the enqueued task - */ - public void execute(final Runnable r) { - synchronized(mLock) { - if (!mIsShutdown) { - mTasks.offer(new Runnable() { - @Override - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - } - } - - /** - * Enqueues the given task into the prioritized task queue. - * @param r the enqueued task - */ - @UsedForTesting - public void executePrioritized(final Runnable r) { - synchronized(mLock) { - if (!mIsShutdown) { - mPrioritizedTasks.offer(new Runnable() { - @Override - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - } - } - - private boolean fetchNextTasksLocked() { - mActive = mPrioritizedTasks.poll(); - if (mActive == null) { - mActive = mTasks.poll(); - } - return mActive != null; - } - - private void scheduleNext() { - synchronized(mLock) { - if (fetchNextTasksLocked()) { - mThreadPoolExecutor.execute(mActive); - } - } - } - - public void shutdown() { - synchronized(mLock) { - mIsShutdown = true; - mThreadPoolExecutor.shutdown(); - } - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java index 4521ec531..e3cac97f0 100644 --- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java +++ b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java @@ -62,18 +62,22 @@ public class RecapitalizeStatus { private Locale mLocale; private int[] mSortedSeparators; private String mStringAfter; - private boolean mIsActive; + private boolean mIsStarted; + private boolean mIsEnabled = true; private static final int[] EMPTY_STORTED_SEPARATORS = {}; public RecapitalizeStatus() { // By default, initialize with dummy values that won't match any real recapitalize. - initialize(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS); - deactivate(); + start(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS); + stop(); } - public void initialize(final int cursorStart, final int cursorEnd, final String string, + public void start(final int cursorStart, final int cursorEnd, final String string, final Locale locale, final int[] sortedSeparators) { + if (!mIsEnabled) { + return; + } mCursorStartBefore = cursorStart; mStringBefore = string; mCursorStartAfter = cursorStart; @@ -96,15 +100,27 @@ public class RecapitalizeStatus { mRotationStyleCurrentIndex = currentMode; mSkipOriginalMixedCaseMode = true; } - mIsActive = true; + mIsStarted = true; + } + + public void stop() { + mIsStarted = false; + } + + public boolean isStarted() { + return mIsStarted; + } + + public void enable() { + mIsEnabled = true; } - public void deactivate() { - mIsActive = false; + public void disable() { + mIsEnabled = false; } - public boolean isActive() { - return mIsActive; + public boolean mIsEnabled() { + return mIsEnabled; } public boolean isSetAt(final int cursorStart, final int cursorEnd) { diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index 49f4929b4..093c5a6c1 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -41,8 +41,7 @@ public final class ResourceUtils { // This utility class is not publicly instantiable. } - private static final HashMap<String, String> sDeviceOverrideValueMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String> sDeviceOverrideValueMap = new HashMap<>(); private static final String[] BUILD_KEYS_AND_VALUES = { "HARDWARE", Build.HARDWARE, @@ -54,8 +53,8 @@ public final class ResourceUtils { private static final String sBuildKeyValuesDebugString; static { - sBuildKeyValues = CollectionUtils.newHashMap(); - final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList(); + sBuildKeyValues = new HashMap<>(); + final ArrayList<String> keyValuePairs = new ArrayList<>(); final int keyCount = BUILD_KEYS_AND_VALUES.length / 2; for (int i = 0; i < keyCount; i++) { final int index = i * 2; diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java index a059f877b..79c19d077 100644 --- a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java @@ -17,37 +17,18 @@ package com.android.inputmethod.latin.utils; import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.util.Log; - -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.SettingsValues; public final class StatsUtils { - private static final String TAG = StatsUtils.class.getSimpleName(); - private static final StatsUtils sInstance = new StatsUtils(); - - public static void onCreateCompleted(final Context context) { - sInstance.onCreateCompletedInternal(context); + public static void init(final Context context) { } - private void onCreateCompletedInternal(final Context context) { - mContext = context; - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); - final Boolean usePersonalizedDict = - prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); - Log.d(TAG, "onCreateCompleted. context: " + context.toString() + "usePersonalizedDict: " - + usePersonalizedDict); + public static void onCreate(final SettingsValues settingsValues) { } - public static void onDestroy() { - sInstance.onDestroyInternal(); + public static void onLoadSettings(final SettingsValues settingsValues) { } - private void onDestroyInternal() { - Log.d(TAG, "onDestroy. context: " + mContext.toString()); - mContext = null; + public static void onDestroy() { } - - private Context mContext; } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index 374badc19..e4237a7f2 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.Locale; public final class StringUtils { - private static final String TAG = StringUtils.class.getSimpleName(); public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case public static final int CAPITALIZE_FIRST = 1; // First only public static final int CAPITALIZE_ALL = 2; // All caps @@ -110,7 +109,7 @@ public final class StringUtils { if (!containsInArray(text, elements)) { return extraValues; } - final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1); + final ArrayList<String> result = new ArrayList<>(elements.length - 1); for (final String element : elements) { if (!text.equals(element)) { result.add(element); @@ -316,24 +315,6 @@ public final class StringUtils { return true; } - /** - * Returns true if all code points in text are whitespace, false otherwise. Empty is true. - */ - // Interestingly enough, U+00A0 NO-BREAK SPACE and U+200B ZERO-WIDTH SPACE are not considered - // whitespace, while EN SPACE, EM SPACE and IDEOGRAPHIC SPACES are. - public static boolean containsOnlyWhitespace(final String text) { - final int length = text.length(); - int i = 0; - while (i < length) { - final int codePoint = text.codePointAt(i); - if (!Character.isWhitespace(codePoint)) { - return false; - } - i += Character.charCount(codePoint); - } - return true; - } - public static boolean isIdenticalAfterCapitalizeEachWord(final String text, final int[] sortedSeparators) { boolean needsCapsNext = true; @@ -538,6 +519,15 @@ public final class StringUtils { ? casedText.codePointAt(0) : CODE_UNSPECIFIED; } + public static int getTrailingSingleQuotesCount(final CharSequence charSequence) { + final int lastIndex = charSequence.length() - 1; + int i = lastIndex; + while (i >= 0 && charSequence.charAt(i) == Constants.CODE_SINGLE_QUOTE) { + --i; + } + return lastIndex - i; + } + @UsedForTesting public static class Stringizer<E> { public String stringize(final E element) { diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index b37779bdc..351d01400 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -49,17 +49,14 @@ public final class SubtypeLocaleUtils { private static Resources sResources; private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. - private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>(); // Keyboard layout to subtype name resource id map. - private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name resource id map. - private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name with layout resource id map. private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap = - CollectionUtils.newHashMap(); + new HashMap<>(); private static final String SUBTYPE_NAME_RESOURCE_PREFIX = "string/subtype_"; private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = @@ -71,7 +68,7 @@ public final class SubtypeLocaleUtils { // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap = - CollectionUtils.newHashMap(); + new HashMap<>(); private SubtypeLocaleUtils() { // Intentional empty constructor for utility class. @@ -324,4 +321,8 @@ public final class SubtypeLocaleUtils { public static boolean isRtlLanguage(final InputMethodSubtype subtype) { return isRtlLanguage(getSubtypeLocale(subtype)); } + + public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) { + return subtype.getExtraValueOf(Constants.Subtype.ExtraValue.COMBINING_RULES); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java index 42ea3c959..ab2b00e36 100644 --- a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java +++ b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java @@ -27,8 +27,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils; public final class TargetPackageInfoGetterTask extends AsyncTask<String, Void, PackageInfo> { private static final int MAX_CACHE_ENTRIES = 64; // arbitrary - private static final LruCache<String, PackageInfo> sCache = - new LruCache<String, PackageInfo>(MAX_CACHE_ENTRIES); + private static final LruCache<String, PackageInfo> sCache = new LruCache<>(MAX_CACHE_ENTRIES); public static PackageInfo getCachedPackageInfo(final String packageName) { if (null == packageName) return null; diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index 087a7f255..fafba79c2 100644 --- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -30,7 +30,7 @@ public final class TypefaceUtils { } // This sparse array caches key label text height in pixel indexed by key label text size. - private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray(); + private static final SparseArray<Float> sTextHeightCache = new SparseArray<>(); // Working variable for the following method. private static final Rect sTextHeightBounds = new Rect(); @@ -50,7 +50,7 @@ public final class TypefaceUtils { } // This sparse array caches key label text width in pixel indexed by key label text size. - private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray(); + private static final SparseArray<Float> sTextWidthCache = new SparseArray<>(); // Working variable for the following method. private static final Rect sTextWidthBounds = new Rect(); diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java deleted file mode 100644 index 06826dac0..000000000 --- a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2016 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.content.Intent; -import android.content.pm.PackageManager; -import android.inputmethodservice.InputMethodService; -import android.net.Uri; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.util.Log; -import android.view.MotionEvent; - -import com.android.inputmethod.latin.LatinImeLogger; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.channels.FileChannel; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -public final class UsabilityStudyLogUtils { - // TODO: remove code duplication with ResearchLog class - private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); - private static final String FILENAME = "log.txt"; - private final Handler mLoggingHandler; - private File mFile; - private File mDirectory; - private InputMethodService mIms; - private PrintWriter mWriter; - private final Date mDate; - private final SimpleDateFormat mDateFormat; - - private UsabilityStudyLogUtils() { - mDate = new Date(); - mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); - - HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mLoggingHandler = new Handler(handlerThread.getLooper()); - } - - // Initialization-on-demand holder - private static final class OnDemandInitializationHolder { - public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); - } - - public static UsabilityStudyLogUtils getInstance() { - return OnDemandInitializationHolder.sInstance; - } - - public void init(final InputMethodService ims) { - mIms = ims; - mDirectory = ims.getFilesDir(); - } - - private void createLogFileIfNotExist() { - if ((mFile == null || !mFile.exists()) - && (mDirectory != null && mDirectory.exists())) { - try { - mWriter = getPrintWriter(mDirectory, FILENAME, false); - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't create log file."); - } - } - } - - public static void writeBackSpace(final int x, final int y) { - UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); - } - - public static void writeChar(final char c, final int x, final int y) { - String inputChar = String.valueOf(c); - switch (c) { - case '\n': - inputChar = "<enter>"; - break; - case '\t': - inputChar = "<tab>"; - break; - case ' ': - inputChar = "<space>"; - break; - } - UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); - LatinImeLogger.onPrintAllUsabilityStudyLogs(); - } - - public static void writeMotionEvent(final MotionEvent me) { - final int action = me.getActionMasked(); - final long eventTime = me.getEventTime(); - final int pointerCount = me.getPointerCount(); - for (int index = 0; index < pointerCount; index++) { - final int id = me.getPointerId(index); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - - final String eventTag; - switch (action) { - case MotionEvent.ACTION_UP: - eventTag = "[Up]"; - break; - case MotionEvent.ACTION_DOWN: - eventTag = "[Down]"; - break; - case MotionEvent.ACTION_POINTER_UP: - eventTag = "[PointerUp]"; - break; - case MotionEvent.ACTION_POINTER_DOWN: - eventTag = "[PointerDown]"; - break; - case MotionEvent.ACTION_MOVE: - eventTag = "[Move]"; - break; - default: - eventTag = "[Action" + action + "]"; - break; - } - getInstance().write(eventTag + eventTime + "," + id + "," + x + "," + y + "," + size - + "," + pressure); - } - } - - public void write(final String log) { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - createLogFileIfNotExist(); - final long currentTime = System.currentTimeMillis(); - mDate.setTime(currentTime); - - final String printString = String.format(Locale.US, "%s\t%d\t%s\n", - mDateFormat.format(mDate), currentTime, log); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Write: " + log); - } - mWriter.print(printString); - } - }); - } - - private synchronized String getBufferedLogs() { - mWriter.flush(); - final StringBuilder sb = new StringBuilder(); - final BufferedReader br = getBufferedReader(); - String line; - try { - while ((line = br.readLine()) != null) { - sb.append('\n'); - sb.append(line); - } - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't read log file."); - } finally { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); - } - try { - br.close(); - } catch (final IOException e) { - // ignore. - } - } - return sb.toString(); - } - - public void emailResearcherLogsAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - final Date date = new Date(); - date.setTime(System.currentTimeMillis()); - final String currentDateTimeString = - new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); - if (mFile == null) { - Log.w(USABILITY_TAG, "No internal log file found."); - return; - } - if (mIms.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); - return; - } - mWriter.flush(); - final String destPath = Environment.getExternalStorageDirectory() - + "/research-" + currentDateTimeString + ".log"; - final File destFile = new File(destPath); - try { - final FileInputStream srcStream = new FileInputStream(mFile); - final FileOutputStream destStream = new FileOutputStream(destFile); - final FileChannel src = srcStream.getChannel(); - final FileChannel dest = destStream.getChannel(); - src.transferTo(0, src.size(), dest); - src.close(); - srcStream.close(); - dest.close(); - destStream.close(); - } catch (final FileNotFoundException e1) { - Log.w(USABILITY_TAG, e1); - return; - } catch (final IOException e2) { - Log.w(USABILITY_TAG, e2); - return; - } - if (!destFile.exists()) { - Log.w(USABILITY_TAG, "Dest file doesn't exist."); - return; - } - final Intent intent = new Intent(Intent.ACTION_SEND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); - } - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); - intent.putExtra(Intent.EXTRA_SUBJECT, - "[Research Logs] " + currentDateTimeString); - mIms.startActivity(intent); - } - }); - } - - public void printAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); - } - }); - } - - public void clearAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - if (mFile != null && mFile.exists()) { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Delete log file."); - } - mFile.delete(); - mWriter.close(); - } - } - }); - } - - private BufferedReader getBufferedReader() { - createLogFileIfNotExist(); - try { - return new BufferedReader(new FileReader(mFile)); - } catch (final FileNotFoundException e) { - return null; - } - } - - private PrintWriter getPrintWriter(final File dir, final String filename, - final boolean renew) throws IOException { - mFile = new File(dir, filename); - if (mFile.exists()) { - if (renew) { - mFile.delete(); - } - } - return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java deleted file mode 100644 index a75d353c9..000000000 --- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.inputmethodservice.InputMethodService; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.settings.Settings; - -public final class UserLogRingCharBuffer { - public /* for test */ static final int BUFSIZE = 20; - public /* for test */ int mLength = 0; - - private static UserLogRingCharBuffer sUserLogRingCharBuffer = new UserLogRingCharBuffer(); - private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; - private static final int INVALID_COORDINATE = -2; - private boolean mEnabled = false; - private int mEnd = 0; - private char[] mCharBuf = new char[BUFSIZE]; - private int[] mXBuf = new int[BUFSIZE]; - private int[] mYBuf = new int[BUFSIZE]; - - private UserLogRingCharBuffer() { - // Intentional empty constructor for singleton. - } - - @UsedForTesting - public static UserLogRingCharBuffer getInstance() { - return sUserLogRingCharBuffer; - } - - public static UserLogRingCharBuffer init(final InputMethodService context, - final boolean enabled, final boolean usabilityStudy) { - if (!(enabled || usabilityStudy)) { - return null; - } - sUserLogRingCharBuffer.mEnabled = true; - UsabilityStudyLogUtils.getInstance().init(context); - return sUserLogRingCharBuffer; - } - - private static int normalize(final int in) { - int ret = in % BUFSIZE; - return ret < 0 ? ret + BUFSIZE : ret; - } - - // TODO: accept code points - @UsedForTesting - public void push(final char c, final int x, final int y) { - if (!mEnabled) { - return; - } - if (LatinImeLogger.sUsabilityStudy) { - UsabilityStudyLogUtils.getInstance().writeChar(c, x, y); - } - mCharBuf[mEnd] = c; - mXBuf[mEnd] = x; - mYBuf[mEnd] = y; - mEnd = normalize(mEnd + 1); - if (mLength < BUFSIZE) { - ++mLength; - } - } - - public char pop() { - if (mLength < 1) { - return PLACEHOLDER_DELIMITER_CHAR; - } - mEnd = normalize(mEnd - 1); - --mLength; - return mCharBuf[mEnd]; - } - - public char getBackwardNthChar(final int n) { - if (mLength <= n || n < 0) { - return PLACEHOLDER_DELIMITER_CHAR; - } - return mCharBuf[normalize(mEnd - n - 1)]; - } - - public int getPreviousX(final char c, final int back) { - final int index = normalize(mEnd - 2 - back); - if (mLength <= back - || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { - return INVALID_COORDINATE; - } - return mXBuf[index]; - } - - public int getPreviousY(final char c, final int back) { - int index = normalize(mEnd - 2 - back); - if (mLength <= back - || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { - return INVALID_COORDINATE; - } - return mYBuf[index]; - } - - public String getLastWord(final int ignoreCharCount) { - final StringBuilder sb = new StringBuilder(); - int i = ignoreCharCount; - for (; i < mLength; ++i) { - final char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!Settings.getInstance().isWordSeparator(c)) { - break; - } - } - for (; i < mLength; ++i) { - char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!Settings.getInstance().isWordSeparator(c)) { - sb.append(c); - } else { - break; - } - } - return sb.reverse().toString(); - } - - public void reset() { - mLength = 0; - } -} diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java deleted file mode 100644 index 4f86526a7..000000000 --- a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -/** - * Arrange for the uploading service to be run on regular intervals. - */ -public final class BootBroadcastReceiver extends BroadcastReceiver { - @Override - public void onReceive(final Context context, final Intent intent) { - if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { - UploaderService.cancelAndRescheduleUploadingService(context, - true /* needsRescheduling */); - } - } -} diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java deleted file mode 100644 index 520b88d2f..000000000 --- a/java/src/com/android/inputmethod/research/FeedbackActivity.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.app.Activity; -import android.os.Bundle; - -import com.android.inputmethod.latin.R; - -public class FeedbackActivity extends Activity { - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.research_feedback_activity); - final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout); - layout.setActivity(this); - } - - @Override - public void onBackPressed() { - ResearchLogger.getInstance().onLeavingSendFeedbackDialog(); - super.onBackPressed(); - } -} diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java deleted file mode 100644 index 75fbbf1ba..000000000 --- a/java/src/com/android/inputmethod/research/FeedbackFragment.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.app.Fragment; -import android.os.Bundle; -import android.text.Editable; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Toast; - -import com.android.inputmethod.latin.R; - -public class FeedbackFragment extends Fragment implements OnClickListener { - private static final String TAG = FeedbackFragment.class.getSimpleName(); - - public static final String KEY_FEEDBACK_STRING = "FeedbackString"; - public static final String KEY_INCLUDE_ACCOUNT_NAME = "IncludeAccountName"; - public static final String KEY_HAS_USER_RECORDING = "HasRecording"; - - private EditText mEditText; - private CheckBox mIncludingAccountNameCheckBox; - private CheckBox mIncludingUserRecordingCheckBox; - private Button mSendButton; - private Button mCancelButton; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container, - false); - mEditText = (EditText) view.findViewById(R.id.research_feedback_contents); - mEditText.requestFocus(); - mIncludingAccountNameCheckBox = (CheckBox) view.findViewById( - R.id.research_feedback_include_account_name); - mIncludingUserRecordingCheckBox = (CheckBox) view.findViewById( - R.id.research_feedback_include_recording_checkbox); - mIncludingUserRecordingCheckBox.setOnClickListener(this); - - mSendButton = (Button) view.findViewById(R.id.research_feedback_send_button); - mSendButton.setOnClickListener(this); - mCancelButton = (Button) view.findViewById(R.id.research_feedback_cancel_button); - mCancelButton.setOnClickListener(this); - - if (savedInstanceState != null) { - restoreState(savedInstanceState); - } else { - final Bundle bundle = getActivity().getIntent().getExtras(); - if (bundle != null) { - restoreState(bundle); - } - } - return view; - } - - @Override - public void onClick(final View view) { - final ResearchLogger researchLogger = ResearchLogger.getInstance(); - if (view == mIncludingUserRecordingCheckBox) { - if (mIncludingUserRecordingCheckBox.isChecked()) { - final Bundle bundle = new Bundle(); - onSaveInstanceState(bundle); - - // Let the user make a recording - getActivity().finish(); - - researchLogger.setFeedbackDialogBundle(bundle); - researchLogger.onLeavingSendFeedbackDialog(); - researchLogger.startRecording(); - } - } else if (view == mSendButton) { - final Editable editable = mEditText.getText(); - final String feedbackContents = editable.toString(); - if (TextUtils.isEmpty(feedbackContents)) { - Toast.makeText(getActivity(), - R.string.research_feedback_empty_feedback_error_message, - Toast.LENGTH_LONG).show(); - } else { - final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked(); - researchLogger.sendFeedback(feedbackContents, false /* isIncludingHistory */, - isIncludingAccountName, mIncludingUserRecordingCheckBox.isChecked()); - getActivity().finish(); - researchLogger.setFeedbackDialogBundle(null); - researchLogger.onLeavingSendFeedbackDialog(); - } - } else if (view == mCancelButton) { - Log.d(TAG, "Finishing"); - getActivity().finish(); - researchLogger.setFeedbackDialogBundle(null); - researchLogger.onLeavingSendFeedbackDialog(); - } else { - Log.e(TAG, "Unknown view passed to FeedbackFragment.onClick()"); - } - } - - @Override - public void onSaveInstanceState(final Bundle bundle) { - final String savedFeedbackString = mEditText.getText().toString(); - - bundle.putString(KEY_FEEDBACK_STRING, savedFeedbackString); - bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, mIncludingAccountNameCheckBox.isChecked()); - bundle.putBoolean(KEY_HAS_USER_RECORDING, mIncludingUserRecordingCheckBox.isChecked()); - } - - private void restoreState(final Bundle bundle) { - mEditText.setText(bundle.getString(KEY_FEEDBACK_STRING)); - mIncludingAccountNameCheckBox.setChecked(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME)); - mIncludingUserRecordingCheckBox.setChecked(bundle.getBoolean(KEY_HAS_USER_RECORDING)); - } -} diff --git a/java/src/com/android/inputmethod/research/FeedbackLayout.java b/java/src/com/android/inputmethod/research/FeedbackLayout.java deleted file mode 100644 index d283d14b2..000000000 --- a/java/src/com/android/inputmethod/research/FeedbackLayout.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.app.Activity; -import android.content.Context; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.widget.LinearLayout; - -public class FeedbackLayout extends LinearLayout { - private Activity mActivity; - - public FeedbackLayout(Context context) { - super(context); - } - - public FeedbackLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public FeedbackLayout(Context context, AttributeSet attrs, int defstyle) { - super(context, attrs, defstyle); - } - - public void setActivity(Activity activity) { - mActivity = activity; - } - - @Override - public boolean dispatchKeyEventPreIme(KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - KeyEvent.DispatcherState state = getKeyDispatcherState(); - if (state != null) { - if (event.getAction() == KeyEvent.ACTION_DOWN - && event.getRepeatCount() == 0) { - state.startTracking(event, this); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP - && !event.isCanceled() && state.isTracking(event)) { - mActivity.onBackPressed(); - return true; - } - } - } - return super.dispatchKeyEventPreIme(event); - } -} diff --git a/java/src/com/android/inputmethod/research/FixedLogBuffer.java b/java/src/com/android/inputmethod/research/FixedLogBuffer.java deleted file mode 100644 index 8b64de8ae..000000000 --- a/java/src/com/android/inputmethod/research/FixedLogBuffer.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * A buffer that holds a fixed number of LogUnits. - * - * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are - * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches - * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to - * stay under the capacity limit. - * - * This variant of a LogBuffer has a limited memory footprint because of its limited size. This - * makes it useful, for example, for recording a window of the user's most recent actions in case - * they want to report an observed error that they do not know how to reproduce. - */ -public class FixedLogBuffer extends LogBuffer { - /* package for test */ int mWordCapacity; - // The number of members of mLogUnits that are actual words. - private int mNumActualWords; - - /** - * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and - * unlimited number of non-word LogUnits), and that outputs its result to a researchLog. - * - * @param wordCapacity maximum number of words - */ - public FixedLogBuffer(final int wordCapacity) { - super(); - if (wordCapacity <= 0) { - throw new IllegalArgumentException("wordCapacity must be 1 or greater."); - } - mWordCapacity = wordCapacity; - mNumActualWords = 0; - } - - /** - * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's - * (oldest first) if word capacity is reached. - */ - @Override - public void shiftIn(final LogUnit newLogUnit) { - if (!newLogUnit.hasOneOrMoreWords()) { - // This LogUnit doesn't contain any word, so it doesn't count toward the word-limit. - super.shiftIn(newLogUnit); - return; - } - final int numWordsIncoming = newLogUnit.getNumWords(); - if (mNumActualWords >= mWordCapacity) { - // Give subclass a chance to handle the buffer full condition by shifting out logUnits. - // TODO: Tell onBufferFull() how much space it needs to make to avoid forced eviction. - onBufferFull(); - // If still full, evict. - if (mNumActualWords >= mWordCapacity) { - shiftOutWords(numWordsIncoming); - } - } - super.shiftIn(newLogUnit); - mNumActualWords += numWordsIncoming; - } - - @Override - public LogUnit unshiftIn() { - final LogUnit logUnit = super.unshiftIn(); - if (logUnit != null && logUnit.hasOneOrMoreWords()) { - mNumActualWords -= logUnit.getNumWords(); - } - return logUnit; - } - - public int getNumWords() { - return mNumActualWords; - } - - /** - * Removes all LogUnits from the buffer without calling onShiftOut(). - */ - @Override - public void clear() { - super.clear(); - mNumActualWords = 0; - } - - /** - * Called when the buffer has just shifted in one more word than its maximum, and its about to - * shift out LogUnits to bring it back down to the maximum. - * - * Base class does nothing; subclasses may override if they want to record non-privacy sensitive - * events that fall off the end. - */ - protected void onBufferFull() { - } - - @Override - public LogUnit shiftOut() { - final LogUnit logUnit = super.shiftOut(); - if (logUnit != null && logUnit.hasOneOrMoreWords()) { - mNumActualWords -= logUnit.getNumWords(); - } - return logUnit; - } - - /** - * Remove LogUnits from the front of the LogBuffer until {@code numWords} have been removed. - * - * If there are less than {@code numWords} in the buffer, shifts out all {@code LogUnit}s. - * - * @param numWords the minimum number of words in {@link LogUnit}s to shift out - * @return the number of actual words LogUnit}s shifted out - */ - protected int shiftOutWords(final int numWords) { - int numWordsShiftedOut = 0; - do { - final LogUnit logUnit = shiftOut(); - if (logUnit == null) break; - numWordsShiftedOut += logUnit.getNumWords(); - } while (numWordsShiftedOut < numWords); - return numWordsShiftedOut; - } - - public void shiftOutAll() { - final LinkedList<LogUnit> logUnits = getLogUnits(); - while (!logUnits.isEmpty()) { - shiftOut(); - } - mNumActualWords = 0; - } - - /** - * Returns a list of {@link LogUnit}s at the front of the buffer that have words associated with - * them. - * - * There will be no more than {@code n} words in the returned list. So if 2 words are - * requested, and the first LogUnit has 3 words, it is not returned. If 2 words are requested, - * and the first LogUnit has only 1 word, and the next LogUnit 2 words, only the first LogUnit - * is returned. If the first LogUnit has no words associated with it, and the second LogUnit - * has three words, then only the first LogUnit (which has no associated words) is returned. If - * there are not enough LogUnits in the buffer to meet the word requirement, then all LogUnits - * will be returned. - * - * @param n The maximum number of {@link LogUnit}s with words to return. - * @return The list of the {@link LogUnit}s containing the first n words - */ - public ArrayList<LogUnit> peekAtFirstNWords(int n) { - final LinkedList<LogUnit> logUnits = getLogUnits(); - // Allocate space for n*2 logUnits. There will be at least n, one for each word, and - // there may be additional for punctuation, between-word commands, etc. This should be - // enough that reallocation won't be necessary. - final ArrayList<LogUnit> resultList = new ArrayList<LogUnit>(n * 2); - for (final LogUnit logUnit : logUnits) { - n -= logUnit.getNumWords(); - if (n < 0) break; - resultList.add(logUnit); - } - return resultList; - } -} diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java deleted file mode 100644 index 6170b4339..000000000 --- a/java/src/com/android/inputmethod/research/JsonUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.SharedPreferences; -import android.util.JsonWriter; -import android.view.MotionEvent; -import android.view.inputmethod.CompletionInfo; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; - -import java.io.IOException; -import java.util.Map; - -/** - * Routines for mapping classes and variables to JSON representations for logging. - */ -/* package */ class JsonUtils { - private JsonUtils() { - // This utility class is not publicly instantiable. - } - - /* package */ static void writeJson(final CompletionInfo[] ci, final JsonWriter jsonWriter) - throws IOException { - jsonWriter.beginArray(); - for (int j = 0; j < ci.length; j++) { - jsonWriter.value(ci[j].toString()); - } - jsonWriter.endArray(); - } - - /* package */ static void writeJson(final SharedPreferences prefs, final JsonWriter jsonWriter) - throws IOException { - jsonWriter.beginObject(); - for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) { - jsonWriter.name(entry.getKey()); - final Object innerValue = entry.getValue(); - if (innerValue == null) { - jsonWriter.nullValue(); - } else if (innerValue instanceof Boolean) { - jsonWriter.value((Boolean) innerValue); - } else if (innerValue instanceof Number) { - jsonWriter.value((Number) innerValue); - } else { - jsonWriter.value(innerValue.toString()); - } - } - jsonWriter.endObject(); - } - - /* package */ static void writeJson(final Key[] keys, final JsonWriter jsonWriter) - throws IOException { - jsonWriter.beginArray(); - for (Key key : keys) { - writeJson(key, jsonWriter); - } - jsonWriter.endArray(); - } - - private static void writeJson(final Key key, final JsonWriter jsonWriter) throws IOException { - jsonWriter.beginObject(); - jsonWriter.name("code").value(key.getCode()); - jsonWriter.name("altCode").value(key.getAltCode()); - jsonWriter.name("x").value(key.getX()); - jsonWriter.name("y").value(key.getY()); - jsonWriter.name("w").value(key.getWidth()); - jsonWriter.name("h").value(key.getHeight()); - jsonWriter.endObject(); - } - - /* package */ static void writeJson(final SuggestedWords words, final JsonWriter jsonWriter) - throws IOException { - jsonWriter.beginObject(); - jsonWriter.name("typedWordValid").value(words.mTypedWordValid); - jsonWriter.name("willAutoCorrect") - .value(words.mWillAutoCorrect); - jsonWriter.name("isPunctuationSuggestions") - .value(words.isPunctuationSuggestions()); - jsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions); - jsonWriter.name("isPrediction").value(words.mIsPrediction); - jsonWriter.name("suggestedWords"); - jsonWriter.beginArray(); - final int size = words.size(); - for (int j = 0; j < size; j++) { - final SuggestedWordInfo wordInfo = words.getInfo(j); - jsonWriter.beginObject(); - jsonWriter.name("word").value(wordInfo.toString()); - jsonWriter.name("score").value(wordInfo.mScore); - jsonWriter.name("kind").value(wordInfo.mKind); - jsonWriter.name("sourceDict").value(wordInfo.mSourceDict.mDictType); - jsonWriter.endObject(); - } - jsonWriter.endArray(); - jsonWriter.endObject(); - } - - /* package */ static void writeJson(final MotionEvent me, final JsonWriter jsonWriter) - throws IOException { - jsonWriter.beginObject(); - jsonWriter.name("pointerIds"); - jsonWriter.beginArray(); - final int pointerCount = me.getPointerCount(); - for (int index = 0; index < pointerCount; index++) { - jsonWriter.value(me.getPointerId(index)); - } - jsonWriter.endArray(); - - jsonWriter.name("xyt"); - jsonWriter.beginArray(); - final int historicalSize = me.getHistorySize(); - for (int index = 0; index < historicalSize; index++) { - jsonWriter.beginObject(); - jsonWriter.name("t"); - jsonWriter.value(me.getHistoricalEventTime(index)); - jsonWriter.name("d"); - jsonWriter.beginArray(); - for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { - jsonWriter.beginObject(); - jsonWriter.name("x"); - jsonWriter.value(me.getHistoricalX(pointerIndex, index)); - jsonWriter.name("y"); - jsonWriter.value(me.getHistoricalY(pointerIndex, index)); - jsonWriter.endObject(); - } - jsonWriter.endArray(); - jsonWriter.endObject(); - } - jsonWriter.beginObject(); - jsonWriter.name("t"); - jsonWriter.value(me.getEventTime()); - jsonWriter.name("d"); - jsonWriter.beginArray(); - for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { - jsonWriter.beginObject(); - jsonWriter.name("x"); - jsonWriter.value(me.getX(pointerIndex)); - jsonWriter.name("y"); - jsonWriter.value(me.getY(pointerIndex)); - jsonWriter.endObject(); - } - jsonWriter.endArray(); - jsonWriter.endObject(); - jsonWriter.endArray(); - jsonWriter.endObject(); - } -} diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java deleted file mode 100644 index b07b761f0..000000000 --- a/java/src/com/android/inputmethod/research/LogBuffer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import java.util.LinkedList; - -/** - * Maintain a FIFO queue of LogUnits. - * - * This class provides an unbounded queue. This is useful when the user is aware that their actions - * are being recorded, such as when they are trying to reproduce a bug. In this case, there should - * not be artificial restrictions on how many events that can be saved. - */ -public class LogBuffer { - // TODO: Gracefully handle situations in which this LogBuffer is consuming too much memory. - // This may happen, for example, if the user has forgotten that data is being logged. - private final LinkedList<LogUnit> mLogUnits; - - public LogBuffer() { - mLogUnits = new LinkedList<LogUnit>(); - } - - protected LinkedList<LogUnit> getLogUnits() { - return mLogUnits; - } - - public void clear() { - mLogUnits.clear(); - } - - public void shiftIn(final LogUnit logUnit) { - mLogUnits.add(logUnit); - } - - public LogUnit unshiftIn() { - if (mLogUnits.isEmpty()) { - return null; - } - return mLogUnits.removeLast(); - } - - public LogUnit peekLastLogUnit() { - if (mLogUnits.isEmpty()) { - return null; - } - return mLogUnits.peekLast(); - } - - public boolean isEmpty() { - return mLogUnits.isEmpty(); - } - - public LogUnit shiftOut() { - if (isEmpty()) { - return null; - } - return mLogUnits.removeFirst(); - } -} diff --git a/java/src/com/android/inputmethod/research/LogStatement.java b/java/src/com/android/inputmethod/research/LogStatement.java deleted file mode 100644 index 06b918af5..000000000 --- a/java/src/com/android/inputmethod/research/LogStatement.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.SharedPreferences; -import android.util.JsonWriter; -import android.util.Log; -import android.view.MotionEvent; -import android.view.inputmethod.CompletionInfo; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.IOException; - -/** - * A template for typed information stored in the logs. - * - * A LogStatement contains a name, keys, and flags about whether the {@code Object[] values} - * associated with the {@code String[] keys} are likely to reveal information about the user. The - * actual values are stored separately. - */ -public class LogStatement { - private static final String TAG = LogStatement.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - - // Constants for particular statements - public static final String TYPE_POINTER_TRACKER_CALL_LISTENER_ON_CODE_INPUT = - "PointerTrackerCallListenerOnCodeInput"; - public static final String KEY_CODE = "code"; - public static final String VALUE_RESEARCH = "research"; - public static final String TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS = - "MainKeyboardViewOnLongPress"; - public static final String ACTION = "action"; - public static final String VALUE_DOWN = "DOWN"; - public static final String TYPE_MOTION_EVENT = "MotionEvent"; - public static final String KEY_IS_LOGGING_RELATED = "isLoggingRelated"; - - // Keys for internal key/value pairs - private static final String CURRENT_TIME_KEY = "_ct"; - private static final String UPTIME_KEY = "_ut"; - private static final String EVENT_TYPE_KEY = "_ty"; - - // Name specifying the LogStatement type. - private final String mType; - - // mIsPotentiallyPrivate indicates that event contains potentially private information. If - // the word that this event is a part of is determined to be privacy-sensitive, then this - // event should not be included in the output log. The system waits to output until the - // containing word is known. - private final boolean mIsPotentiallyPrivate; - - // mIsPotentiallyRevealing indicates that this statement may disclose details about other - // words typed in other LogUnits. This can happen if the user is not inserting spaces, and - // data from Suggestions and/or Composing text reveals the entire "megaword". For example, - // say the user is typing "for the win", and the system wants to record the bigram "the - // win". If the user types "forthe", omitting the space, the system will give "for the" as - // a suggestion. If the user accepts the autocorrection, the suggestion for "for the" is - // included in the log for the word "the", disclosing that the previous word had been "for". - // For now, we simply do not include this data when logging part of a "megaword". - private final boolean mIsPotentiallyRevealing; - - // mKeys stores the names that are the attributes in the output json objects - private final String[] mKeys; - private static final String[] NULL_KEYS = new String[0]; - - LogStatement(final String name, final boolean isPotentiallyPrivate, - final boolean isPotentiallyRevealing, final String... keys) { - mType = name; - mIsPotentiallyPrivate = isPotentiallyPrivate; - mIsPotentiallyRevealing = isPotentiallyRevealing; - mKeys = (keys == null) ? NULL_KEYS : keys; - } - - public String getType() { - return mType; - } - - public boolean isPotentiallyPrivate() { - return mIsPotentiallyPrivate; - } - - public boolean isPotentiallyRevealing() { - return mIsPotentiallyRevealing; - } - - public String[] getKeys() { - return mKeys; - } - - /** - * Utility function to test whether a key-value pair exists in a LogStatement. - * - * A LogStatement is really just a template -- it does not contain the values, only the - * keys. So the values must be passed in as an argument. - * - * @param queryKey the String that is tested by {@code String.equals()} to the keys in the - * LogStatement - * @param queryValue an Object that must be {@code Object.equals()} to the key's corresponding - * value in the {@code values} array - * @param values the values corresponding to mKeys - * - * @returns {@true} if {@code queryKey} exists in the keys for this LogStatement, and {@code - * queryValue} matches the corresponding value in {@code values} - * - * @throws IllegalArgumentException if {@code values.length} is not equal to keys().length() - */ - public boolean containsKeyValuePair(final String queryKey, final Object queryValue, - final Object[] values) { - if (mKeys.length != values.length) { - throw new IllegalArgumentException("Mismatched number of keys and values."); - } - final int length = mKeys.length; - for (int i = 0; i < length; i++) { - if (mKeys[i].equals(queryKey) && values[i].equals(queryValue)) { - return true; - } - } - return false; - } - - /** - * Utility function to set a value in a LogStatement. - * - * A LogStatement is really just a template -- it does not contain the values, only the - * keys. So the values must be passed in as an argument. - * - * @param queryKey the String that is tested by {@code String.equals()} to the keys in the - * LogStatement - * @param values the array of values corresponding to mKeys - * @param newValue the replacement value to go into the {@code values} array - * - * @returns {@true} if the key exists and the value was successfully set, {@false} otherwise - * - * @throws IllegalArgumentException if {@code values.length} is not equal to keys().length() - */ - public boolean setValue(final String queryKey, final Object[] values, final Object newValue) { - if (mKeys.length != values.length) { - throw new IllegalArgumentException("Mismatched number of keys and values."); - } - final int length = mKeys.length; - for (int i = 0; i < length; i++) { - if (mKeys[i].equals(queryKey)) { - values[i] = newValue; - return true; - } - } - return false; - } - - /** - * Write the contents out through jsonWriter. - * - * The JsonWriter class must have already had {@code JsonWriter.beginArray} called on it. - * - * Note that this method is not thread safe for the same jsonWriter. Callers must ensure - * thread safety. - */ - public boolean outputToLocked(final JsonWriter jsonWriter, final Long time, - final Object... values) { - if (DEBUG) { - if (mKeys.length != values.length) { - Log.d(TAG, "Key and Value list sizes do not match. " + mType); - } - } - try { - jsonWriter.beginObject(); - jsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis()); - jsonWriter.name(UPTIME_KEY).value(time); - jsonWriter.name(EVENT_TYPE_KEY).value(mType); - final int length = values.length; - for (int i = 0; i < length; i++) { - jsonWriter.name(mKeys[i]); - final Object value = values[i]; - if (value instanceof CharSequence) { - jsonWriter.value(value.toString()); - } else if (value instanceof Number) { - jsonWriter.value((Number) value); - } else if (value instanceof Boolean) { - jsonWriter.value((Boolean) value); - } else if (value instanceof CompletionInfo[]) { - JsonUtils.writeJson((CompletionInfo[]) value, jsonWriter); - } else if (value instanceof SharedPreferences) { - JsonUtils.writeJson((SharedPreferences) value, jsonWriter); - } else if (value instanceof Key[]) { - JsonUtils.writeJson((Key[]) value, jsonWriter); - } else if (value instanceof SuggestedWords) { - JsonUtils.writeJson((SuggestedWords) value, jsonWriter); - } else if (value instanceof MotionEvent) { - JsonUtils.writeJson((MotionEvent) value, jsonWriter); - } else if (value == null) { - jsonWriter.nullValue(); - } else { - if (DEBUG) { - Log.w(TAG, "Unrecognized type to be logged: " - + (value == null ? "<null>" : value.getClass().getName())); - } - jsonWriter.nullValue(); - } - } - jsonWriter.endObject(); - } catch (IOException e) { - e.printStackTrace(); - Log.w(TAG, "Error in JsonWriter; skipping LogStatement"); - return false; - } - return true; - } -} diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java deleted file mode 100644 index 3366df12a..000000000 --- a/java/src/com/android/inputmethod/research/LogUnit.java +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.JsonWriter; -import android.util.Log; - -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Pattern; - -/** - * A group of log statements related to each other. - * - * A LogUnit is collection of LogStatements, each of which is generated by at a particular point - * in the code. (There is no LogStatement class; the data is stored across the instance variables - * here.) A single LogUnit's statements can correspond to all the calls made while in the same - * composing region, or all the calls between committing the last composing region, and the first - * character of the next composing region. - * - * Individual statements in a log may be marked as potentially private. If so, then they are only - * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit - * will not violate the user's privacy. Checks for this may include whether other LogUnits have - * been published recently, or whether the LogUnit contains numbers, etc. - */ -public class LogUnit { - private static final String TAG = LogUnit.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - - private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+"); - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - - private final ArrayList<LogStatement> mLogStatementList; - private final ArrayList<Object[]> mValuesList; - // Assume that mTimeList is sorted in increasing order. Do not insert null values into - // mTimeList. - private final ArrayList<Long> mTimeList; - // Words that this LogUnit generates. Should be null if the data in the LogUnit does not - // generate a genuine word (i.e. separators alone do not count as a word). Should never be - // empty. Note that if the user types spaces explicitly, then normally mWords should contain - // only a single word; it will only contain space-separate multiple words if the user does not - // enter a space, and the system enters one automatically. - private String mWords; - private String[] mWordArray = EMPTY_STRING_ARRAY; - private boolean mMayContainDigit; - private boolean mIsPartOfMegaword; - private boolean mContainsUserDeletions; - - // mCorrectionType indicates whether the word was corrected at all, and if so, the nature of the - // correction. - private int mCorrectionType; - // LogUnits start in this state. If a word is entered without being corrected, it will have - // this CorrectiontType. - public static final int CORRECTIONTYPE_NO_CORRECTION = 0; - // The LogUnit was corrected manually by the user in an unspecified way. - public static final int CORRECTIONTYPE_CORRECTION = 1; - // The LogUnit was corrected manually by the user to a word not in the list of suggestions of - // the first word typed here. (Note: this is a heuristic value, it may be incorrect, for - // example, if the user repositions the cursor). - public static final int CORRECTIONTYPE_DIFFERENT_WORD = 2; - // The LogUnit was corrected manually by the user to a word that was in the list of suggestions - // of the first word typed here. (Again, a heuristic). It is probably a typo correction. - public static final int CORRECTIONTYPE_TYPO = 3; - // TODO: Rather than just tracking the current state, keep a historical record of the LogUnit's - // state and statistics. This should include how many times it has been corrected, whether - // other LogUnit edits were done between edits to this LogUnit, etc. Also track when a LogUnit - // previously contained a word, but was corrected to empty (because it was deleted, and there is - // no known replacement). - - private SuggestedWords mSuggestedWords; - - public LogUnit() { - mLogStatementList = new ArrayList<LogStatement>(); - mValuesList = new ArrayList<Object[]>(); - mTimeList = new ArrayList<Long>(); - mIsPartOfMegaword = false; - mCorrectionType = CORRECTIONTYPE_NO_CORRECTION; - mSuggestedWords = null; - } - - private LogUnit(final ArrayList<LogStatement> logStatementList, - final ArrayList<Object[]> valuesList, - final ArrayList<Long> timeList, - final boolean isPartOfMegaword) { - mLogStatementList = logStatementList; - mValuesList = valuesList; - mTimeList = timeList; - mIsPartOfMegaword = isPartOfMegaword; - mCorrectionType = CORRECTIONTYPE_NO_CORRECTION; - mSuggestedWords = null; - } - - private static final Object[] NULL_VALUES = new Object[0]; - /** - * Adds a new log statement. The time parameter in successive calls to this method must be - * monotonically increasing, or splitByTime() will not work. - */ - public void addLogStatement(final LogStatement logStatement, final long time, - Object... values) { - if (values == null) { - values = NULL_VALUES; - } - mLogStatementList.add(logStatement); - mValuesList.add(values); - mTimeList.add(time); - } - - /** - * Publish the contents of this LogUnit to {@code researchLog}. - * - * For each publishable {@code LogStatement}, invoke {@link LogStatement#outputToLocked}. - * - * @param researchLog where to publish the contents of this {@code LogUnit} - * @param canIncludePrivateData whether the private data in this {@code LogUnit} should be - * included - * - * @throws IOException if publication to the log file is not possible - */ - public synchronized void publishTo(final ResearchLog researchLog, - final boolean canIncludePrivateData) throws IOException { - // Write out any logStatement that passes the privacy filter. - final int size = mLogStatementList.size(); - if (size != 0) { - // Note that jsonWriter is only set to a non-null value if the logUnit start text is - // output and at least one logStatement is output. - JsonWriter jsonWriter = researchLog.getInitializedJsonWriterLocked(); - outputLogUnitStart(jsonWriter, canIncludePrivateData); - for (int i = 0; i < size; i++) { - final LogStatement logStatement = mLogStatementList.get(i); - if (!canIncludePrivateData && logStatement.isPotentiallyPrivate()) { - continue; - } - if (mIsPartOfMegaword && logStatement.isPotentiallyRevealing()) { - continue; - } - logStatement.outputToLocked(jsonWriter, mTimeList.get(i), mValuesList.get(i)); - } - outputLogUnitStop(jsonWriter); - } - } - - private static final String WORD_KEY = "_wo"; - private static final String NUM_WORDS_KEY = "_nw"; - private static final String CORRECTION_TYPE_KEY = "_corType"; - private static final String LOG_UNIT_BEGIN_KEY = "logUnitStart"; - private static final String LOG_UNIT_END_KEY = "logUnitEnd"; - - final LogStatement LOGSTATEMENT_LOG_UNIT_BEGIN_WITH_PRIVATE_DATA = - new LogStatement(LOG_UNIT_BEGIN_KEY, false /* isPotentiallyPrivate */, - false /* isPotentiallyRevealing */, WORD_KEY, CORRECTION_TYPE_KEY, - NUM_WORDS_KEY); - final LogStatement LOGSTATEMENT_LOG_UNIT_BEGIN_WITHOUT_PRIVATE_DATA = - new LogStatement(LOG_UNIT_BEGIN_KEY, false /* isPotentiallyPrivate */, - false /* isPotentiallyRevealing */, NUM_WORDS_KEY); - private void outputLogUnitStart(final JsonWriter jsonWriter, - final boolean canIncludePrivateData) { - final LogStatement logStatement; - if (canIncludePrivateData) { - LOGSTATEMENT_LOG_UNIT_BEGIN_WITH_PRIVATE_DATA.outputToLocked(jsonWriter, - SystemClock.uptimeMillis(), getWordsAsString(), getCorrectionType(), - getNumWords()); - } else { - LOGSTATEMENT_LOG_UNIT_BEGIN_WITHOUT_PRIVATE_DATA.outputToLocked(jsonWriter, - SystemClock.uptimeMillis(), getNumWords()); - } - } - - final LogStatement LOGSTATEMENT_LOG_UNIT_END = - new LogStatement(LOG_UNIT_END_KEY, false /* isPotentiallyPrivate */, - false /* isPotentiallyRevealing */); - private void outputLogUnitStop(final JsonWriter jsonWriter) { - LOGSTATEMENT_LOG_UNIT_END.outputToLocked(jsonWriter, SystemClock.uptimeMillis()); - } - - /** - * Mark the current logUnit as containing data to generate {@code newWords}. - * - * If {@code setWord()} was previously called for this LogUnit, then the method will try to - * determine what kind of correction it is, and update its internal state of the correctionType - * accordingly. - * - * @param newWords The words this LogUnit generates. Caller should not pass null or the empty - * string. - */ - public void setWords(final String newWords) { - if (hasOneOrMoreWords()) { - // The word was already set once, and it is now being changed. See if the new word - // is close to the old word. If so, then the change is probably a typo correction. - // If not, the user may have decided to enter a different word, so flag it. - if (mSuggestedWords != null) { - if (isInSuggestedWords(newWords, mSuggestedWords)) { - mCorrectionType = CORRECTIONTYPE_TYPO; - } else { - mCorrectionType = CORRECTIONTYPE_DIFFERENT_WORD; - } - } else { - // No suggested words, so it's not clear whether it's a typo or different word. - // Mark it as a generic correction. - mCorrectionType = CORRECTIONTYPE_CORRECTION; - } - } else { - mCorrectionType = CORRECTIONTYPE_NO_CORRECTION; - } - mWords = newWords; - - // Update mWordArray - mWordArray = (TextUtils.isEmpty(mWords)) ? EMPTY_STRING_ARRAY - : WHITESPACE_PATTERN.split(mWords); - if (mWordArray.length > 0 && TextUtils.isEmpty(mWordArray[0])) { - // Empty string at beginning of array. Must have been whitespace at the start of the - // word. Remove the empty string. - mWordArray = Arrays.copyOfRange(mWordArray, 1, mWordArray.length); - } - } - - public String getWordsAsString() { - return mWords; - } - - /** - * Retuns the words generated by the data in this LogUnit. - * - * The first word may be an empty string, if the data in the LogUnit started by generating - * whitespace. - * - * @return the array of words. an empty list of there are no words associated with this LogUnit. - */ - public String[] getWordsAsStringArray() { - return mWordArray; - } - - public boolean hasOneOrMoreWords() { - return mWordArray.length >= 1; - } - - public int getNumWords() { - return mWordArray.length; - } - - // TODO: Refactor to eliminate getter/setters - public void setMayContainDigit() { - mMayContainDigit = true; - } - - // TODO: Refactor to eliminate getter/setters - public boolean mayContainDigit() { - return mMayContainDigit; - } - - // TODO: Refactor to eliminate getter/setters - public void setContainsUserDeletions() { - mContainsUserDeletions = true; - } - - // TODO: Refactor to eliminate getter/setters - public boolean containsUserDeletions() { - return mContainsUserDeletions; - } - - // TODO: Refactor to eliminate getter/setters - public void setCorrectionType(final int correctionType) { - mCorrectionType = correctionType; - } - - // TODO: Refactor to eliminate getter/setters - public int getCorrectionType() { - return mCorrectionType; - } - - public boolean isEmpty() { - return mLogStatementList.isEmpty(); - } - - /** - * Split this logUnit, with all events before maxTime staying in the current logUnit, and all - * events after maxTime going into a new LogUnit that is returned. - */ - public LogUnit splitByTime(final long maxTime) { - // Assume that mTimeList is in sorted order. - final int length = mTimeList.size(); - // TODO: find time by binary search, e.g. using Collections#binarySearch() - for (int index = 0; index < length; index++) { - if (mTimeList.get(index) > maxTime) { - final List<LogStatement> laterLogStatements = - mLogStatementList.subList(index, length); - final List<Object[]> laterValues = mValuesList.subList(index, length); - final List<Long> laterTimes = mTimeList.subList(index, length); - - // Create the LogUnit containing the later logStatements and associated data. - final LogUnit newLogUnit = new LogUnit( - new ArrayList<LogStatement>(laterLogStatements), - new ArrayList<Object[]>(laterValues), - new ArrayList<Long>(laterTimes), - true /* isPartOfMegaword */); - newLogUnit.mWords = null; - newLogUnit.mMayContainDigit = mMayContainDigit; - newLogUnit.mContainsUserDeletions = mContainsUserDeletions; - - // Purge the logStatements and associated data from this LogUnit. - laterLogStatements.clear(); - laterValues.clear(); - laterTimes.clear(); - mIsPartOfMegaword = true; - - return newLogUnit; - } - } - return new LogUnit(); - } - - public void append(final LogUnit logUnit) { - mLogStatementList.addAll(logUnit.mLogStatementList); - mValuesList.addAll(logUnit.mValuesList); - mTimeList.addAll(logUnit.mTimeList); - mWords = null; - if (logUnit.mWords != null) { - setWords(logUnit.mWords); - } - mMayContainDigit = mMayContainDigit || logUnit.mMayContainDigit; - mContainsUserDeletions = mContainsUserDeletions || logUnit.mContainsUserDeletions; - mIsPartOfMegaword = false; - } - - public SuggestedWords getSuggestions() { - return mSuggestedWords; - } - - /** - * Initialize the suggestions. - * - * Once set to a non-null value, the suggestions may not be changed again. This is to keep - * track of the list of words that are close to the user's initial effort to type the word. - * Only words that are close to the initial effort are considered typo corrections. - */ - public void initializeSuggestions(final SuggestedWords suggestedWords) { - if (mSuggestedWords == null) { - mSuggestedWords = suggestedWords; - } - } - - private static boolean isInSuggestedWords(final String queryWord, - final SuggestedWords suggestedWords) { - if (TextUtils.isEmpty(queryWord)) { - return false; - } - final int size = suggestedWords.size(); - for (int i = 0; i < size; i++) { - final SuggestedWordInfo wordInfo = suggestedWords.getInfo(i); - if (queryWord.equals(wordInfo.mWord)) { - return true; - } - } - return false; - } - - /** - * Remove data associated with selecting the Research button. - * - * A LogUnit will capture all user interactions with the IME, including the "meta-interactions" - * of using the Research button to control the logging (e.g. by starting and stopping recording - * of a test case). Because meta-interactions should not be part of the normal log, calling - * this method will set a field in the LogStatements of the motion events to indiciate that - * they should be disregarded. - * - * This implementation assumes that the data recorded by the meta-interaction takes the - * form of all events following the first MotionEvent.ACTION_DOWN before the first long-press - * before the last onCodeEvent containing a code matching {@code LogStatement.VALUE_RESEARCH}. - * - * @returns true if data was removed - */ - public boolean removeResearchButtonInvocation() { - // This method is designed to be idempotent. - - // First, find last invocation of "research" key - final int indexOfLastResearchKey = findLastIndexContainingKeyValue( - LogStatement.TYPE_POINTER_TRACKER_CALL_LISTENER_ON_CODE_INPUT, - LogStatement.KEY_CODE, LogStatement.VALUE_RESEARCH); - if (indexOfLastResearchKey < 0) { - // Could not find invocation of "research" key. Leave log as is. - if (DEBUG) { - Log.d(TAG, "Could not find research key"); - } - return false; - } - - // Look for the long press that started the invocation of the research key code input. - final int indexOfLastLongPressBeforeResearchKey = - findLastIndexBefore(LogStatement.TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS, - indexOfLastResearchKey); - - // Look for DOWN event preceding the long press - final int indexOfLastDownEventBeforeLongPress = - findLastIndexContainingKeyValueBefore(LogStatement.TYPE_MOTION_EVENT, - LogStatement.ACTION, LogStatement.VALUE_DOWN, - indexOfLastLongPressBeforeResearchKey); - - // Flag all LatinKeyboardViewProcessMotionEvents from the DOWN event to the research key as - // logging-related - final int startingIndex = indexOfLastDownEventBeforeLongPress == -1 ? 0 - : indexOfLastDownEventBeforeLongPress; - for (int index = startingIndex; index < indexOfLastResearchKey; index++) { - final LogStatement logStatement = mLogStatementList.get(index); - final String type = logStatement.getType(); - final Object[] values = mValuesList.get(index); - if (type.equals(LogStatement.TYPE_MOTION_EVENT)) { - logStatement.setValue(LogStatement.KEY_IS_LOGGING_RELATED, values, true); - } - } - return true; - } - - /** - * Find the index of the last LogStatement before {@code startingIndex} of type {@code type}. - * - * @param queryType a String that must be {@code String.equals()} to the LogStatement type - * @param startingIndex the index to start the backward search from. Must be less than the - * length of mLogStatementList, or an IndexOutOfBoundsException is thrown. Can be negative, - * in which case -1 is returned. - * - * @return The index of the last LogStatement, -1 if none exists. - */ - private int findLastIndexBefore(final String queryType, final int startingIndex) { - return findLastIndexContainingKeyValueBefore(queryType, null, null, startingIndex); - } - - /** - * Find the index of the last LogStatement before {@code startingIndex} of type {@code type} - * containing the given key-value pair. - * - * @param queryType a String that must be {@code String.equals()} to the LogStatement type - * @param queryKey a String that must be {@code String.equals()} to a key in the LogStatement - * @param queryValue an Object that must be {@code String.equals()} to the key's corresponding - * value - * - * @return The index of the last LogStatement, -1 if none exists. - */ - private int findLastIndexContainingKeyValue(final String queryType, final String queryKey, - final Object queryValue) { - return findLastIndexContainingKeyValueBefore(queryType, queryKey, queryValue, - mLogStatementList.size() - 1); - } - - /** - * Find the index of the last LogStatement before {@code startingIndex} of type {@code type} - * containing the given key-value pair. - * - * @param queryType a String that must be {@code String.equals()} to the LogStatement type - * @param queryKey a String that must be {@code String.equals()} to a key in the LogStatement - * @param queryValue an Object that must be {@code String.equals()} to the key's corresponding - * value - * @param startingIndex the index to start the backward search from. Must be less than the - * length of mLogStatementList, or an IndexOutOfBoundsException is thrown. Can be negative, - * in which case -1 is returned. - * - * @return The index of the last LogStatement, -1 if none exists. - */ - private int findLastIndexContainingKeyValueBefore(final String queryType, final String queryKey, - final Object queryValue, final int startingIndex) { - if (startingIndex < 0) { - return -1; - } - for (int index = startingIndex; index >= 0; index--) { - final LogStatement logStatement = mLogStatementList.get(index); - final String type = logStatement.getType(); - if (type.equals(queryType) && (queryKey == null - || logStatement.containsKeyValuePair(queryKey, queryValue, - mValuesList.get(index)))) { - return index; - } - } - return -1; - } -} diff --git a/java/src/com/android/inputmethod/research/LoggingUtils.java b/java/src/com/android/inputmethod/research/LoggingUtils.java deleted file mode 100644 index 1261d6780..000000000 --- a/java/src/com/android/inputmethod/research/LoggingUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.view.MotionEvent; - -/* package */ class LoggingUtils { - private LoggingUtils() { - // This utility class is not publicly instantiable. - } - - /* package */ static String getMotionEventActionTypeString(final int actionType) { - switch (actionType) { - case MotionEvent.ACTION_CANCEL: return "CANCEL"; - case MotionEvent.ACTION_UP: return "UP"; - case MotionEvent.ACTION_DOWN: return "DOWN"; - case MotionEvent.ACTION_POINTER_UP: return "POINTER_UP"; - case MotionEvent.ACTION_POINTER_DOWN: return "POINTER_DOWN"; - case MotionEvent.ACTION_MOVE: return "MOVE"; - case MotionEvent.ACTION_OUTSIDE: return "OUTSIDE"; - default: return "ACTION_" + actionType; - } - } -} diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java deleted file mode 100644 index ffdb43c15..000000000 --- a/java/src/com/android/inputmethod/research/MainLogBuffer.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.util.Log; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * MainLogBuffer is a FixedLogBuffer that tracks the state of LogUnits to make privacy guarantees. - * - * There are three forms of privacy protection: 1) only words in the main dictionary are allowed to - * be logged in enough detail to determine their contents, 2) only a subset of words are logged - * in detail, such as 10%, and 3) no numbers are logged. - * - * This class maintains a list of LogUnits, each corresponding to a word. As the user completes - * words, they are added here. But if the user backs up over their current word to edit a word - * entered earlier, then it is pulled out of this LogBuffer, changes are then added to the end of - * the LogUnit, and it is pushed back in here when the user is done. Because words may be pulled - * back out even after they are pushed in, we must not publish the contents of this LogBuffer too - * quickly. However, we cannot let the contents pile up either, or it will limit the editing that - * a user can perform. - * - * To balance these requirements (keep history so user can edit, flush history so it does not pile - * up), the LogBuffer is considered "complete" when the user has entered enough words to form an - * n-gram, followed by enough additional non-detailed words (that are in the 90%, as per above). - * Once complete, the n-gram may be published to flash storage (via the ResearchLog class). - * However, the additional non-detailed words are retained, in case the user backspaces to edit - * them. The MainLogBuffer then continues to add words, publishing individual non-detailed words - * as new words arrive. After enough non-detailed words have been pushed out to account for the - * 90% between words, the words at the front of the LogBuffer can be published as an n-gram again. - * - * If the words that would form the valid n-gram are not in the dictionary, then words are pushed - * through the LogBuffer one at a time until an n-gram is found that is entirely composed of - * dictionary words. - * - * If the user closes a session, then the entire LogBuffer is flushed, publishing any embedded - * n-gram containing dictionary words. - */ -public abstract class MainLogBuffer extends FixedLogBuffer { - private static final String TAG = MainLogBuffer.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - - // Keep consistent with switch statement in Statistics.recordPublishabilityResultCode() - public static final int PUBLISHABILITY_PUBLISHABLE = 0; - public static final int PUBLISHABILITY_UNPUBLISHABLE_STOPPING = 1; - public static final int PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT = 2; - public static final int PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY = 3; - public static final int PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE = 4; - public static final int PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT = 5; - public static final int PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY = 6; - - // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams. - public static final int N_GRAM_SIZE = 2; - - private final DictionaryFacilitatorForSuggest mDictionaryFacilitator; - @UsedForTesting - private Dictionary mDictionaryForTesting; - private boolean mIsStopping = false; - - /* package for test */ int mNumWordsBetweenNGrams; - - // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod - // after a sample is taken. - /* package for test */ int mNumWordsUntilSafeToSample; - - public MainLogBuffer(final int wordsBetweenSamples, final int numInitialWordsToIgnore, - final DictionaryFacilitatorForSuggest dictionaryFacilitator) { - super(N_GRAM_SIZE + wordsBetweenSamples); - mNumWordsBetweenNGrams = wordsBetweenSamples; - mNumWordsUntilSafeToSample = DEBUG ? 0 : numInitialWordsToIgnore; - mDictionaryFacilitator = dictionaryFacilitator; - } - - @UsedForTesting - /* package for test */ void setDictionaryForTesting(final Dictionary dictionary) { - mDictionaryForTesting = dictionary; - } - - private boolean isValidDictWord(final String word) { - if (mDictionaryForTesting != null) { - return mDictionaryForTesting.isValidWord(word); - } - if (mDictionaryFacilitator != null) { - return mDictionaryFacilitator.isValidMainDictWord(word); - } - return false; - } - - public void setIsStopping() { - mIsStopping = true; - } - - /** - * Determines whether the string determined by a series of LogUnits will not violate user - * privacy if published. - * - * @param logUnits a LogUnit list to check for publishability - * @param nGramSize the smallest n-gram acceptable to be published. if - * {@link ResearchLogger#IS_LOGGING_EVERYTHING} is true, then publish if there are more than - * {@code minNGramSize} words in the logUnits, otherwise wait. if {@link - * ResearchLogger#IS_LOGGING_EVERYTHING} is false, then ensure that there are exactly nGramSize - * words in the LogUnits. - * - * @return one of the {@code PUBLISHABILITY_*} result codes defined in this class. - */ - private int getPublishabilityResultCode(final ArrayList<LogUnit> logUnits, - final int nGramSize) { - // Bypass privacy checks when debugging. - if (ResearchLogger.IS_LOGGING_EVERYTHING) { - if (mIsStopping) { - return PUBLISHABILITY_UNPUBLISHABLE_STOPPING; - } - // Only check that it is the right length. If not, wait for later words to make - // complete n-grams. - int numWordsInLogUnitList = 0; - final int length = logUnits.size(); - for (int i = 0; i < length; i++) { - final LogUnit logUnit = logUnits.get(i); - numWordsInLogUnitList += logUnit.getNumWords(); - } - if (numWordsInLogUnitList >= nGramSize) { - return PUBLISHABILITY_PUBLISHABLE; - } else { - return PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT; - } - } - - // Check that we are not sampling too frequently. Having sampled recently might disclose - // too much of the user's intended meaning. - if (mNumWordsUntilSafeToSample > 0) { - return PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY; - } - // Reload the dictionary in case it has changed (e.g., because the user has changed - // languages). - if ((mDictionaryFacilitator == null - || !mDictionaryFacilitator.hasInitializedMainDictionary()) - && mDictionaryForTesting == null) { - // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a - // word is out-of-vocabulary or not. Therefore, we must judge the entire buffer - // contents to potentially pose a privacy risk. - return PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE; - } - - // Check each word in the buffer. If any word poses a privacy threat, we cannot upload - // the complete buffer contents in detail. - int numWordsInLogUnitList = 0; - for (final LogUnit logUnit : logUnits) { - if (!logUnit.hasOneOrMoreWords()) { - // Digits outside words are a privacy threat. - if (logUnit.mayContainDigit()) { - return PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT; - } - } else { - numWordsInLogUnitList += logUnit.getNumWords(); - final String[] words = logUnit.getWordsAsStringArray(); - for (final String word : words) { - // Words not in the dictionary are a privacy threat. - if (ResearchLogger.hasLetters(word) && !isValidDictWord(word)) { - if (DEBUG) { - Log.d(TAG, "\"" + word + "\" NOT SAFE!: hasLetters: " - + ResearchLogger.hasLetters(word) - + ", isValid: " + isValidDictWord(word)); - } - return PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY; - } - } - } - } - - // Finally, only return true if the ngram is the right size. - if (numWordsInLogUnitList == nGramSize) { - return PUBLISHABILITY_PUBLISHABLE; - } else { - return PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT; - } - } - - public void shiftAndPublishAll() throws IOException { - final LinkedList<LogUnit> logUnits = getLogUnits(); - while (!logUnits.isEmpty()) { - publishLogUnitsAtFrontOfBuffer(); - } - } - - @Override - protected final void onBufferFull() { - try { - publishLogUnitsAtFrontOfBuffer(); - } catch (final IOException e) { - if (DEBUG) { - Log.w(TAG, "IOException when publishing front of LogBuffer", e); - } - } - } - - /** - * If there is a safe n-gram at the front of this log buffer, publish it with all details, and - * remove the LogUnits that constitute it. - * - * An n-gram might not be "safe" if it violates privacy controls. E.g., it might contain - * numbers, an out-of-vocabulary word, or another n-gram may have been published recently. If - * there is no safe n-gram, then the LogUnits up through the first word-containing LogUnit are - * published, but without disclosing any privacy-related details, such as the word the LogUnit - * generated, motion data, etc. - * - * Note that a LogUnit can hold more than one word if the user types without explicit spaces. - * In this case, the words may be grouped together in such a way that pulling an n-gram off the - * front would require splitting a LogUnit. Splitting a LogUnit is not possible, so this case - * is treated just as the unsafe n-gram case. This may cause n-grams to be sampled at slightly - * less than the target frequency. - */ - protected final void publishLogUnitsAtFrontOfBuffer() throws IOException { - // TODO: Refactor this method to require fewer passes through the LogUnits. Should really - // require only one pass. - ArrayList<LogUnit> logUnits = peekAtFirstNWords(N_GRAM_SIZE); - final int publishabilityResultCode = getPublishabilityResultCode(logUnits, N_GRAM_SIZE); - ResearchLogger.recordPublishabilityResultCode(publishabilityResultCode); - if (publishabilityResultCode == MainLogBuffer.PUBLISHABILITY_PUBLISHABLE) { - // Good n-gram at the front of the buffer. Publish it, disclosing details. - publish(logUnits, true /* canIncludePrivateData */); - shiftOutWords(N_GRAM_SIZE); - mNumWordsUntilSafeToSample = mNumWordsBetweenNGrams; - return; - } - // No good n-gram at front, and buffer is full. Shift out up through the first logUnit - // with associated words (or if there is none, all the existing logUnits). - logUnits.clear(); - LogUnit logUnit = shiftOut(); - while (logUnit != null) { - logUnits.add(logUnit); - final int numWords = logUnit.getNumWords(); - if (numWords > 0) { - mNumWordsUntilSafeToSample = Math.max(0, mNumWordsUntilSafeToSample - numWords); - break; - } - logUnit = shiftOut(); - } - publish(logUnits, false /* canIncludePrivateData */); - } - - /** - * Called when a list of logUnits should be published. - * - * It is the subclass's responsibility to implement the publication. - * - * @param logUnits The list of logUnits to be published. - * @param canIncludePrivateData Whether the private data in the logUnits can be included in - * publication. - * - * @throws IOException if publication to the log file is not possible - */ - protected abstract void publish(final ArrayList<LogUnit> logUnits, - final boolean canIncludePrivateData) throws IOException; - - @Override - protected int shiftOutWords(final int numWords) { - final int numWordsShiftedOut = super.shiftOutWords(numWords); - mNumWordsUntilSafeToSample = Math.max(0, mNumWordsUntilSafeToSample - numWordsShiftedOut); - if (DEBUG) { - Log.d(TAG, "wordsUntilSafeToSample now at " + mNumWordsUntilSafeToSample); - } - return numWordsShiftedOut; - } -} diff --git a/java/src/com/android/inputmethod/research/MotionEventReader.java b/java/src/com/android/inputmethod/research/MotionEventReader.java deleted file mode 100644 index 3388645b7..000000000 --- a/java/src/com/android/inputmethod/research/MotionEventReader.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.research; - -import android.util.JsonReader; -import android.util.Log; -import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; -import android.view.MotionEvent.PointerProperties; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; - -public class MotionEventReader { - private static final String TAG = MotionEventReader.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - // Assumes that MotionEvent.ACTION_MASK does not have all bits set.` - private static final int UNINITIALIZED_ACTION = ~MotionEvent.ACTION_MASK; - // No legitimate int is negative - private static final int UNINITIALIZED_INT = -1; - // No legitimate long is negative - private static final long UNINITIALIZED_LONG = -1L; - // No legitimate float is negative - private static final float UNINITIALIZED_FLOAT = -1.0f; - - public ReplayData readMotionEventData(final File file) { - final ReplayData replayData = new ReplayData(); - try { - // Read file - final JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader( - new FileInputStream(file)))); - jsonReader.beginArray(); - while (jsonReader.hasNext()) { - readLogStatement(jsonReader, replayData); - } - jsonReader.endArray(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - return replayData; - } - - @UsedForTesting - static class ReplayData { - final ArrayList<Integer> mActions = new ArrayList<Integer>(); - final ArrayList<PointerProperties[]> mPointerPropertiesArrays - = new ArrayList<PointerProperties[]>(); - final ArrayList<PointerCoords[]> mPointerCoordsArrays = new ArrayList<PointerCoords[]>(); - final ArrayList<Long> mTimes = new ArrayList<Long>(); - } - - /** - * Read motion data from a logStatement and store it in {@code replayData}. - * - * Two kinds of logStatements can be read. In the first variant, the MotionEvent data is - * represented as attributes at the top level like so: - * - * <pre> - * { - * "_ct": 1359590400000, - * "_ut": 4381933, - * "_ty": "MotionEvent", - * "action": "UP", - * "isLoggingRelated": false, - * "x": 100, - * "y": 200 - * } - * </pre> - * - * In the second variant, there is a separate attribute for the MotionEvent that includes - * historical data if present: - * - * <pre> - * { - * "_ct": 135959040000, - * "_ut": 4382702, - * "_ty": "MotionEvent", - * "action": "MOVE", - * "isLoggingRelated": false, - * "motionEvent": { - * "pointerIds": [ - * 0 - * ], - * "xyt": [ - * { - * "t": 4382551, - * "d": [ - * { - * "x": 141.25, - * "y": 151.8485107421875, - * "toma": 101.82337188720703, - * "tomi": 101.82337188720703, - * "o": 0.0 - * } - * ] - * }, - * { - * "t": 4382559, - * "d": [ - * { - * "x": 140.7266082763672, - * "y": 151.8485107421875, - * "toma": 101.82337188720703, - * "tomi": 101.82337188720703, - * "o": 0.0 - * } - * ] - * } - * ] - * } - * }, - * </pre> - */ - @UsedForTesting - /* package for test */ void readLogStatement(final JsonReader jsonReader, - final ReplayData replayData) throws IOException { - String logStatementType = null; - int actionType = UNINITIALIZED_ACTION; - int x = UNINITIALIZED_INT; - int y = UNINITIALIZED_INT; - long time = UNINITIALIZED_LONG; - boolean isLoggingRelated = false; - - jsonReader.beginObject(); - while (jsonReader.hasNext()) { - final String key = jsonReader.nextName(); - if (key.equals("_ty")) { - logStatementType = jsonReader.nextString(); - } else if (key.equals("_ut")) { - time = jsonReader.nextLong(); - } else if (key.equals("x")) { - x = jsonReader.nextInt(); - } else if (key.equals("y")) { - y = jsonReader.nextInt(); - } else if (key.equals("action")) { - final String s = jsonReader.nextString(); - if (s.equals("UP")) { - actionType = MotionEvent.ACTION_UP; - } else if (s.equals("DOWN")) { - actionType = MotionEvent.ACTION_DOWN; - } else if (s.equals("MOVE")) { - actionType = MotionEvent.ACTION_MOVE; - } - } else if (key.equals("loggingRelated")) { - isLoggingRelated = jsonReader.nextBoolean(); - } else if (logStatementType != null && logStatementType.equals("MotionEvent") - && key.equals("motionEvent")) { - if (actionType == UNINITIALIZED_ACTION) { - Log.e(TAG, "no actionType assigned in MotionEvent json"); - } - // Second variant of LogStatement. - if (isLoggingRelated) { - jsonReader.skipValue(); - } else { - readEmbeddedMotionEvent(jsonReader, replayData, actionType); - } - } else { - if (DEBUG) { - Log.w(TAG, "Unknown JSON key in LogStatement: " + key); - } - jsonReader.skipValue(); - } - } - jsonReader.endObject(); - - if (logStatementType != null && time != UNINITIALIZED_LONG && x != UNINITIALIZED_INT - && y != UNINITIALIZED_INT && actionType != UNINITIALIZED_ACTION - && logStatementType.equals("MotionEvent") && !isLoggingRelated) { - // First variant of LogStatement. - final PointerProperties pointerProperties = new PointerProperties(); - pointerProperties.id = 0; - pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN; - final PointerProperties[] pointerPropertiesArray = { - pointerProperties - }; - final PointerCoords pointerCoords = new PointerCoords(); - pointerCoords.x = x; - pointerCoords.y = y; - pointerCoords.pressure = 1.0f; - pointerCoords.size = 1.0f; - final PointerCoords[] pointerCoordsArray = { - pointerCoords - }; - addMotionEventData(replayData, actionType, time, pointerPropertiesArray, - pointerCoordsArray); - } - } - - private void readEmbeddedMotionEvent(final JsonReader jsonReader, final ReplayData replayData, - final int actionType) throws IOException { - jsonReader.beginObject(); - PointerProperties[] pointerPropertiesArray = null; - while (jsonReader.hasNext()) { // pointerIds/xyt - final String name = jsonReader.nextName(); - if (name.equals("pointerIds")) { - pointerPropertiesArray = readPointerProperties(jsonReader); - } else if (name.equals("xyt")) { - readPointerData(jsonReader, replayData, actionType, pointerPropertiesArray); - } - } - jsonReader.endObject(); - } - - private PointerProperties[] readPointerProperties(final JsonReader jsonReader) - throws IOException { - final ArrayList<PointerProperties> pointerPropertiesArrayList = - new ArrayList<PointerProperties>(); - jsonReader.beginArray(); - while (jsonReader.hasNext()) { - final PointerProperties pointerProperties = new PointerProperties(); - pointerProperties.id = jsonReader.nextInt(); - pointerProperties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN; - pointerPropertiesArrayList.add(pointerProperties); - } - jsonReader.endArray(); - return pointerPropertiesArrayList.toArray( - new PointerProperties[pointerPropertiesArrayList.size()]); - } - - private void readPointerData(final JsonReader jsonReader, final ReplayData replayData, - final int actionType, final PointerProperties[] pointerPropertiesArray) - throws IOException { - if (pointerPropertiesArray == null) { - Log.e(TAG, "PointerIDs must be given before xyt data in json for MotionEvent"); - jsonReader.skipValue(); - return; - } - long time = UNINITIALIZED_LONG; - jsonReader.beginArray(); - while (jsonReader.hasNext()) { // Array of historical data - jsonReader.beginObject(); - final ArrayList<PointerCoords> pointerCoordsArrayList = new ArrayList<PointerCoords>(); - while (jsonReader.hasNext()) { // Time/data object - final String name = jsonReader.nextName(); - if (name.equals("t")) { - time = jsonReader.nextLong(); - } else if (name.equals("d")) { - jsonReader.beginArray(); - while (jsonReader.hasNext()) { // array of data per pointer - final PointerCoords pointerCoords = readPointerCoords(jsonReader); - if (pointerCoords != null) { - pointerCoordsArrayList.add(pointerCoords); - } - } - jsonReader.endArray(); - } else { - jsonReader.skipValue(); - } - } - jsonReader.endObject(); - // Data was recorded as historical events, but must be split apart into - // separate MotionEvents for replaying - if (time != UNINITIALIZED_LONG) { - addMotionEventData(replayData, actionType, time, pointerPropertiesArray, - pointerCoordsArrayList.toArray( - new PointerCoords[pointerCoordsArrayList.size()])); - } else { - Log.e(TAG, "Time not assigned in json for MotionEvent"); - } - } - jsonReader.endArray(); - } - - private PointerCoords readPointerCoords(final JsonReader jsonReader) throws IOException { - jsonReader.beginObject(); - float x = UNINITIALIZED_FLOAT; - float y = UNINITIALIZED_FLOAT; - while (jsonReader.hasNext()) { // x,y - final String name = jsonReader.nextName(); - if (name.equals("x")) { - x = (float) jsonReader.nextDouble(); - } else if (name.equals("y")) { - y = (float) jsonReader.nextDouble(); - } else { - jsonReader.skipValue(); - } - } - jsonReader.endObject(); - - if (Float.compare(x, UNINITIALIZED_FLOAT) == 0 - || Float.compare(y, UNINITIALIZED_FLOAT) == 0) { - Log.w(TAG, "missing x or y value in MotionEvent json"); - return null; - } - final PointerCoords pointerCoords = new PointerCoords(); - pointerCoords.x = x; - pointerCoords.y = y; - pointerCoords.pressure = 1.0f; - pointerCoords.size = 1.0f; - return pointerCoords; - } - - private void addMotionEventData(final ReplayData replayData, final int actionType, - final long time, final PointerProperties[] pointerProperties, - final PointerCoords[] pointerCoords) { - replayData.mActions.add(actionType); - replayData.mTimes.add(time); - replayData.mPointerPropertiesArrays.add(pointerProperties); - replayData.mPointerCoordsArrays.add(pointerCoords); - } -} diff --git a/java/src/com/android/inputmethod/research/Replayer.java b/java/src/com/android/inputmethod/research/Replayer.java deleted file mode 100644 index 903875f3c..000000000 --- a/java/src/com/android/inputmethod/research/Replayer.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.SystemClock; -import android.util.Log; -import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; -import android.view.MotionEvent.PointerProperties; - -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.research.MotionEventReader.ReplayData; - -/** - * Replays a sequence of motion events in realtime on the screen. - * - * Useful for user inspection of logged data. - */ -public class Replayer { - private static final String TAG = Replayer.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final long START_TIME_DELAY_MS = 500; - - private boolean mIsReplaying = false; - private KeyboardSwitcher mKeyboardSwitcher; - - private Replayer() { - } - - private static final Replayer sInstance = new Replayer(); - public static Replayer getInstance() { - return sInstance; - } - - public void setKeyboardSwitcher(final KeyboardSwitcher keyboardSwitcher) { - mKeyboardSwitcher = keyboardSwitcher; - } - - private static final int MSG_MOTION_EVENT = 0; - private static final int MSG_DONE = 1; - private static final int COMPLETION_TIME_MS = 500; - - // TODO: Support historical events and multi-touch. - public void replay(final ReplayData replayData, final Runnable callback) { - if (mIsReplaying) { - return; - } - mIsReplaying = true; - final int numActions = replayData.mActions.size(); - if (DEBUG) { - Log.d(TAG, "replaying " + numActions + " actions"); - } - if (numActions == 0) { - mIsReplaying = false; - return; - } - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - - // The reference time relative to the times stored in events. - final long origStartTime = replayData.mTimes.get(0); - // The reference time relative to which events are replayed in the present. - final long currentStartTime = SystemClock.uptimeMillis() + START_TIME_DELAY_MS; - // The adjustment needed to translate times from the original recorded time to the current - // time. - final long timeAdjustment = currentStartTime - origStartTime; - final Handler handler = new Handler(Looper.getMainLooper()) { - // Track the time of the most recent DOWN event, to be passed as a parameter when - // constructing a MotionEvent. It's initialized here to the origStartTime, but this is - // only a precaution. The value should be overwritten by the first ACTION_DOWN event - // before the first use of the variable. Note that this may cause the first few events - // to have incorrect {@code downTime}s. - private long mOrigDownTime = origStartTime; - - @Override - public void handleMessage(final Message msg) { - switch (msg.what) { - case MSG_MOTION_EVENT: - final int index = msg.arg1; - final int action = replayData.mActions.get(index); - final PointerProperties[] pointerPropertiesArray = - replayData.mPointerPropertiesArrays.get(index); - final PointerCoords[] pointerCoordsArray = - replayData.mPointerCoordsArrays.get(index); - final long origTime = replayData.mTimes.get(index); - if (action == MotionEvent.ACTION_DOWN) { - mOrigDownTime = origTime; - } - - final MotionEvent me = MotionEvent.obtain(mOrigDownTime + timeAdjustment, - origTime + timeAdjustment, action, - pointerPropertiesArray.length, pointerPropertiesArray, - pointerCoordsArray, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0); - mainKeyboardView.processMotionEvent(me); - me.recycle(); - break; - case MSG_DONE: - mIsReplaying = false; - ResearchLogger.getInstance().requestIndicatorRedraw(); - break; - } - } - }; - - handler.post(new Runnable() { - @Override - public void run() { - ResearchLogger.getInstance().requestIndicatorRedraw(); - } - }); - for (int i = 0; i < numActions; i++) { - final Message msg = Message.obtain(handler, MSG_MOTION_EVENT, i, 0); - final long msgTime = replayData.mTimes.get(i) + timeAdjustment; - handler.sendMessageAtTime(msg, msgTime); - if (DEBUG) { - Log.d(TAG, "queuing event at " + msgTime); - } - } - - final long presentDoneTime = replayData.mTimes.get(numActions - 1) + timeAdjustment - + COMPLETION_TIME_MS; - handler.sendMessageAtTime(Message.obtain(handler, MSG_DONE), presentDoneTime); - if (callback != null) { - handler.postAtTime(callback, presentDoneTime + 1); - } - } - - public boolean isReplaying() { - return mIsReplaying; - } -} diff --git a/java/src/com/android/inputmethod/research/ReplayerService.java b/java/src/com/android/inputmethod/research/ReplayerService.java deleted file mode 100644 index 88d9033cf..000000000 --- a/java/src/com/android/inputmethod/research/ReplayerService.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.research; - -import android.app.IntentService; -import android.content.Intent; -import android.util.Log; - -import com.android.inputmethod.research.MotionEventReader.ReplayData; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -/** - * Provide a mechanism to invoke the replayer from outside. - * - * In particular, makes access from a host possible through {@code adb am startservice}. - */ -public class ReplayerService extends IntentService { - private static final String TAG = ReplayerService.class.getSimpleName(); - private static final String EXTRA_FILENAME = "com.android.inputmethod.research.extra.FILENAME"; - private static final long MAX_REPLAY_TIME = TimeUnit.SECONDS.toMillis(60); - - public ReplayerService() { - super(ReplayerService.class.getSimpleName()); - } - - @Override - protected void onHandleIntent(final Intent intent) { - final String filename = intent.getStringExtra(EXTRA_FILENAME); - if (filename == null) return; - - final ReplayData replayData = new MotionEventReader().readMotionEventData( - new File(filename)); - synchronized (this) { - Replayer.getInstance().replay(replayData, new Runnable() { - @Override - public void run() { - synchronized (ReplayerService.this) { - ReplayerService.this.notify(); - } - } - }); - try { - wait(MAX_REPLAY_TIME); - } catch (InterruptedException e) { - Log.e(TAG, "Timeout while replaying.", e); - } - } - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java deleted file mode 100644 index 46e620ae5..000000000 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.Context; -import android.util.JsonWriter; -import android.util.Log; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.concurrent.Callable; -import java.util.concurrent.Executors; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * Logs the use of the LatinIME keyboard. - * - * This class logs operations on the IME keyboard, including what the user has typed. Data is - * written to a {@link JsonWriter}, which will write to a local file. - * - * The JsonWriter is created on-demand by calling {@link #getInitializedJsonWriterLocked}. - * - * This class uses an executor to perform file-writing operations on a separate thread. It also - * tries to avoid creating unnecessary files if there is nothing to write. It also handles - * flushing, making sure it happens, but not too frequently. - * - * This functionality is off by default. See - * {@link ProductionFlag#USES_DEVELOPMENT_ONLY_DIAGNOSTICS}. - */ -public class ResearchLog { - // TODO: Automatically initialize the JsonWriter rather than requiring the caller to manage it. - private static final String TAG = ResearchLog.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final long FLUSH_DELAY_IN_MS = TimeUnit.SECONDS.toMillis(5); - - /* package */ final ScheduledExecutorService mExecutor; - /* package */ final File mFile; - private final Context mContext; - - // Earlier implementations used a dummy JsonWriter that just swallowed what it was given, but - // this was tricky to do well, because JsonWriter throws an exception if it is passed more than - // one top-level object. - private JsonWriter mJsonWriter = null; - - // true if at least one byte of data has been written out to the log file. This must be - // remembered because JsonWriter requires that calls matching calls to beginObject and - // endObject, as well as beginArray and endArray, and the file is opened lazily, only when - // it is certain that data will be written. Alternatively, the matching call exceptions - // could be caught, but this might suppress other errors. - private boolean mHasWrittenData = false; - - public ResearchLog(final File outputFile, final Context context) { - mExecutor = Executors.newSingleThreadScheduledExecutor(); - mFile = outputFile; - mContext = context; - } - - /** - * Returns true if this is a FeedbackLog. - * - * FeedbackLogs record only the data associated with a Feedback dialog. Instead of normal - * logging, they contain a LogStatement with the complete feedback string and optionally a - * recording of the user's supplied demo of the problem. - */ - public boolean isFeedbackLog() { - return false; - } - - /** - * Waits for any publication requests to finish and closes the {@link JsonWriter} used for - * output. - * - * See class comment for details about {@code JsonWriter} construction. - * - * @param onClosed run after the close() operation has completed asynchronously - */ - private synchronized void close(final Runnable onClosed) { - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - if (mJsonWriter == null) return null; - // TODO: This is necessary to avoid an exception. Better would be to not even - // open the JsonWriter if the file is not even opened unless there is valid data - // to write. - if (!mHasWrittenData) { - mJsonWriter.beginArray(); - } - mJsonWriter.endArray(); - mHasWrittenData = false; - mJsonWriter.flush(); - mJsonWriter.close(); - if (DEBUG) { - Log.d(TAG, "closed " + mFile); - } - } catch (final Exception e) { - Log.d(TAG, "error when closing ResearchLog:", e); - } finally { - // Marking the file as read-only signals that this log file is ready to be - // uploaded. - if (mFile != null && mFile.exists()) { - mFile.setWritable(false, false); - } - if (onClosed != null) { - onClosed.run(); - } - } - return null; - } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - } - - /** - * Block until the research log has shut down and spooled out all output or {@code timeout} - * occurs. - * - * @param timeout time to wait for close in milliseconds - */ - public void blockingClose(final long timeout) { - close(null); - awaitTermination(timeout, TimeUnit.MILLISECONDS); - } - - /** - * Waits for publication requests to finish, closes the JsonWriter, but then deletes the backing - * output file. - * - * @param onAbort run after the abort() operation has completed asynchronously - */ - private synchronized void abort(final Runnable onAbort) { - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - try { - if (mJsonWriter == null) return null; - if (mHasWrittenData) { - // TODO: This is necessary to avoid an exception. Better would be to not - // even open the JsonWriter if the file is not even opened unless there is - // valid data to write. - if (!mHasWrittenData) { - mJsonWriter.beginArray(); - } - mJsonWriter.endArray(); - mJsonWriter.close(); - mHasWrittenData = false; - } - } finally { - if (mFile != null) { - mFile.delete(); - } - if (onAbort != null) { - onAbort.run(); - } - } - return null; - } - }); - removeAnyScheduledFlush(); - mExecutor.shutdown(); - } - - /** - * Block until the research log has aborted or {@code timeout} occurs. - * - * @param timeout time to wait for close in milliseconds - */ - public void blockingAbort(final long timeout) { - abort(null); - awaitTermination(timeout, TimeUnit.MILLISECONDS); - } - - @UsedForTesting - public void awaitTermination(final long delay, final TimeUnit timeUnit) { - try { - if (!mExecutor.awaitTermination(delay, timeUnit)) { - Log.e(TAG, "ResearchLog executor timed out while awaiting terminaion"); - } - } catch (final InterruptedException e) { - Log.e(TAG, "ResearchLog executor interrupted while awaiting terminaion", e); - } - } - - /* package */ synchronized void flush() { - removeAnyScheduledFlush(); - mExecutor.submit(mFlushCallable); - } - - private final Callable<Object> mFlushCallable = new Callable<Object>() { - @Override - public Object call() throws Exception { - if (mJsonWriter != null) mJsonWriter.flush(); - return null; - } - }; - - private ScheduledFuture<Object> mFlushFuture; - - private void removeAnyScheduledFlush() { - if (mFlushFuture != null) { - mFlushFuture.cancel(false); - mFlushFuture = null; - } - } - - private void scheduleFlush() { - removeAnyScheduledFlush(); - mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS); - } - - /** - * Queues up {@code logUnit} to be published in the background. - * - * @param logUnit the {@link LogUnit} to be published - * @param canIncludePrivateData whether private data in the LogUnit should be included - */ - public synchronized void publish(final LogUnit logUnit, final boolean canIncludePrivateData) { - try { - mExecutor.submit(new Callable<Object>() { - @Override - public Object call() throws Exception { - logUnit.publishTo(ResearchLog.this, canIncludePrivateData); - scheduleFlush(); - return null; - } - }); - } catch (final RejectedExecutionException e) { - // TODO: Add code to record loss of data, and report. - if (DEBUG) { - Log.d(TAG, "ResearchLog.publish() rejecting scheduled execution", e); - } - } - } - - /** - * Return a JsonWriter for this ResearchLog. It is initialized the first time this method is - * called. The cached value is returned in future calls. - * - * @throws IOException if opening the JsonWriter is not possible - */ - public JsonWriter getInitializedJsonWriterLocked() throws IOException { - if (mJsonWriter != null) return mJsonWriter; - if (mFile == null) throw new FileNotFoundException(); - try { - final JsonWriter jsonWriter = createJsonWriter(mContext, mFile); - if (jsonWriter == null) throw new IOException("Could not create JsonWriter"); - - jsonWriter.beginArray(); - mJsonWriter = jsonWriter; - mHasWrittenData = true; - return mJsonWriter; - } catch (final IOException e) { - if (DEBUG) { - Log.w(TAG, "Exception when creating JsonWriter", e); - Log.w(TAG, "Closing JsonWriter"); - } - if (mJsonWriter != null) mJsonWriter.close(); - mJsonWriter = null; - throw e; - } - } - - /** - * Create the JsonWriter to write the ResearchLog to. - * - * This method may be overriden in testing to redirect the output. - */ - /* package for test */ JsonWriter createJsonWriter(final Context context, final File file) - throws IOException { - return new JsonWriter(new BufferedWriter(new OutputStreamWriter( - context.openFileOutput(file.getName(), Context.MODE_PRIVATE)))); - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java deleted file mode 100644 index d156068d6..000000000 --- a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.Context; -import android.util.Log; - -import java.io.File; -import java.io.FileFilter; - -/** - * Manages log files. - * - * This class handles all aspects where and how research log data is stored. This includes - * generating log filenames in the correct place with the correct names, and cleaning up log files - * under this directory. - */ -public class ResearchLogDirectory { - public static final String TAG = ResearchLogDirectory.class.getSimpleName(); - /* package */ static final String LOG_FILENAME_PREFIX = "researchLog"; - private static final String FILENAME_SUFFIX = ".txt"; - private static final String USER_RECORDING_FILENAME_PREFIX = "recording"; - - private static final ReadOnlyLogFileFilter sUploadableLogFileFilter = - new ReadOnlyLogFileFilter(); - - private final File mFilesDir; - - static class ReadOnlyLogFileFilter implements FileFilter { - @Override - public boolean accept(final File pathname) { - return pathname.getName().startsWith(ResearchLogDirectory.LOG_FILENAME_PREFIX) - && !pathname.canWrite(); - } - } - - /** - * Creates a new ResearchLogDirectory, creating the storage directory if it does not exist. - */ - public ResearchLogDirectory(final Context context) { - mFilesDir = getLoggingDirectory(context); - if (mFilesDir == null) { - throw new NullPointerException("No files directory specified"); - } - if (!mFilesDir.exists()) { - mFilesDir.mkdirs(); - } - } - - private File getLoggingDirectory(final Context context) { - // TODO: Switch to using a subdirectory of getFilesDir(). - return context.getFilesDir(); - } - - /** - * Get an array of log files that are ready for uploading. - * - * A file is ready for uploading if it is marked as read-only. - * - * @return the array of uploadable files - */ - public File[] getUploadableLogFiles() { - try { - return mFilesDir.listFiles(sUploadableLogFileFilter); - } catch (final SecurityException e) { - Log.e(TAG, "Could not cleanup log directory, permission denied", e); - return new File[0]; - } - } - - public void cleanupLogFilesOlderThan(final long time) { - try { - for (final File file : mFilesDir.listFiles()) { - final String filename = file.getName(); - if ((filename.startsWith(LOG_FILENAME_PREFIX) - || filename.startsWith(USER_RECORDING_FILENAME_PREFIX)) - && (file.lastModified() < time)) { - file.delete(); - } - } - } catch (final SecurityException e) { - Log.e(TAG, "Could not cleanup log directory, permission denied", e); - } - } - - public File getLogFilePath(final long time, final long nanoTime) { - return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time, nanoTime)); - } - - public File getUserRecordingFilePath(final long time, final long nanoTime) { - return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time, - nanoTime)); - } - - private static String getUniqueFilename(final String prefix, final long time, - final long nanoTime) { - return prefix + "-" + time + "-" + nanoTime + FILENAME_SUFFIX; - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java deleted file mode 100644 index d907dd1b0..000000000 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ /dev/null @@ -1,1885 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.inputmethod.CompletionInfo; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; -import android.widget.Toast; - -import com.android.inputmethod.keyboard.Key; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.MainKeyboardView; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; -import com.android.inputmethod.latin.LatinIME; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.RichInputConnection; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.define.ProductionFlag; -import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.StringUtils; -import com.android.inputmethod.latin.utils.TextRange; -import com.android.inputmethod.research.MotionEventReader.ReplayData; -import com.android.inputmethod.research.ui.SplashScreen; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; - -// TODO: Add a unit test for every "logging" method (i.e. that is called from the IME and calls -// enqueueEvent to record a LogStatement). -/** - * Logs the use of the LatinIME keyboard. - * - * This class logs operations on the IME keyboard, including what the user has typed. - * Data is stored locally in a file in app-specific storage. - * - * This functionality is off by default. See - * {@link ProductionFlag#USES_DEVELOPMENT_ONLY_DIAGNOSTICS}. - */ -public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener, - SplashScreen.UserConsentListener { - // TODO: This class has grown quite large and combines several concerns that should be - // separated. The following refactorings will be applied as soon as possible after adding - // support for replaying historical events, fixing some replay bugs, adding some ui constraints - // on the feedback dialog, and adding the survey dialog. - // TODO: Refactor. Move feedback screen code into separate class. - // TODO: Refactor. Move logging invocations into their own class. - // TODO: Refactor. Move currentLogUnit management into separate class. - private static final String TAG = ResearchLogger.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final boolean DEBUG_REPLAY_AFTER_FEEDBACK = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - // Whether the feedback dialog preserves the editable text across invocations. Should be false - // for normal research builds so users do not have to delete the same feedback string they - // entered earlier. Should be true for builds internal to a development team so when the text - // field holds a channel name, the developer does not have to re-enter it when using the - // feedback mechanism to generate multiple tests. - private static final boolean FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD = false; - /* package */ static boolean sIsLogging = false; - private static final int OUTPUT_FORMAT_VERSION = 6; - // Whether all words should be recorded, leaving unsampled word between bigrams. Useful for - // testing. - /* package for test */ static final boolean IS_LOGGING_EVERYTHING = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - // The number of words between n-grams to omit from the log. - private static final int NUMBER_OF_WORDS_BETWEEN_SAMPLES = - IS_LOGGING_EVERYTHING ? 0 : (DEBUG ? 2 : 18); - - // Whether to show an indicator on the screen that logging is on. Currently a very small red - // dot in the lower right hand corner. Most users should not notice it. - private static final boolean IS_SHOWING_INDICATOR = true; - // Change the default indicator to something very visible. Currently two red vertical bars on - // either side of they keyboard. - private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false || - (IS_LOGGING_EVERYTHING && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG); - // FEEDBACK_WORD_BUFFER_SIZE should add 1 because it must also hold the feedback LogUnit itself. - public static final int FEEDBACK_WORD_BUFFER_SIZE = (Integer.MAX_VALUE - 1) + 1; - - // The special output text to invoke a research feedback dialog. - public static final String RESEARCH_KEY_OUTPUT_TEXT = ".research."; - - // constants related to specific log points - private static final int[] WHITESPACE_SEPARATORS = - StringUtils.toSortedCodePointArray(" \t\n\r"); - private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1 - private static final String PREF_RESEARCH_SAVED_CHANNEL = "pref_research_saved_channel"; - - private static final long RESEARCHLOG_CLOSE_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); - private static final long RESEARCHLOG_ABORT_TIMEOUT_IN_MS = TimeUnit.SECONDS.toMillis(5); - private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = TimeUnit.DAYS.toMillis(1); - private static final long MAX_LOGFILE_AGE_IN_MS = TimeUnit.DAYS.toMillis(4); - - private static final ResearchLogger sInstance = new ResearchLogger(); - private static String sAccountType = null; - private static String sAllowedAccountDomain = null; - private ResearchLog mMainResearchLog; // always non-null after init() is called - // mFeedbackLog records all events for the session, private or not (excepting - // passwords). It is written to permanent storage only if the user explicitly commands - // the system to do so. - // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are - // complete. - /* package for test */ MainLogBuffer mMainLogBuffer; // always non-null after init() is called - /* package */ ResearchLog mUserRecordingLog; - /* package */ LogBuffer mUserRecordingLogBuffer; - private File mUserRecordingFile = null; - - private boolean mIsPasswordView = false; - private SharedPreferences mPrefs; - - // digits entered by the user are replaced with this codepoint. - /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT = - Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area" - // U+E001 is in the "private-use area" - /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001"; - protected static final int SUSPEND_DURATION_IN_MINUTES = 1; - - // used to check whether words are not unique - private DictionaryFacilitatorForSuggest mDictionaryFacilitator; - private MainKeyboardView mMainKeyboardView; - // TODO: Check whether a superclass can be used instead of LatinIME. - /* package for test */ LatinIME mLatinIME; - private final Statistics mStatistics; - private final MotionEventReader mMotionEventReader = new MotionEventReader(); - private final Replayer mReplayer = Replayer.getInstance(); - private ResearchLogDirectory mResearchLogDirectory; - private SplashScreen mSplashScreen; - - private Intent mUploadNowIntent; - - /* package for test */ LogUnit mCurrentLogUnit = new LogUnit(); - - // Gestured or tapped words may be committed after the gesture of the next word has started. - // To ensure that the gesture data of the next word is not associated with the previous word, - // thereby leaking private data, we store the time of the down event that started the second - // gesture, and when committing the earlier word, split the LogUnit. - private long mSavedDownEventTime; - private Bundle mFeedbackDialogBundle = null; - // Whether the feedback dialog is visible, and the user is typing into it. Normal logging is - // not performed on text that the user types into the feedback dialog. - private boolean mInFeedbackDialog = false; - private Handler mUserRecordingTimeoutHandler; - private static final long USER_RECORDING_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(30); - - // Stores a temporary LogUnit while generating a phantom space. Needed because phantom spaces - // are issued out-of-order, immediately before the characters generated by other operations that - // have already outputted LogStatements. - private LogUnit mPhantomSpaceLogUnit = null; - - private ResearchLogger() { - mStatistics = Statistics.getInstance(); - } - - public static ResearchLogger getInstance() { - return sInstance; - } - - public void init(final LatinIME latinIME, final KeyboardSwitcher keyboardSwitcher) { - assert latinIME != null; - mLatinIME = latinIME; - mPrefs = PreferenceManager.getDefaultSharedPreferences(latinIME); - mPrefs.registerOnSharedPreferenceChangeListener(this); - - // Initialize fields from preferences - sIsLogging = ResearchSettings.readResearchLoggerEnabledFlag(mPrefs); - - // Initialize fields from resources - final Resources res = latinIME.getResources(); - sAccountType = res.getString(R.string.research_account_type); - sAllowedAccountDomain = res.getString(R.string.research_allowed_account_domain); - - // Initialize directory manager - mResearchLogDirectory = new ResearchLogDirectory(mLatinIME); - cleanLogDirectoryIfNeeded(mResearchLogDirectory, System.currentTimeMillis()); - - // Initialize log buffers - resetLogBuffers(); - - // Initialize external services - mUploadNowIntent = new Intent(mLatinIME, UploaderService.class); - mUploadNowIntent.putExtra(UploaderService.EXTRA_UPLOAD_UNCONDITIONALLY, true); - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - UploaderService.cancelAndRescheduleUploadingService(mLatinIME, - true /* needsRescheduling */); - } - mReplayer.setKeyboardSwitcher(keyboardSwitcher); - } - - private void resetLogBuffers() { - mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis(), System.nanoTime()), mLatinIME); - final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); - mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, - mDictionaryFacilitator) { - @Override - protected void publish(final ArrayList<LogUnit> logUnits, - boolean canIncludePrivateData) { - canIncludePrivateData |= IS_LOGGING_EVERYTHING; - for (final LogUnit logUnit : logUnits) { - if (DEBUG) { - final String wordsString = logUnit.getWordsAsString(); - Log.d(TAG, "onPublish: '" + wordsString - + "', hc: " + logUnit.containsUserDeletions() - + ", cipd: " + canIncludePrivateData); - } - for (final String word : logUnit.getWordsAsStringArray()) { - final boolean isDictionaryWord = mDictionaryFacilitator != null - && mDictionaryFacilitator.isValidMainDictWord(word); - mStatistics.recordWordEntered( - isDictionaryWord, logUnit.containsUserDeletions()); - } - } - publishLogUnits(logUnits, mMainResearchLog, canIncludePrivateData); - } - }; - } - - private void cleanLogDirectoryIfNeeded(final ResearchLogDirectory researchLogDirectory, - final long now) { - final long lastCleanupTime = ResearchSettings.readResearchLastDirCleanupTime(mPrefs); - if (now - lastCleanupTime < DURATION_BETWEEN_DIR_CLEANUP_IN_MS) return; - final long oldestAllowedFileTime = now - MAX_LOGFILE_AGE_IN_MS; - mResearchLogDirectory.cleanupLogFilesOlderThan(oldestAllowedFileTime); - ResearchSettings.writeResearchLastDirCleanupTime(mPrefs, now); - } - - public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) { - mMainKeyboardView = mainKeyboardView; - maybeShowSplashScreen(); - } - - public void mainKeyboardView_onDetachedFromWindow() { - mMainKeyboardView = null; - } - - public void onDestroy() { - if (mPrefs != null) { - mPrefs.unregisterOnSharedPreferenceChangeListener(this); - } - } - - private void maybeShowSplashScreen() { - if (ResearchSettings.readHasSeenSplash(mPrefs)) return; - if (mSplashScreen != null && mSplashScreen.isShowing()) return; - if (mMainKeyboardView == null) return; - final IBinder windowToken = mMainKeyboardView.getWindowToken(); - if (windowToken == null) return; - - mSplashScreen = new SplashScreen(mLatinIME, this); - mSplashScreen.showSplashScreen(windowToken); - } - - @Override - public void onSplashScreenUserClickedOk() { - if (mPrefs == null) { - mPrefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); - if (mPrefs == null) return; - } - sIsLogging = true; - ResearchSettings.writeResearchLoggerEnabledFlag(mPrefs, true); - ResearchSettings.writeHasSeenSplash(mPrefs, true); - restart(); - } - - private void checkForEmptyEditor() { - if (mLatinIME == null) { - return; - } - final InputConnection ic = mLatinIME.getCurrentInputConnection(); - if (ic == null) { - return; - } - final CharSequence textBefore = ic.getTextBeforeCursor(1, 0); - if (!TextUtils.isEmpty(textBefore)) { - mStatistics.setIsEmptyUponStarting(false); - return; - } - final CharSequence textAfter = ic.getTextAfterCursor(1, 0); - if (!TextUtils.isEmpty(textAfter)) { - mStatistics.setIsEmptyUponStarting(false); - return; - } - if (textBefore != null && textAfter != null) { - mStatistics.setIsEmptyUponStarting(true); - } - } - - private void start() { - if (DEBUG) { - Log.d(TAG, "start called"); - } - maybeShowSplashScreen(); - requestIndicatorRedraw(); - mStatistics.reset(); - checkForEmptyEditor(); - } - - /* package */ void stop() { - if (DEBUG) { - Log.d(TAG, "stop called"); - } - // Commit mCurrentLogUnit before closing. - commitCurrentLogUnit(); - - try { - mMainLogBuffer.shiftAndPublishAll(); - } catch (final IOException e) { - Log.w(TAG, "IOException when publishing LogBuffer", e); - } - logStatistics(); - commitCurrentLogUnit(); - mMainLogBuffer.setIsStopping(); - try { - mMainLogBuffer.shiftAndPublishAll(); - } catch (final IOException e) { - Log.w(TAG, "IOException when publishing LogBuffer", e); - } - mMainResearchLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); - - resetLogBuffers(); - cancelFeedbackDialog(); - } - - public void abort() { - if (DEBUG) { - Log.d(TAG, "abort called"); - } - mMainLogBuffer.clear(); - mMainResearchLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); - - resetLogBuffers(); - } - - private void restart() { - stop(); - start(); - } - - @Override - public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - if (key == null || prefs == null) { - return; - } - requestIndicatorRedraw(); - mPrefs = prefs; - prefsChanged(prefs); - } - - public void onResearchKeySelected(final LatinIME latinIME) { - mCurrentLogUnit.removeResearchButtonInvocation(); - if (mInFeedbackDialog) { - Toast.makeText(latinIME, R.string.research_please_exit_feedback_form, - Toast.LENGTH_LONG).show(); - return; - } - presentFeedbackDialog(latinIME); - } - - public void presentFeedbackDialogFromSettings() { - if (mLatinIME != null) { - presentFeedbackDialog(mLatinIME); - } - } - - public void presentFeedbackDialog(final LatinIME latinIME) { - if (isMakingUserRecording()) { - saveRecording(); - } - mInFeedbackDialog = true; - - final Intent intent = new Intent(); - intent.setClass(mLatinIME, FeedbackActivity.class); - if (mFeedbackDialogBundle == null) { - // Restore feedback field with channel name - final Bundle bundle = new Bundle(); - bundle.putBoolean(FeedbackFragment.KEY_INCLUDE_ACCOUNT_NAME, true); - bundle.putBoolean(FeedbackFragment.KEY_HAS_USER_RECORDING, false); - if (FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD) { - final String savedChannelName = mPrefs.getString(PREF_RESEARCH_SAVED_CHANNEL, ""); - bundle.putString(FeedbackFragment.KEY_FEEDBACK_STRING, savedChannelName); - } - mFeedbackDialogBundle = bundle; - } - intent.putExtras(mFeedbackDialogBundle); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - latinIME.startActivity(intent); - } - - public void setFeedbackDialogBundle(final Bundle bundle) { - mFeedbackDialogBundle = bundle; - } - - public void startRecording() { - final Resources res = mLatinIME.getResources(); - Toast.makeText(mLatinIME, - res.getString(R.string.research_feedback_demonstration_instructions), - Toast.LENGTH_LONG).show(); - startRecordingInternal(); - } - - private void startRecordingInternal() { - if (mUserRecordingLog != null) { - mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); - } - mUserRecordingFile = mResearchLogDirectory.getUserRecordingFilePath( - System.currentTimeMillis(), System.nanoTime()); - mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME); - mUserRecordingLogBuffer = new LogBuffer(); - resetRecordingTimer(); - } - - private boolean isMakingUserRecording() { - return mUserRecordingLog != null; - } - - private void resetRecordingTimer() { - if (mUserRecordingTimeoutHandler == null) { - mUserRecordingTimeoutHandler = new Handler(); - } - clearRecordingTimer(); - mUserRecordingTimeoutHandler.postDelayed(mRecordingHandlerTimeoutRunnable, - USER_RECORDING_TIMEOUT_MS); - } - - private void clearRecordingTimer() { - mUserRecordingTimeoutHandler.removeCallbacks(mRecordingHandlerTimeoutRunnable); - } - - private Runnable mRecordingHandlerTimeoutRunnable = new Runnable() { - @Override - public void run() { - cancelRecording(); - requestIndicatorRedraw(); - final Resources res = mLatinIME.getResources(); - Toast.makeText(mLatinIME, res.getString(R.string.research_feedback_recording_failure), - Toast.LENGTH_LONG).show(); - } - }; - - private void cancelRecording() { - if (mUserRecordingLog != null) { - mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); - } - mUserRecordingLog = null; - mUserRecordingLogBuffer = null; - if (mFeedbackDialogBundle != null) { - mFeedbackDialogBundle.putBoolean("HasRecording", false); - } - } - - private void saveRecording() { - commitCurrentLogUnit(); - publishLogBuffer(mUserRecordingLogBuffer, mUserRecordingLog, true); - mUserRecordingLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); - mUserRecordingLog = null; - mUserRecordingLogBuffer = null; - - if (mFeedbackDialogBundle != null) { - mFeedbackDialogBundle.putBoolean(FeedbackFragment.KEY_HAS_USER_RECORDING, true); - } - clearRecordingTimer(); - } - - // TODO: currently unreachable. Remove after being sure enable/disable is - // not needed. - /* - public void enableOrDisable(final boolean showEnable, final LatinIME latinIME) { - if (showEnable) { - if (!sIsLogging) { - setLoggingAllowed(true); - } - resumeLogging(); - Toast.makeText(latinIME, - R.string.research_notify_session_logging_enabled, - Toast.LENGTH_LONG).show(); - } else { - Toast toast = Toast.makeText(latinIME, - R.string.research_notify_session_log_deleting, - Toast.LENGTH_LONG); - toast.show(); - boolean isLogDeleted = abort(); - final long currentTime = System.currentTimeMillis(); - final long resumeTime = currentTime - + TimeUnit.MINUTES.toMillis(SUSPEND_DURATION_IN_MINUTES); - suspendLoggingUntil(resumeTime); - toast.cancel(); - Toast.makeText(latinIME, R.string.research_notify_logging_suspended, - Toast.LENGTH_LONG).show(); - } - } - */ - - /** - * Get the name of the first allowed account on the device. - * - * Allowed accounts must be in the domain given by ALLOWED_ACCOUNT_DOMAIN. - * - * @return The user's account name. - */ - public String getAccountName() { - if (sAccountType == null || sAccountType.isEmpty()) { - return null; - } - if (sAllowedAccountDomain == null || sAllowedAccountDomain.isEmpty()) { - return null; - } - final AccountManager manager = AccountManager.get(mLatinIME); - // Filter first by account type. - final Account[] accounts = manager.getAccountsByType(sAccountType); - - for (final Account account : accounts) { - if (DEBUG) { - Log.d(TAG, account.name); - } - final String[] parts = account.name.split("@"); - if (parts.length > 1 && parts[1].equals(sAllowedAccountDomain)) { - return parts[0]; - } - } - return null; - } - - private static final LogStatement LOGSTATEMENT_FEEDBACK = - new LogStatement("UserFeedback", false, false, "contents", "accountName", "recording"); - public void sendFeedback(final String feedbackContents, final boolean includeHistory, - final boolean isIncludingAccountName, final boolean isIncludingRecording) { - String recording = ""; - if (isIncludingRecording) { - // Try to read recording from recently written json file - if (mUserRecordingFile != null) { - FileChannel channel = null; - try { - channel = new FileInputStream(mUserRecordingFile).getChannel(); - final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, - channel.size()); - // Android's openFileOutput() creates the file, so we use Android's default - // Charset (UTF-8) here to read it. - recording = Charset.defaultCharset().decode(buffer).toString(); - } catch (FileNotFoundException e) { - Log.e(TAG, "Could not find recording file", e); - } catch (IOException e) { - Log.e(TAG, "Error reading recording file", e); - } finally { - if (channel != null) { - try { - channel.close(); - } catch (IOException e) { - Log.e(TAG, "Error closing recording file", e); - } - } - } - } - } - final LogUnit feedbackLogUnit = new LogUnit(); - final String accountName = isIncludingAccountName ? getAccountName() : ""; - feedbackLogUnit.addLogStatement(LOGSTATEMENT_FEEDBACK, SystemClock.uptimeMillis(), - feedbackContents, accountName, recording); - - final ResearchLog feedbackLog = new FeedbackLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis(), System.nanoTime()), mLatinIME); - final LogBuffer feedbackLogBuffer = new LogBuffer(); - feedbackLogBuffer.shiftIn(feedbackLogUnit); - publishLogBuffer(feedbackLogBuffer, feedbackLog, true /* isIncludingPrivateData */); - feedbackLog.blockingClose(RESEARCHLOG_CLOSE_TIMEOUT_IN_MS); - uploadNow(); - - if (isIncludingRecording && DEBUG_REPLAY_AFTER_FEEDBACK) { - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - final ReplayData replayData = - mMotionEventReader.readMotionEventData(mUserRecordingFile); - mReplayer.replay(replayData, null); - } - }, TimeUnit.SECONDS.toMillis(1)); - } - - if (FEEDBACK_DIALOG_SHOULD_PRESERVE_TEXT_FIELD) { - // Use feedback string as a channel name to label feedback strings. Here we record the - // string for prepopulating the field next time. - final String channelName = feedbackContents; - if (mPrefs == null) { - return; - } - mPrefs.edit().putString(PREF_RESEARCH_SAVED_CHANNEL, channelName).apply(); - } - } - - public void uploadNow() { - if (DEBUG) { - Log.d(TAG, "calling uploadNow()"); - } - mLatinIME.startService(mUploadNowIntent); - } - - public void onLeavingSendFeedbackDialog() { - mInFeedbackDialog = false; - } - - private void cancelFeedbackDialog() { - if (isMakingUserRecording()) { - cancelRecording(); - } - mInFeedbackDialog = false; - } - - public void initDictionary(final DictionaryFacilitatorForSuggest dictionaryFacilitator) { - mDictionaryFacilitator = dictionaryFacilitator; - // MainLogBuffer now has an out-of-date Suggest object. Close down MainLogBuffer and create - // a new one. - if (mMainLogBuffer != null) { - restart(); - } - } - - private void setIsPasswordView(boolean isPasswordView) { - mIsPasswordView = isPasswordView; - } - - /** - * Returns true if logging is permitted. - * - * This method is called when adding a LogStatement to a LogUnit, and when adding a LogUnit to a - * ResearchLog. It is checked in both places in case conditions change between these times, and - * as a defensive measure in case refactoring changes the logging pipeline. - */ - private boolean isAllowedToLogTo(final ResearchLog researchLog) { - // Logging is never allowed in these circumstances - if (mIsPasswordView) return false; - if (!sIsLogging) return false; - if (mInFeedbackDialog) { - // The FeedbackDialog is up. Normal logging should not happen (the user might be trying - // out things while the dialog is up, and their reporting of an issue may not be - // representative of what they normally type). However, after the user has finished - // entering their feedback, the logger packs their comments and an encoded version of - // any demonstration of the issue into a special "FeedbackLog". So if the FeedbackLog - // is the destination, we do want to allow logging to it. - return researchLog.isFeedbackLog(); - } - // No other exclusions. Logging is permitted. - return true; - } - - public void requestIndicatorRedraw() { - if (!IS_SHOWING_INDICATOR) { - return; - } - if (mMainKeyboardView == null) { - return; - } - mMainKeyboardView.invalidateAllKeys(); - } - - private boolean isReplaying() { - return mReplayer.isReplaying(); - } - - private int getIndicatorColor() { - if (isMakingUserRecording()) { - return Color.YELLOW; - } - if (isReplaying()) { - return Color.GREEN; - } - return Color.RED; - } - - public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width, - int height) { - // TODO: Reimplement using a keyboard background image specific to the ResearchLogger - // and remove this method. - // The check for MainKeyboardView ensures that the indicator only decorates the main - // keyboard, not every keyboard. - if (IS_SHOWING_INDICATOR && (isAllowedToLogTo(mMainResearchLog) || isReplaying()) - && view instanceof MainKeyboardView) { - final int savedColor = paint.getColor(); - paint.setColor(getIndicatorColor()); - final Style savedStyle = paint.getStyle(); - paint.setStyle(Style.STROKE); - final float savedStrokeWidth = paint.getStrokeWidth(); - if (IS_SHOWING_INDICATOR_CLEARLY) { - paint.setStrokeWidth(5); - canvas.drawLine(0, 0, 0, height, paint); - canvas.drawLine(width, 0, width, height, paint); - } else { - // Put a tiny dot on the screen so a knowledgeable user can check whether it is - // enabled. The dot is actually a zero-width, zero-height rectangle, placed at the - // lower-right corner of the canvas, painted with a non-zero border width. - paint.setStrokeWidth(3); - canvas.drawRect(width - 1, height - 1, width, height, paint); - } - paint.setColor(savedColor); - paint.setStyle(savedStyle); - paint.setStrokeWidth(savedStrokeWidth); - } - } - - /** - * Buffer a research log event, flagging it as privacy-sensitive. - */ - private synchronized void enqueueEvent(final LogStatement logStatement, - final Object... values) { - enqueueEvent(mCurrentLogUnit, logStatement, values); - } - - private synchronized void enqueueEvent(final LogUnit logUnit, final LogStatement logStatement, - final Object... values) { - assert values.length == logStatement.getKeys().length; - if (isAllowedToLogTo(mMainResearchLog) && logUnit != null) { - final long time = SystemClock.uptimeMillis(); - logUnit.addLogStatement(logStatement, time, values); - } - } - - private void setCurrentLogUnitContainsDigitFlag() { - mCurrentLogUnit.setMayContainDigit(); - } - - private void setCurrentLogUnitContainsUserDeletions() { - mCurrentLogUnit.setContainsUserDeletions(); - } - - private void setCurrentLogUnitCorrectionType(final int correctionType) { - mCurrentLogUnit.setCorrectionType(correctionType); - } - - /* package for test */ void commitCurrentLogUnit() { - if (DEBUG) { - Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasOneOrMoreWords() ? - ": " + mCurrentLogUnit.getWordsAsString() : "")); - } - if (!mCurrentLogUnit.isEmpty()) { - mMainLogBuffer.shiftIn(mCurrentLogUnit); - if (mUserRecordingLogBuffer != null) { - mUserRecordingLogBuffer.shiftIn(mCurrentLogUnit); - } - mCurrentLogUnit = new LogUnit(); - } else { - if (DEBUG) { - Log.d(TAG, "Warning: tried to commit empty log unit."); - } - } - } - - private static final LogStatement LOGSTATEMENT_UNCOMMIT_CURRENT_LOGUNIT = - new LogStatement("UncommitCurrentLogUnit", false, false); - public void uncommitCurrentLogUnit(final String expectedWord, - final boolean dumpCurrentLogUnit) { - // The user has deleted this word and returned to the previous. Check that the word in the - // logUnit matches the expected word. If so, restore the last log unit committed to be the - // current logUnit. I.e., pull out the last LogUnit from all the LogBuffers, and make - // it the mCurrentLogUnit so the new edits are captured with the word. Optionally dump the - // contents of mCurrentLogUnit (useful if they contain deletions of the next word that - // should not be reported to protect user privacy) - // - // Note that we don't use mLastLogUnit here, because it only goes one word back and is only - // needed for reverts, which only happen one back. - final LogUnit oldLogUnit = mMainLogBuffer.peekLastLogUnit(); - - // Check that expected word matches. It's ok if both strings are null, because this is the - // case where the LogUnit is storing a non-word, e.g. a separator. - if (oldLogUnit != null) { - // Because the word is stored in the LogUnit with digits scrubbed, the comparison must - // be made on a scrubbed version of the expectedWord as well. - final String scrubbedExpectedWord = scrubDigitsFromString(expectedWord); - final String oldLogUnitWords = oldLogUnit.getWordsAsString(); - if (!TextUtils.equals(scrubbedExpectedWord, oldLogUnitWords)) return; - } - - // Uncommit, merging if necessary. - mMainLogBuffer.unshiftIn(); - if (oldLogUnit != null && !dumpCurrentLogUnit) { - oldLogUnit.append(mCurrentLogUnit); - mSavedDownEventTime = Long.MAX_VALUE; - } - if (oldLogUnit == null) { - mCurrentLogUnit = new LogUnit(); - } else { - mCurrentLogUnit = oldLogUnit; - } - enqueueEvent(LOGSTATEMENT_UNCOMMIT_CURRENT_LOGUNIT); - if (DEBUG) { - Log.d(TAG, "uncommitCurrentLogUnit (dump=" + dumpCurrentLogUnit + ") back to " - + (mCurrentLogUnit.hasOneOrMoreWords() ? ": '" - + mCurrentLogUnit.getWordsAsString() + "'" : "")); - } - } - - /** - * Publish all the logUnits in the logBuffer, without doing any privacy filtering. - */ - /* package for test */ void publishLogBuffer(final LogBuffer logBuffer, - final ResearchLog researchLog, final boolean canIncludePrivateData) { - publishLogUnits(logBuffer.getLogUnits(), researchLog, canIncludePrivateData); - } - - private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_OPENING = - new LogStatement("logSegmentStart", false, false, "isIncludingPrivateData"); - private static final LogStatement LOGSTATEMENT_LOG_SEGMENT_CLOSING = - new LogStatement("logSegmentEnd", false, false); - /** - * Publish all LogUnits in a list. - * - * Any privacy checks should be performed before calling this method. - */ - /* package for test */ void publishLogUnits(final List<LogUnit> logUnits, - final ResearchLog researchLog, final boolean canIncludePrivateData) { - final LogUnit openingLogUnit = new LogUnit(); - if (logUnits.isEmpty()) return; - if (!isAllowedToLogTo(researchLog)) return; - // LogUnits not containing private data, such as contextual data for the log, do not require - // logSegment boundary statements. - if (canIncludePrivateData) { - openingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_OPENING, - SystemClock.uptimeMillis(), canIncludePrivateData); - researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */); - } - for (LogUnit logUnit : logUnits) { - if (DEBUG) { - Log.d(TAG, "publishLogBuffer: " + (logUnit.hasOneOrMoreWords() - ? logUnit.getWordsAsString() : "<wordless>") - + ", correction?: " + logUnit.containsUserDeletions()); - } - researchLog.publish(logUnit, canIncludePrivateData); - } - if (canIncludePrivateData) { - final LogUnit closingLogUnit = new LogUnit(); - closingLogUnit.addLogStatement(LOGSTATEMENT_LOG_SEGMENT_CLOSING, - SystemClock.uptimeMillis()); - researchLog.publish(closingLogUnit, true /* isIncludingPrivateData */); - } - } - - public static boolean hasLetters(final String word) { - final int length = word.length(); - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = word.codePointAt(i); - if (Character.isLetter(codePoint)) { - return true; - } - } - return false; - } - - /** - * Commit the portion of mCurrentLogUnit before maxTime as a worded logUnit. - * - * After this operation completes, mCurrentLogUnit will hold any logStatements that happened - * after maxTime. - */ - /* package for test */ void commitCurrentLogUnitAsWord(final String word, final long maxTime, - final boolean isBatchMode) { - if (word == null) { - return; - } - if (word.length() > 0 && hasLetters(word)) { - mCurrentLogUnit.setWords(word); - } - final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime); - enqueueCommitText(word, isBatchMode); - commitCurrentLogUnit(); - mCurrentLogUnit = newLogUnit; - } - - /** - * Record the time of a MotionEvent.ACTION_DOWN. - * - * Warning: Not thread safe. Only call from the main thread. - */ - private void setSavedDownEventTime(final long time) { - mSavedDownEventTime = time; - } - - public void onWordFinished(final String word, final boolean isBatchMode) { - commitCurrentLogUnitAsWord(word, mSavedDownEventTime, isBatchMode); - mSavedDownEventTime = Long.MAX_VALUE; - } - - private static int scrubDigitFromCodePoint(int codePoint) { - return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint; - } - - /* package for test */ static String scrubDigitsFromString(final String s) { - if (s == null) return null; - StringBuilder sb = null; - final int length = s.length(); - for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) { - final int codePoint = Character.codePointAt(s, i); - if (Character.isDigit(codePoint)) { - if (sb == null) { - sb = new StringBuilder(length); - sb.append(s.substring(0, i)); - } - sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT); - } else { - if (sb != null) { - sb.appendCodePoint(codePoint); - } - } - } - if (sb == null) { - return s; - } else { - return sb.toString(); - } - } - - private String scrubWord(String word) { - if (mDictionaryFacilitator != null && mDictionaryFacilitator.isValidMainDictWord(word)) { - return word; - } - return WORD_REPLACEMENT_STRING; - } - - // Specific logging methods follow below. The comments for each logging method should - // indicate what specific method is logged, and how to trigger it from the user interface. - // - // Logging methods can be generally classified into two flavors, "UserAction", which should - // correspond closely to an event that is sensed by the IME, and is usually generated - // directly by the user, and "SystemResponse" which corresponds to an event that the IME - // generates, often after much processing of user input. SystemResponses should correspond - // closely to user-visible events. - // TODO: Consider exposing the UserAction classification in the log output. - - /** - * Log a call to LatinIME.onStartInputViewInternal(). - * - * UserAction: called each time the keyboard is opened up. - */ - private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL = - new LogStatement("LatinImeOnStartInputViewInternal", false, false, "uuid", - "packageName", "inputType", "imeOptions", "fieldId", "display", "model", - "prefs", "versionCode", "versionName", "outputFormatVersion", "logEverything", - "isDevTeamBuild"); - public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo, - final SharedPreferences prefs) { - final ResearchLogger researchLogger = getInstance(); - if (editorInfo != null) { - final boolean isPassword = InputTypeUtils.isPasswordInputType(editorInfo.inputType) - || InputTypeUtils.isVisiblePasswordInputType(editorInfo.inputType); - getInstance().setIsPasswordView(isPassword); - researchLogger.start(); - final Context context = researchLogger.mLatinIME; - try { - final PackageInfo packageInfo; - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), - 0); - final Integer versionCode = packageInfo.versionCode; - final String versionName = packageInfo.versionName; - final String uuid = ResearchSettings.readResearchLoggerUuid(researchLogger.mPrefs); - researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_START_INPUT_VIEW_INTERNAL, - uuid, editorInfo.packageName, Integer.toHexString(editorInfo.inputType), - Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId, - Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName, - OUTPUT_FORMAT_VERSION, IS_LOGGING_EVERYTHING, - researchLogger.isDevTeamBuild()); - // Commit the logUnit so the LatinImeOnStartInputViewInternal event is in its own - // logUnit at the beginning of the log. - researchLogger.commitCurrentLogUnit(); - } catch (final NameNotFoundException e) { - Log.e(TAG, "NameNotFound", e); - } - } - } - - // TODO: Update this heuristic pattern to something more reliable. Developer builds tend to - // have the developer name and year embedded. - private static final Pattern developerBuildRegex = Pattern.compile("[A-Za-z]\\.20[1-9]"); - private boolean isDevTeamBuild() { - try { - final PackageInfo packageInfo; - packageInfo = mLatinIME.getPackageManager().getPackageInfo(mLatinIME.getPackageName(), - 0); - final String versionName = packageInfo.versionName; - return developerBuildRegex.matcher(versionName).find(); - } catch (final NameNotFoundException e) { - Log.e(TAG, "Could not determine package name", e); - return false; - } - } - - /** - * Log a change in preferences. - * - * UserAction: called when the user changes the settings. - */ - private static final LogStatement LOGSTATEMENT_PREFS_CHANGED = - new LogStatement("PrefsChanged", false, false, "prefs"); - public static void prefsChanged(final SharedPreferences prefs) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_PREFS_CHANGED, prefs); - } - - /** - * Log a call to MainKeyboardView.processMotionEvent(). - * - * UserAction: called when the user puts their finger onto the screen (ACTION_DOWN). - * - */ - private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT = - new LogStatement("MotionEvent", true, false, "action", - LogStatement.KEY_IS_LOGGING_RELATED, "motionEvent"); - public static void mainKeyboardView_processMotionEvent(final MotionEvent me) { - if (me == null) { - return; - } - final int action = me.getActionMasked(); - final long eventTime = me.getEventTime(); - final String actionString = LoggingUtils.getMotionEventActionTypeString(action); - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT, - actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me)); - if (action == MotionEvent.ACTION_DOWN) { - // Subtract 1 from eventTime so the down event is included in the later - // LogUnit, not the earlier (the test is for inequality). - researchLogger.setSavedDownEventTime(eventTime - 1); - } - // Refresh the timer in case we are capturing user feedback. - if (researchLogger.isMakingUserRecording()) { - researchLogger.resetRecordingTimer(); - } - } - - /** - * Log a call to LatinIME.onCodeInput(). - * - * SystemResponse: The main processing step for entering text. Called when the user performs a - * tap, a flick, a long press, releases a gesture, or taps a punctuation suggestion. - */ - private static final LogStatement LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT = - new LogStatement("LatinImeOnCodeInput", true, false, "code", "x", "y"); - public static void latinIME_onCodeInput(final int code, final int x, final int y) { - final long time = SystemClock.uptimeMillis(); - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATIN_IME_ON_CODE_INPUT, - Constants.printableCode(scrubDigitFromCodePoint(code)), x, y); - if (Character.isDigit(code)) { - researchLogger.setCurrentLogUnitContainsDigitFlag(); - } - researchLogger.mStatistics.recordChar(code, time); - } - /** - * Log a call to LatinIME.onDisplayCompletions(). - * - * SystemResponse: The IME has displayed application-specific completions. They may show up - * in the suggestion strip, such as a landscape phone. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS = - new LogStatement("LatinIMEOnDisplayCompletions", true, true, - "applicationSpecifiedCompletions"); - public static void latinIME_onDisplayCompletions( - final CompletionInfo[] applicationSpecifiedCompletions) { - // Note; passing an array as a single element in a vararg list. Must create a new - // dummy array around it or it will get expanded. - getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_ONDISPLAYCOMPLETIONS, - new Object[] { applicationSpecifiedCompletions }); - } - - /** - * The IME is finishing; it is either being destroyed, or is about to be hidden. - * - * UserAction: The user has performed an action that has caused the IME to be closed. They may - * have focused on something other than a text field, or explicitly closed it. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL = - new LogStatement("LatinIMEOnFinishInputViewInternal", false, false, "isTextTruncated", - "text"); - public static void latinIME_onFinishInputViewInternal(final boolean finishingInput) { - // The finishingInput flag is set in InputMethodService. It is true if called from - // doFinishInput(), which can be called as part of doStartInput(). This can happen at times - // when the IME is not closing, such as when powering up. The finishinInput flag is false - // if called from finishViews(), which is called from hideWindow() and onDestroy(). These - // are the situations in which we want to finish up the researchLog. - if (!finishingInput) { - final ResearchLogger researchLogger = getInstance(); - // Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g. - // during a live user test), so the normal isPotentiallyPrivate and - // isPotentiallyRevealing flags do not apply - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL, - true /* isTextTruncated */, "" /* text */); - researchLogger.commitCurrentLogUnit(); - getInstance().stop(); - } - } - - /** - * Log a call to LatinIME.onUpdateSelection(). - * - * UserAction/SystemResponse: The user has moved the cursor or selection. This function may - * be called, however, when the system has moved the cursor, say by inserting a character. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_ONUPDATESELECTION = - new LogStatement("LatinIMEOnUpdateSelection", true, false, "lastSelectionStart", - "lastSelectionEnd", "oldSelStart", "oldSelEnd", "newSelStart", "newSelEnd", - "composingSpanStart", "composingSpanEnd", "expectingUpdateSelection", - "expectingUpdateSelectionFromLogger", "context"); - public static void latinIME_onUpdateSelection(final int lastSelectionStart, - final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd, final int composingSpanStart, - final int composingSpanEnd, final RichInputConnection connection) { - String word = ""; - if (connection != null) { - TextRange range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); - if (range != null) { - word = range.mWord.toString(); - } - } - final ResearchLogger researchLogger = getInstance(); - final String scrubbedWord = researchLogger.scrubWord(word); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONUPDATESELECTION, lastSelectionStart, - lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, - composingSpanStart, composingSpanEnd, false /* expectingUpdateSelection */, - false /* expectingUpdateSelectionFromLogger */, scrubbedWord); - } - - /** - * Log a call to LatinIME.onTextInput(). - * - * SystemResponse: Raw text is added to the TextView. - */ - public static void latinIME_onTextInput(final String text, final boolean isBatchMode) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE, isBatchMode); - } - - /** - * Log a revert of onTextInput() (known in the IME as "EnteredText"). - * - * SystemResponse: Remove the LogUnit recording the textInput - */ - public static void latinIME_handleBackspace_cancelTextInput(final String text) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.uncommitCurrentLogUnit(text, true /* dumpCurrentLogUnit */); - } - - /** - * Log a call to LatinIME.pickSuggestionManually(). - * - * UserAction: The user has chosen a specific word from the suggestion strip. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY = - new LogStatement("LatinIMEPickSuggestionManually", true, false, "replacedWord", "index", - "suggestion", "x", "y", "isBatchMode", "score", "kind", "sourceDict"); - /** - * Log a call to LatinIME.pickSuggestionManually(). - * - * @param replacedWord the typed word that this manual suggestion replaces. May not be null. - * @param index the index in the suggestion strip - * @param suggestion the committed suggestion. May not be null. - * @param isBatchMode whether this was input in batch mode, aka gesture. - * @param score the internal score of the suggestion, as output by the dictionary - * @param kind the kind of suggestion, as one of the SuggestedWordInfo#KIND_* constants - * @param sourceDict the source origin of this word, as one of the Dictionary#TYPE_* constants. - */ - public static void latinIME_pickSuggestionManually(final String replacedWord, - final int index, final String suggestion, final boolean isBatchMode, - final int score, final int kind, final String sourceDict) { - final ResearchLogger researchLogger = getInstance(); - // Note : suggestion can't be null here, because it's only called in a place where it - // can't be null. - if (!replacedWord.equals(suggestion.toString())) { - // The user chose something other than what was already there. - researchLogger.setCurrentLogUnitContainsUserDeletions(); - researchLogger.setCurrentLogUnitCorrectionType(LogUnit.CORRECTIONTYPE_TYPO); - } - final String scrubbedWord = scrubDigitsFromString(suggestion); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY, - scrubDigitsFromString(replacedWord), index, - scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE, - Constants.SUGGESTION_STRIP_COORDINATE, isBatchMode, score, kind, sourceDict); - researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE, isBatchMode); - researchLogger.mStatistics.recordManualSuggestion(SystemClock.uptimeMillis()); - } - - /** - * Log a call to LatinIME.punctuationSuggestion(). - * - * UserAction: The user has chosen punctuation from the punctuation suggestion strip. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION = - new LogStatement("LatinIMEPunctuationSuggestion", false, false, "index", "suggestion", - "x", "y", "isPrediction"); - public static void latinIME_punctuationSuggestion(final int index, final String suggestion, - final boolean isBatchMode, final boolean isPrediction) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion, - Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, - isPrediction); - researchLogger.commitCurrentLogUnitAsWord(suggestion, Long.MAX_VALUE, isBatchMode); - } - - /** - * Log a call to LatinIME.sendKeyCodePoint(). - * - * SystemResponse: The IME is inserting text into the TextView for non-word-constituent, - * strings (separators, numbers, other symbols). - */ - private static final LogStatement LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT = - new LogStatement("LatinIMESendKeyCodePoint", true, false, "code"); - public static void latinIME_sendKeyCodePoint(final int code) { - final ResearchLogger researchLogger = getInstance(); - final LogUnit phantomSpaceLogUnit = researchLogger.mPhantomSpaceLogUnit; - if (phantomSpaceLogUnit == null) { - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT, - Constants.printableCode(scrubDigitFromCodePoint(code))); - if (Character.isDigit(code)) { - researchLogger.setCurrentLogUnitContainsDigitFlag(); - } - researchLogger.commitCurrentLogUnit(); - } else { - researchLogger.enqueueEvent(phantomSpaceLogUnit, LOGSTATEMENT_LATINIME_SENDKEYCODEPOINT, - Constants.printableCode(scrubDigitFromCodePoint(code))); - if (Character.isDigit(code)) { - phantomSpaceLogUnit.setMayContainDigit(); - } - researchLogger.mMainLogBuffer.shiftIn(phantomSpaceLogUnit); - if (researchLogger.mUserRecordingLogBuffer != null) { - researchLogger.mUserRecordingLogBuffer.shiftIn(phantomSpaceLogUnit); - } - researchLogger.mPhantomSpaceLogUnit = null; - } - } - - /** - * Log a call to LatinIME.promotePhantomSpace(). - * - * SystemResponse: The IME is inserting a real space in place of a phantom space. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE = - new LogStatement("LatinIMEPromotePhantomSpace", false, false); - public static void latinIME_promotePhantomSpace() { - // A phantom space is always added before the text that triggered it. The triggering text - // and the events that created it will be in mCurrentLogUnit, but the phantom space should - // be in its own LogUnit, committed before the triggering text. Although it is created - // here, it is not added to the LogBuffer until the following call to - // latinIME_sendKeyCodePoint, because SENDKEYCODEPOINT LogStatement also must go into that - // LogUnit. - final ResearchLogger researchLogger = getInstance(); - researchLogger.mPhantomSpaceLogUnit = new LogUnit(); - researchLogger.enqueueEvent(researchLogger.mPhantomSpaceLogUnit, - LOGSTATEMENT_LATINIME_PROMOTEPHANTOMSPACE); - } - - /** - * Log a call to LatinIME.swapSwapperAndSpace(). - * - * SystemResponse: A symbol has been swapped with a space character. E.g. punctuation may swap - * if a soft space is inserted after a word. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE = - new LogStatement("LatinIMESwapSwapperAndSpace", false, false, "originalCharacters", - "charactersAfterSwap"); - public static void latinIME_swapSwapperAndSpace(final CharSequence originalCharacters, - final String charactersAfterSwap) { - final ResearchLogger researchLogger = getInstance(); - final LogUnit logUnit; - logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - if (logUnit != null) { - researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_SWAPSWAPPERANDSPACE, - originalCharacters, charactersAfterSwap); - } - } - - /** - * Log a call to LatinIME.maybeDoubleSpacePeriod(). - * - * SystemResponse: Two spaces have been replaced by period space. - */ - public static void latinIME_maybeDoubleSpacePeriod(final String text, - final boolean isBatchMode) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.commitCurrentLogUnitAsWord(text, Long.MAX_VALUE, isBatchMode); - } - - /** - * Log a call to MainKeyboardView.onLongPress(). - * - * UserAction: The user has performed a long-press on a key. - */ - private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS = - new LogStatement("MainKeyboardViewOnLongPress", false, false); - public static void mainKeyboardView_onLongPress() { - getInstance().enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_ONLONGPRESS); - } - - /** - * Log a call to MainKeyboardView.setKeyboard(). - * - * SystemResponse: The IME has switched to a new keyboard (e.g. French, English). - * This is typically called right after LatinIME.onStartInputViewInternal (when starting a new - * IME), but may happen at other times if the user explicitly requests a keyboard change. - */ - private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD = - new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale", - "orientation", "width", "modeName", "action", "navigateNext", - "navigatePrevious", "clobberSettingsKey", "passwordInput", - "supportsSwitchingToShortcutIme", "hasShortcutKey", "languageSwitchKeyEnabled", - "isMultiLine", "tw", "th", - "keys"); - public static void mainKeyboardView_setKeyboard(final Keyboard keyboard, - final int orientation) { - final KeyboardId kid = keyboard.mId; - final boolean isPasswordView = kid.passwordInput(); - final ResearchLogger researchLogger = getInstance(); - researchLogger.setIsPasswordView(isPasswordView); - researchLogger.enqueueEvent(LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD, - KeyboardId.elementIdToName(kid.mElementId), - kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), - orientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(), - kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey, - isPasswordView, kid.mSupportsSwitchingToShortcutIme, kid.mHasShortcutKey, - kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth, - keyboard.mOccupiedHeight, keyboard.getSortedKeys()); - } - - /** - * Log a call to LatinIME.revertCommit(). - * - * SystemResponse: The IME has reverted commited text. This happens when the user enters - * a word, commits it by pressing space or punctuation, and then reverts the commit by hitting - * backspace. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_REVERTCOMMIT = - new LogStatement("LatinIMERevertCommit", true, false, "committedWord", - "originallyTypedWord", "separatorString"); - public static void latinIME_revertCommit(final String committedWord, - final String originallyTypedWord, final boolean isBatchMode, - final String separatorString) { - // TODO: Prioritize adding a unit test for this method (as it is especially complex) - // TODO: Update the UserRecording LogBuffer as well as the MainLogBuffer - final ResearchLogger researchLogger = getInstance(); - // - // 1. Remove separator LogUnit - final LogUnit lastLogUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - // Check that we're not at the beginning of input - if (lastLogUnit == null) return; - // Check that we're after a separator - if (lastLogUnit.getWordsAsString() != null) return; - // Remove separator - final LogUnit separatorLogUnit = researchLogger.mMainLogBuffer.unshiftIn(); - - // 2. Add revert LogStatement - final LogUnit revertedLogUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - if (revertedLogUnit == null) return; - if (!revertedLogUnit.getWordsAsString().equals(scrubDigitsFromString(committedWord))) { - // Any word associated with the reverted LogUnit has already had its digits scrubbed, so - // any digits in the committedWord argument must also be scrubbed for an accurate - // comparison. - return; - } - researchLogger.enqueueEvent(revertedLogUnit, LOGSTATEMENT_LATINIME_REVERTCOMMIT, - committedWord, originallyTypedWord, separatorString); - - // 3. Update the word associated with the LogUnit - revertedLogUnit.setWords(originallyTypedWord); - revertedLogUnit.setContainsUserDeletions(); - - // 4. Re-add the separator LogUnit - researchLogger.mMainLogBuffer.shiftIn(separatorLogUnit); - - // 5. Record stats - researchLogger.mStatistics.recordRevertCommit(SystemClock.uptimeMillis()); - } - - /** - * Log a call to PointerTracker.callListenerOnCancelInput(). - * - * UserAction: The user has canceled the input, e.g., by pressing down, but then removing - * outside the keyboard area. - * TODO: Verify - */ - private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT = - new LogStatement("PointerTrackerCallListenerOnCancelInput", false, false); - public static void pointerTracker_callListenerOnCancelInput() { - getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCANCELINPUT); - } - - /** - * Log a call to PointerTracker.callListenerOnCodeInput(). - * - * SystemResponse: The user has entered a key through the normal tapping mechanism. - * LatinIME.onCodeInput will also be called. - */ - private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT = - new LogStatement("PointerTrackerCallListenerOnCodeInput", true, false, "code", - "outputText", "x", "y", "ignoreModifierKey", "altersCode", "isEnabled"); - public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x, - final int y, final boolean ignoreModifierKey, final boolean altersCode, - final int code) { - if (key != null) { - String outputText = key.getOutputText(); - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONCODEINPUT, - Constants.printableCode(scrubDigitFromCodePoint(code)), - outputText == null ? null : scrubDigitsFromString(outputText.toString()), - x, y, ignoreModifierKey, altersCode, key.isEnabled()); - } - } - - /** - * Log a call to PointerTracker.callListenerCallListenerOnRelease(). - * - * UserAction: The user has released their finger or thumb from the screen. - */ - private static final LogStatement LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE = - new LogStatement("PointerTrackerCallListenerOnRelease", true, false, "code", - "withSliding", "ignoreModifierKey", "isEnabled"); - public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode, - final boolean withSliding, final boolean ignoreModifierKey) { - if (key != null) { - getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_CALLLISTENERONRELEASE, - Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding, - ignoreModifierKey, key.isEnabled()); - } - } - - /** - * Log a call to PointerTracker.onDownEvent(). - * - * UserAction: The user has pressed down on a key. - * TODO: Differentiate with LatinIME.processMotionEvent. - */ - private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT = - new LogStatement("PointerTrackerOnDownEvent", true, false, "deltaT", "distanceSquared"); - public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) { - getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONDOWNEVENT, deltaT, - distanceSquared); - } - - /** - * Log a call to PointerTracker.onMoveEvent(). - * - * UserAction: The user has moved their finger while pressing on the screen. - * TODO: Differentiate with LatinIME.processMotionEvent(). - */ - private static final LogStatement LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT = - new LogStatement("PointerTrackerOnMoveEvent", true, false, "x", "y", "lastX", "lastY"); - public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX, - final int lastY) { - getInstance().enqueueEvent(LOGSTATEMENT_POINTERTRACKER_ONMOVEEVENT, x, y, lastX, lastY); - } - - /** - * Log a call to RichInputConnection.commitCompletion(). - * - * SystemResponse: The IME has committed a completion. A completion is an application- - * specific suggestion that is presented in a pop-up menu in the TextView. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION = - new LogStatement("RichInputConnectionCommitCompletion", true, false, "completionInfo"); - public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_COMMITCOMPLETION, - completionInfo); - } - - /** - * Log a call to RichInputConnection.revertDoubleSpacePeriod(). - * - * SystemResponse: The IME has reverted ". ", which had previously replaced two typed spaces. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_REVERTDOUBLESPACEPERIOD = - new LogStatement("RichInputConnectionRevertDoubleSpacePeriod", false, false); - public static void richInputConnection_revertDoubleSpacePeriod() { - final ResearchLogger researchLogger = getInstance(); - // An extra LogUnit is added for the period; this is removed here because of the revert. - researchLogger.uncommitCurrentLogUnit(null, true /* dumpCurrentLogUnit */); - // TODO: This will probably be lost as the user backspaces further. Figure out how to put - // it into the right logUnit. - researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_REVERTDOUBLESPACEPERIOD); - } - - /** - * Log a call to RichInputConnection.revertSwapPunctuation(). - * - * SystemResponse: The IME has reverted a punctuation swap. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_REVERTSWAPPUNCTUATION = - new LogStatement("RichInputConnectionRevertSwapPunctuation", false, false); - public static void richInputConnection_revertSwapPunctuation() { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_REVERTSWAPPUNCTUATION); - } - - /** - * Log a call to LatinIME.commitCurrentAutoCorrection(). - * - * SystemResponse: The IME has committed an auto-correction. An auto-correction changes the raw - * text input to another word (or words) that the user more likely desired to type. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION = - new LogStatement("LatinIMECommitCurrentAutoCorrection", true, true, "typedWord", - "autoCorrection", "separatorString"); - public static void latinIme_commitCurrentAutoCorrection(final String typedWord, - final String autoCorrection, final String separatorString, final boolean isBatchMode, - final SuggestedWords suggestedWords) { - final String scrubbedTypedWord = scrubDigitsFromString(typedWord); - final String scrubbedAutoCorrection = scrubDigitsFromString(autoCorrection); - final ResearchLogger researchLogger = getInstance(); - researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords); - researchLogger.onWordFinished(scrubbedAutoCorrection, isBatchMode); - - // Add the autocorrection logStatement at the end of the logUnit for the committed word. - // We have to do this after calling commitCurrentLogUnitAsWord, because it may split the - // current logUnit, and then we have to peek to get the logUnit reference back. - final LogUnit logUnit = researchLogger.mMainLogBuffer.peekLastLogUnit(); - // TODO: Add test to confirm that the commitCurrentAutoCorrection log statement should - // always be added to logUnit (if non-null) and not mCurrentLogUnit. - researchLogger.enqueueEvent(logUnit, LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION, - scrubbedTypedWord, scrubbedAutoCorrection, separatorString); - } - - private boolean isExpectingCommitText = false; - - /** - * Log a call to RichInputConnection.commitText(). - * - * SystemResponse: The IME is committing text. This happens after the user has typed a word - * and then a space or punctuation key. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT = - new LogStatement("RichInputConnectionCommitText", true, false, "newCursorPosition"); - public static void richInputConnection_commitText(final String committedWord, - final int newCursorPosition, final boolean isBatchMode) { - final ResearchLogger researchLogger = getInstance(); - // Only include opening and closing logSegments if private data is included - final String scrubbedWord = scrubDigitsFromString(committedWord); - if (!researchLogger.isExpectingCommitText) { - researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT, - newCursorPosition); - researchLogger.commitCurrentLogUnitAsWord(scrubbedWord, Long.MAX_VALUE, isBatchMode); - } - researchLogger.isExpectingCommitText = false; - } - - /** - * Shared events for logging committed text. - * - * The "CommitTextEventHappened" LogStatement is written to the log even if privacy rules - * indicate that the word contents should not be logged. It has no contents, and only serves to - * record the event and thereby make it easier to calculate word-level statistics even when the - * word contents are unknown. - */ - private static final LogStatement LOGSTATEMENT_COMMITTEXT = - new LogStatement("CommitText", true /* isPotentiallyPrivate */, - false /* isPotentiallyRevealing */, "committedText", "isBatchMode"); - private static final LogStatement LOGSTATEMENT_COMMITTEXT_EVENT_HAPPENED = - new LogStatement("CommitTextEventHappened", false /* isPotentiallyPrivate */, - false /* isPotentiallyRevealing */); - private void enqueueCommitText(final String word, final boolean isBatchMode) { - // Event containing the word; will be published only if privacy checks pass - enqueueEvent(LOGSTATEMENT_COMMITTEXT, word, isBatchMode); - // Event not containing the word; will always be published - enqueueEvent(LOGSTATEMENT_COMMITTEXT_EVENT_HAPPENED); - } - - /** - * Log a call to RichInputConnection.deleteSurroundingText(). - * - * SystemResponse: The IME has deleted text. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = - new LogStatement("RichInputConnectionDeleteSurroundingText", true, false, - "beforeLength", "afterLength"); - public static void richInputConnection_deleteSurroundingText(final int beforeLength, - final int afterLength) { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, - beforeLength, afterLength); - } - - /** - * Log a call to RichInputConnection.finishComposingText(). - * - * SystemResponse: The IME has left the composing text as-is. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = - new LogStatement("RichInputConnectionFinishComposingText", false, false); - public static void richInputConnection_finishComposingText() { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT); - } - - /** - * Log a call to RichInputConnection.performEditorAction(). - * - * SystemResponse: The IME is invoking an action specific to the editor. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION = - new LogStatement("RichInputConnectionPerformEditorAction", false, false, - "imeActionId"); - public static void richInputConnection_performEditorAction(final int imeActionId) { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_PERFORMEDITORACTION, - imeActionId); - } - - /** - * Log a call to RichInputConnection.sendKeyEvent(). - * - * SystemResponse: The IME is telling the TextView that a key is being pressed through an - * alternate channel. - * TODO: only for hardware keys? - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT = - new LogStatement("RichInputConnectionSendKeyEvent", true, false, "eventTime", "action", - "code"); - public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SENDKEYEVENT, - keyEvent.getEventTime(), keyEvent.getAction(), keyEvent.getKeyCode()); - } - - /** - * Log a call to RichInputConnection.setComposingText(). - * - * SystemResponse: The IME is setting the composing text. Happens each time a character is - * entered. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = - new LogStatement("RichInputConnectionSetComposingText", true, true, "text", - "newCursorPosition"); - public static void richInputConnection_setComposingText(final CharSequence text, - final int newCursorPosition) { - if (text == null) { - throw new RuntimeException("setComposingText is null"); - } - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETCOMPOSINGTEXT, text, - newCursorPosition); - } - - /** - * Log a call to RichInputConnection.setSelection(). - * - * SystemResponse: The IME is requesting that the selection change. User-initiated selection- - * change requests do not go through this method -- it's only when the system wants to change - * the selection. - */ - private static final LogStatement LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION = - new LogStatement("RichInputConnectionSetSelection", true, false, "from", "to"); - public static void richInputConnection_setSelection(final int from, final int to) { - getInstance().enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTION_SETSELECTION, from, to); - } - - /** - * Log a call to SuddenJumpingTouchEventHandler.onTouchEvent(). - * - * SystemResponse: The IME has filtered input events in case of an erroneous sensor reading. - */ - private static final LogStatement LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = - new LogStatement("SuddenJumpingTouchEventHandlerOnTouchEvent", true, false, - "motionEvent"); - public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) { - if (me != null) { - getInstance().enqueueEvent(LOGSTATEMENT_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, - MotionEvent.obtain(me)); - } - } - - /** - * Log a call to SuggestionsView.setSuggestions(). - * - * SystemResponse: The IME is setting the suggestions in the suggestion strip. - */ - private static final LogStatement LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS = - new LogStatement("SuggestionStripViewSetSuggestions", true, true, "suggestedWords"); - public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) { - if (suggestedWords != null) { - getInstance().enqueueEvent(LOGSTATEMENT_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS, - suggestedWords); - } - } - - /** - * The user has indicated a particular point in the log that is of interest. - * - * UserAction: From direct menu invocation. - */ - private static final LogStatement LOGSTATEMENT_USER_TIMESTAMP = - new LogStatement("UserTimestamp", false, false); - public void userTimestamp() { - getInstance().enqueueEvent(LOGSTATEMENT_USER_TIMESTAMP); - } - - /** - * Log a call to LatinIME.onEndBatchInput(). - * - * SystemResponse: The system has completed a gesture. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_ONENDBATCHINPUT = - new LogStatement("LatinIMEOnEndBatchInput", true, false, "enteredText", - "enteredWordPos", "suggestedWords"); - public static void latinIME_onEndBatchInput(final CharSequence enteredText, - final int enteredWordPos, final SuggestedWords suggestedWords) { - final ResearchLogger researchLogger = getInstance(); - if (!TextUtils.isEmpty(enteredText) && hasLetters(enteredText.toString())) { - researchLogger.mCurrentLogUnit.setWords(enteredText.toString()); - } - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText, - enteredWordPos, suggestedWords); - researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords); - researchLogger.mStatistics.recordGestureInput(enteredText.length(), - SystemClock.uptimeMillis()); - } - - private static final LogStatement LOGSTATEMENT_LATINIME_HANDLEBACKSPACE = - new LogStatement("LatinIMEHandleBackspace", true, false, "numCharacters"); - /** - * Log a call to LatinIME.handleBackspace() that is not a batch delete. - * - * UserInput: The user is deleting one or more characters by hitting the backspace key once. - * The covers single character deletes as well as deleting selections. - * - * @param numCharacters how many characters the backspace operation deleted - * @param shouldUncommitLogUnit whether to uncommit the last {@code LogUnit} in the - * {@code LogBuffer} - */ - public static void latinIME_handleBackspace(final int numCharacters, - final boolean shouldUncommitLogUnit) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_HANDLEBACKSPACE, numCharacters); - if (shouldUncommitLogUnit) { - ResearchLogger.getInstance().uncommitCurrentLogUnit( - null, true /* dumpCurrentLogUnit */); - } - } - - /** - * Log a call to LatinIME.handleBackspace() that is a batch delete. - * - * UserInput: The user is deleting a gestured word by hitting the backspace key once. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH = - new LogStatement("LatinIMEHandleBackspaceBatch", true, false, "deletedText", - "numCharacters"); - public static void latinIME_handleBackspace_batch(final CharSequence deletedText, - final int numCharacters) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_HANDLEBACKSPACE_BATCH, deletedText, - numCharacters); - researchLogger.mStatistics.recordGestureDelete(deletedText.length(), - SystemClock.uptimeMillis()); - researchLogger.uncommitCurrentLogUnit(deletedText.toString(), - false /* dumpCurrentLogUnit */); - } - - /** - * Log a long interval between user operation. - * - * UserInput: The user has not done anything for a while. - */ - private static final LogStatement LOGSTATEMENT_ONUSERPAUSE = new LogStatement("OnUserPause", - false, false, "intervalInMs"); - public static void onUserPause(final long interval) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_ONUSERPAUSE, interval); - } - - /** - * Record the current time in case the LogUnit is later split. - * - * If the current logUnit is split, then tapping, motion events, etc. before this time should - * be assigned to one LogUnit, and events after this time should go into the following LogUnit. - */ - public static void recordTimeForLogUnitSplit() { - final ResearchLogger researchLogger = getInstance(); - researchLogger.setSavedDownEventTime(SystemClock.uptimeMillis()); - researchLogger.mSavedDownEventTime = Long.MAX_VALUE; - } - - /** - * Log a call to LatinIME.handleSeparator() - * - * SystemResponse: The system is inserting a separator character, possibly performing auto- - * correction or other actions appropriate at the end of a word. - */ - private static final LogStatement LOGSTATEMENT_LATINIME_HANDLESEPARATOR = - new LogStatement("LatinIMEHandleSeparator", false, false, "primaryCode", - "isComposingWord"); - public static void latinIME_handleSeparator(final int primaryCode, - final boolean isComposingWord) { - final ResearchLogger researchLogger = getInstance(); - researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_HANDLESEPARATOR, primaryCode, - isComposingWord); - } - - /** - * Call this method when the logging system has attempted publication of an n-gram. - * - * Statistics are gathered about the success or failure. - * - * @param publishabilityResultCode a result code as defined by - * {@code MainLogBuffer.PUBLISHABILITY_*} - */ - static void recordPublishabilityResultCode(final int publishabilityResultCode) { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - statistics.recordPublishabilityResultCode(publishabilityResultCode); - } - - /** - * Log statistics. - * - * ContextualData, recorded at the end of a session. - */ - private static final LogStatement LOGSTATEMENT_STATISTICS = - new LogStatement("Statistics", false, false, "charCount", "letterCount", "numberCount", - "spaceCount", "deleteOpsCount", "wordCount", "isEmptyUponStarting", - "isEmptinessStateKnown", "averageTimeBetweenKeys", "averageTimeBeforeDelete", - "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete", - "dictionaryWordCount", "splitWordsCount", "gestureInputCount", - "gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount", - "revertCommitsCount", "correctedWordsCount", "autoCorrectionsCount", - "publishableCount", "unpublishableStoppingCount", - "unpublishableIncorrectWordCount", "unpublishableSampledTooRecentlyCount", - "unpublishableDictionaryUnavailableCount", "unpublishableMayContainDigitCount", - "unpublishableNotInDictionaryCount"); - private static void logStatistics() { - final ResearchLogger researchLogger = getInstance(); - final Statistics statistics = researchLogger.mStatistics; - researchLogger.enqueueEvent(LOGSTATEMENT_STATISTICS, statistics.mCharCount, - statistics.mLetterCount, statistics.mNumberCount, statistics.mSpaceCount, - statistics.mDeleteKeyCount, statistics.mWordCount, statistics.mIsEmptyUponStarting, - statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(), - statistics.mBeforeDeleteKeyCounter.getAverageTime(), - statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(), - statistics.mAfterDeleteKeyCounter.getAverageTime(), - statistics.mDictionaryWordCount, statistics.mSplitWordsCount, - statistics.mGesturesInputCount, statistics.mGesturesCharsCount, - statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount, - statistics.mRevertCommitsCount, statistics.mCorrectedWordsCount, - statistics.mAutoCorrectionsCount, statistics.mPublishableCount, - statistics.mUnpublishableStoppingCount, statistics.mUnpublishableIncorrectWordCount, - statistics.mUnpublishableSampledTooRecently, - statistics.mUnpublishableDictionaryUnavailable, - statistics.mUnpublishableMayContainDigit, statistics.mUnpublishableNotInDictionary); - } -} diff --git a/java/src/com/android/inputmethod/research/ResearchSettings.java b/java/src/com/android/inputmethod/research/ResearchSettings.java deleted file mode 100644 index c0bc03fde..000000000 --- a/java/src/com/android/inputmethod/research/ResearchSettings.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.content.SharedPreferences; - -import java.util.UUID; - -public final class ResearchSettings { - public static final String PREF_RESEARCH_LOGGER_UUID = "pref_research_logger_uuid"; - public static final String PREF_RESEARCH_LOGGER_ENABLED_FLAG = - "pref_research_logger_enabled_flag"; - public static final String PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH = - "pref_research_logger_has_seen_splash"; - public static final String PREF_RESEARCH_LAST_DIR_CLEANUP_TIME = - "pref_research_last_dir_cleanup_time"; - - private ResearchSettings() { - // Intentional empty constructor for singleton. - } - - public static String readResearchLoggerUuid(final SharedPreferences prefs) { - if (prefs.contains(PREF_RESEARCH_LOGGER_UUID)) { - return prefs.getString(PREF_RESEARCH_LOGGER_UUID, null); - } - // Generate a random string as uuid if not yet set - final String newUuid = UUID.randomUUID().toString(); - prefs.edit().putString(PREF_RESEARCH_LOGGER_UUID, newUuid).apply(); - return newUuid; - } - - public static boolean readResearchLoggerEnabledFlag(final SharedPreferences prefs) { - return prefs.getBoolean(PREF_RESEARCH_LOGGER_ENABLED_FLAG, false); - } - - public static void writeResearchLoggerEnabledFlag(final SharedPreferences prefs, - final boolean isEnabled) { - prefs.edit().putBoolean(PREF_RESEARCH_LOGGER_ENABLED_FLAG, isEnabled).apply(); - } - - public static boolean readHasSeenSplash(final SharedPreferences prefs) { - return prefs.getBoolean(PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH, false); - } - - public static void writeHasSeenSplash(final SharedPreferences prefs, - final boolean hasSeenSplash) { - prefs.edit().putBoolean(PREF_RESEARCH_LOGGER_HAS_SEEN_SPLASH, hasSeenSplash).apply(); - } - - public static long readResearchLastDirCleanupTime(final SharedPreferences prefs) { - return prefs.getLong(PREF_RESEARCH_LAST_DIR_CLEANUP_TIME, 0L); - } - - public static void writeResearchLastDirCleanupTime(final SharedPreferences prefs, - final long lastDirCleanupTime) { - prefs.edit().putLong(PREF_RESEARCH_LAST_DIR_CLEANUP_TIME, lastDirCleanupTime).apply(); - } -} diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java deleted file mode 100644 index fd323a104..000000000 --- a/java/src/com/android/inputmethod/research/Statistics.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.util.Log; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.util.concurrent.TimeUnit; - -public class Statistics { - private static final String TAG = Statistics.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - - // TODO: Cleanup comments to only including those giving meaningful information. - // Number of characters entered during a typing session - int mCharCount; - // Number of letter characters entered during a typing session - int mLetterCount; - // Number of number characters entered - int mNumberCount; - // Number of space characters entered - int mSpaceCount; - // Number of delete operations entered (taps on the backspace key) - int mDeleteKeyCount; - // Number of words entered during a session. - int mWordCount; - // Number of words found in the dictionary. - int mDictionaryWordCount; - // Number of words split and spaces automatically entered. - int mSplitWordsCount; - // Number of words entered during a session. - int mCorrectedWordsCount; - // Number of gestures that were input. - int mGesturesInputCount; - // Number of gestures that were deleted. - int mGesturesDeletedCount; - // Total number of characters in words entered by gesture. - int mGesturesCharsCount; - // Number of manual suggestions chosen. - int mManualSuggestionsCount; - // Number of times that autocorrection was invoked. - int mAutoCorrectionsCount; - // Number of times a commit was reverted in this session. - int mRevertCommitsCount; - // Whether the text field was empty upon editing - boolean mIsEmptyUponStarting; - boolean mIsEmptinessStateKnown; - - // Counts of how often an n-gram is collected or not, and the reasons for the decision. - // Keep consistent with publishability result code list in MainLogBuffer - int mPublishableCount; - int mUnpublishableStoppingCount; - int mUnpublishableIncorrectWordCount; - int mUnpublishableSampledTooRecently; - int mUnpublishableDictionaryUnavailable; - int mUnpublishableMayContainDigit; - int mUnpublishableNotInDictionary; - - // Timers to count average time to enter a key, first press a delete key, - // between delete keys, and then to return typing after a delete key. - final AverageTimeCounter mKeyCounter = new AverageTimeCounter(); - final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter(); - final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter(); - final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter(); - - static class AverageTimeCounter { - int mCount; - int mTotalTime; - - public void reset() { - mCount = 0; - mTotalTime = 0; - } - - public void add(long deltaTime) { - mCount++; - mTotalTime += deltaTime; - } - - public int getAverageTime() { - if (mCount == 0) { - return 0; - } - return mTotalTime / mCount; - } - } - - // To account for the interruptions when the user's attention is directed elsewhere, times - // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic. - public static final long MIN_TYPING_INTERMISSION = TimeUnit.SECONDS.toMillis(2); - public static final long MIN_DELETION_INTERMISSION = TimeUnit.SECONDS.toMillis(10); - - // The last time that a tap was performed - private long mLastTapTime; - // The type of the last keypress (delete key or not) - boolean mIsLastKeyDeleteKey; - - private static final Statistics sInstance = new Statistics(); - - public static Statistics getInstance() { - return sInstance; - } - - private Statistics() { - reset(); - } - - public void reset() { - mCharCount = 0; - mLetterCount = 0; - mNumberCount = 0; - mSpaceCount = 0; - mDeleteKeyCount = 0; - mWordCount = 0; - mDictionaryWordCount = 0; - mSplitWordsCount = 0; - mCorrectedWordsCount = 0; - mGesturesInputCount = 0; - mGesturesDeletedCount = 0; - mManualSuggestionsCount = 0; - mRevertCommitsCount = 0; - mAutoCorrectionsCount = 0; - mIsEmptyUponStarting = true; - mIsEmptinessStateKnown = false; - mKeyCounter.reset(); - mBeforeDeleteKeyCounter.reset(); - mDuringRepeatedDeleteKeysCounter.reset(); - mAfterDeleteKeyCounter.reset(); - mGesturesCharsCount = 0; - mGesturesDeletedCount = 0; - mPublishableCount = 0; - mUnpublishableStoppingCount = 0; - mUnpublishableIncorrectWordCount = 0; - mUnpublishableSampledTooRecently = 0; - mUnpublishableDictionaryUnavailable = 0; - mUnpublishableMayContainDigit = 0; - mUnpublishableNotInDictionary = 0; - - mLastTapTime = 0; - mIsLastKeyDeleteKey = false; - } - - public void recordChar(int codePoint, long time) { - if (DEBUG) { - Log.d(TAG, "recordChar() called"); - } - if (codePoint == Constants.CODE_DELETE) { - mDeleteKeyCount++; - recordUserAction(time, true /* isDeletion */); - } else { - mCharCount++; - if (Character.isDigit(codePoint)) { - mNumberCount++; - } - if (Character.isLetter(codePoint)) { - mLetterCount++; - } - if (Character.isSpaceChar(codePoint)) { - mSpaceCount++; - } - recordUserAction(time, false /* isDeletion */); - } - } - - public void recordWordEntered(final boolean isDictionaryWord, - final boolean containsCorrection) { - mWordCount++; - if (isDictionaryWord) { - mDictionaryWordCount++; - } - if (containsCorrection) { - mCorrectedWordsCount++; - } - } - - public void recordSplitWords() { - mSplitWordsCount++; - } - - public void recordGestureInput(final int numCharsEntered, final long time) { - mGesturesInputCount++; - mGesturesCharsCount += numCharsEntered; - recordUserAction(time, false /* isDeletion */); - } - - public void setIsEmptyUponStarting(final boolean isEmpty) { - mIsEmptyUponStarting = isEmpty; - mIsEmptinessStateKnown = true; - } - - public void recordGestureDelete(final int length, final long time) { - mGesturesDeletedCount++; - recordUserAction(time, true /* isDeletion */); - } - - public void recordManualSuggestion(final long time) { - mManualSuggestionsCount++; - recordUserAction(time, false /* isDeletion */); - } - - public void recordAutoCorrection(final long time) { - mAutoCorrectionsCount++; - recordUserAction(time, false /* isDeletion */); - } - - public void recordRevertCommit(final long time) { - mRevertCommitsCount++; - recordUserAction(time, true /* isDeletion */); - } - - private void recordUserAction(final long time, final boolean isDeletion) { - final long delta = time - mLastTapTime; - if (isDeletion) { - if (delta < MIN_DELETION_INTERMISSION) { - if (mIsLastKeyDeleteKey) { - mDuringRepeatedDeleteKeysCounter.add(delta); - } else { - mBeforeDeleteKeyCounter.add(delta); - } - } else { - ResearchLogger.onUserPause(delta); - } - } else { - if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) { - mAfterDeleteKeyCounter.add(delta); - } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) { - mKeyCounter.add(delta); - } else { - ResearchLogger.onUserPause(delta); - } - } - mIsLastKeyDeleteKey = isDeletion; - mLastTapTime = time; - } - - public void recordPublishabilityResultCode(final int publishabilityResultCode) { - // Keep consistent with publishability result code list in MainLogBuffer - switch (publishabilityResultCode) { - case MainLogBuffer.PUBLISHABILITY_PUBLISHABLE: - mPublishableCount++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_STOPPING: - mUnpublishableStoppingCount++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_INCORRECT_WORD_COUNT: - mUnpublishableIncorrectWordCount++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_SAMPLED_TOO_RECENTLY: - mUnpublishableSampledTooRecently++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_DICTIONARY_UNAVAILABLE: - mUnpublishableDictionaryUnavailable++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_MAY_CONTAIN_DIGIT: - mUnpublishableMayContainDigit++; - break; - case MainLogBuffer.PUBLISHABILITY_UNPUBLISHABLE_NOT_IN_DICTIONARY: - mUnpublishableNotInDictionary++; - break; - } - } -} diff --git a/java/src/com/android/inputmethod/research/Uploader.java b/java/src/com/android/inputmethod/research/Uploader.java deleted file mode 100644 index c7ea3e69d..000000000 --- a/java/src/com/android/inputmethod/research/Uploader.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.Manifest; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.BatteryManager; -import android.text.TextUtils; -import android.util.Log; - -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.define.ProductionFlag; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; - -/** - * Manages the uploading of ResearchLog files. - */ -public final class Uploader { - private static final String TAG = Uploader.class.getSimpleName(); - private static final boolean DEBUG = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - // Set IS_INHIBITING_AUTO_UPLOAD to true for local testing - private static final boolean IS_INHIBITING_UPLOAD = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - private static final int BUF_SIZE = 1024 * 8; - - private final Context mContext; - private final ResearchLogDirectory mResearchLogDirectory; - private final URL mUrl; - - public Uploader(final Context context) { - mContext = context; - mResearchLogDirectory = new ResearchLogDirectory(context); - - final String urlString = context.getString(R.string.research_logger_upload_url); - if (TextUtils.isEmpty(urlString)) { - mUrl = null; - return; - } - URL url = null; - try { - url = new URL(urlString); - } catch (final MalformedURLException e) { - Log.e(TAG, "Bad URL for uploading", e); - } - mUrl = url; - } - - public boolean isPossibleToUpload() { - return hasUploadingPermission() && mUrl != null && !IS_INHIBITING_UPLOAD; - } - - private boolean hasUploadingPermission() { - final PackageManager packageManager = mContext.getPackageManager(); - return packageManager.checkPermission(Manifest.permission.INTERNET, - mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED; - } - - public boolean isConvenientToUpload() { - return isExternallyPowered() && hasWifiConnection(); - } - - private boolean isExternallyPowered() { - final Intent intent = mContext.registerReceiver(null, new IntentFilter( - Intent.ACTION_BATTERY_CHANGED)); - final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - return pluggedState == BatteryManager.BATTERY_PLUGGED_AC - || pluggedState == BatteryManager.BATTERY_PLUGGED_USB; - } - - private boolean hasWifiConnection() { - final ConnectivityManager manager = - (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); - return wifiInfo.isConnected(); - } - - public void doUpload() { - final File[] files = mResearchLogDirectory.getUploadableLogFiles(); - if (files == null) return; - for (final File file : files) { - uploadFile(file); - } - } - - private void uploadFile(final File file) { - if (DEBUG) { - Log.d(TAG, "attempting upload of " + file.getAbsolutePath()); - } - final int contentLength = (int) file.length(); - HttpURLConnection connection = null; - InputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(file); - connection = (HttpURLConnection) mUrl.openConnection(); - connection.setRequestMethod("PUT"); - connection.setDoOutput(true); - connection.setFixedLengthStreamingMode(contentLength); - final OutputStream outputStream = connection.getOutputStream(); - uploadContents(fileInputStream, outputStream); - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.d(TAG, "upload failed: " + connection.getResponseCode()); - final InputStream netInputStream = connection.getInputStream(); - final BufferedReader reader = new BufferedReader(new InputStreamReader( - netInputStream)); - String line; - while ((line = reader.readLine()) != null) { - Log.d(TAG, "| " + reader.readLine()); - } - reader.close(); - return; - } - file.delete(); - if (DEBUG) { - Log.d(TAG, "upload successful"); - } - } catch (final IOException e) { - Log.e(TAG, "Exception uploading file", e); - } finally { - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (final IOException e) { - Log.e(TAG, "Exception closing uploaded file", e); - } - } - if (connection != null) { - connection.disconnect(); - } - } - } - - private static void uploadContents(final InputStream is, final OutputStream os) - throws IOException { - // TODO: Switch to NIO. - final byte[] buf = new byte[BUF_SIZE]; - int numBytesRead; - while ((numBytesRead = is.read(buf)) != -1) { - os.write(buf, 0, numBytesRead); - } - } -} diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java deleted file mode 100644 index fd3f2f60e..000000000 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.app.AlarmManager; -import android.app.IntentService; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.SystemClock; - -/** - * Service to invoke the uploader. - * - * Can be regularly invoked, invoked on boot, etc. - */ -public final class UploaderService extends IntentService { - private static final String TAG = UploaderService.class.getSimpleName(); - public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR; - public static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName() - + ".extra.UPLOAD_UNCONDITIONALLY"; - - public UploaderService() { - super("Research Uploader Service"); - } - - @Override - protected void onHandleIntent(final Intent intent) { - // We may reach this point either because the alarm fired, or because the system explicitly - // requested that an Upload occur. In the latter case, we want to cancel the alarm in case - // it's about to fire. - cancelAndRescheduleUploadingService(this, false /* needsRescheduling */); - - final Uploader uploader = new Uploader(this); - if (!uploader.isPossibleToUpload()) return; - if (isUploadingUnconditionally(intent.getExtras()) || uploader.isConvenientToUpload()) { - uploader.doUpload(); - } - cancelAndRescheduleUploadingService(this, true /* needsRescheduling */); - } - - private boolean isUploadingUnconditionally(final Bundle bundle) { - if (bundle == null) return false; - if (bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) { - return bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY); - } - return false; - } - - /** - * Arrange for the UploaderService to be run on a regular basis. - * - * Any existing scheduled invocation of UploaderService is removed and optionally rescheduled. - * This may cause problems if this method is called so often that no scheduled invocation is - * ever run. But if the delay is short enough that it will go off when the user is sleeping, - * then there should be no starvation. - * - * @param context {@link Context} object - * @param needsRescheduling whether to schedule a future intent to be delivered to this service - */ - public static void cancelAndRescheduleUploadingService(final Context context, - final boolean needsRescheduling) { - final Intent intent = new Intent(context, UploaderService.class); - final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); - final AlarmManager alarmManager = (AlarmManager) context.getSystemService( - Context.ALARM_SERVICE); - alarmManager.cancel(pendingIntent); - if (needsRescheduling) { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() - + UploaderService.RUN_INTERVAL, pendingIntent); - } - } -} diff --git a/java/src/com/android/inputmethod/research/ui/SplashScreen.java b/java/src/com/android/inputmethod/research/ui/SplashScreen.java deleted file mode 100644 index 78ed668d1..000000000 --- a/java/src/com/android/inputmethod/research/ui/SplashScreen.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research.ui; - -import android.app.AlertDialog.Builder; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.Intent; -import android.inputmethodservice.InputMethodService; -import android.net.Uri; -import android.os.IBinder; -import android.view.Window; -import android.view.WindowManager.LayoutParams; - -import com.android.inputmethod.latin.R.string; - -/** - * Show a dialog when the user first opens the keyboard. - * - * The splash screen is a modal dialog box presented when the user opens this keyboard for the first - * time. It is useful for giving specific warnings that must be shown to the user before use. - * - * While the splash screen does share with the setup wizard the common goal of presenting - * information to the user before use, they are presented at different times and with different - * capabilities. The setup wizard is launched by tapping on the icon, and walks the user through - * the setup process. It can, however, be bypassed by enabling the keyboard from Settings directly. - * The splash screen cannot be bypassed, and is therefore more appropriate for obtaining user - * consent. - */ -public class SplashScreen { - public interface UserConsentListener { - public void onSplashScreenUserClickedOk(); - } - - final UserConsentListener mListener; - final Dialog mSplashDialog; - - public SplashScreen(final InputMethodService inputMethodService, - final UserConsentListener listener) { - mListener = listener; - final Builder builder = new Builder(inputMethodService) - .setTitle(string.research_splash_title) - .setMessage(string.research_splash_content) - .setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mListener.onSplashScreenUserClickedOk(); - mSplashDialog.dismiss(); - } - }) - .setNegativeButton(android.R.string.no, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final String packageName = inputMethodService.getPackageName(); - final Uri packageUri = Uri.parse("package:" + packageName); - final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, - packageUri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - inputMethodService.startActivity(intent); - } - }) - .setCancelable(true) - .setOnCancelListener( - new OnCancelListener() { - @Override - public void onCancel(DialogInterface dialog) { - inputMethodService.requestHideSelf(0); - } - }); - mSplashDialog = builder.create(); - } - - /** - * Show the splash screen. - * - * The user must consent to the terms presented in the SplashScreen before they can use the - * keyboard. If they cancel instead, they are given the option to uninstall the keybard. - * - * @param windowToken {@link IBinder} to attach dialog to - */ - public void showSplashScreen(final IBinder windowToken) { - final Window window = mSplashDialog.getWindow(); - final LayoutParams lp = window.getAttributes(); - lp.token = windowToken; - lp.type = LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; - window.setAttributes(lp); - window.addFlags(LayoutParams.FLAG_ALT_FOCUSABLE_IM); - mSplashDialog.show(); - } - - public boolean isShowing() { - return mSplashDialog.isShowing(); - } -} diff --git a/native/jni/Application.mk b/native/jni/Application.mk index caf3b2622..ce095350e 100644 --- a/native/jni/Application.mk +++ b/native/jni/Application.mk @@ -1 +1 @@ -APP_STL := stlport_static +APP_STL := c++_static diff --git a/native/jni/CleanupNativeFileList.mk b/native/jni/CleanupNativeFileList.mk index 1738f8c58..eed6f1e63 100644 --- a/native/jni/CleanupNativeFileList.mk +++ b/native/jni/CleanupNativeFileList.mk @@ -13,6 +13,7 @@ # limitations under the License. LATIN_IME_CORE_SRC_FILES := +LATIN_IME_CORE_SRC_FILES_BACKWARD_V401 := LATIN_IME_CORE_TEST_FILES := LATIN_IME_JNI_SRC_FILES := LATIN_IME_SRC_DIR := diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk index 34c190718..2dd75c4f5 100644 --- a/native/jni/NativeFileList.mk +++ b/native/jni/NativeFileList.mk @@ -28,6 +28,7 @@ LATIN_IME_CORE_SRC_FILES := \ $(addprefix suggest/core/dictionary/, \ bigram_dictionary.cpp \ dictionary.cpp \ + dictionary_utils.cpp \ digraph_utils.cpp \ error_type_utils.cpp \ multi_bigram_map.cpp \ @@ -46,23 +47,22 @@ LATIN_IME_CORE_SRC_FILES := \ $(addprefix suggest/policyimpl/dictionary/, \ header/header_policy.cpp \ header/header_read_write_utils.cpp \ - shortcut/shortcut_list_reading_utils.cpp \ structure/dictionary_structure_with_buffer_policy_factory.cpp) \ - $(addprefix suggest/policyimpl/dictionary/bigram/, \ - bigram_list_read_write_utils.cpp \ - ver4_bigram_list_policy.cpp) \ $(addprefix suggest/policyimpl/dictionary/structure/pt_common/, \ + bigram/bigram_list_read_write_utils.cpp \ dynamic_pt_gc_event_listeners.cpp \ dynamic_pt_reading_helper.cpp \ dynamic_pt_reading_utils.cpp \ dynamic_pt_updating_helper.cpp \ dynamic_pt_writing_utils.cpp \ - patricia_trie_reading_utils.cpp) \ + patricia_trie_reading_utils.cpp \ + shortcut/shortcut_list_reading_utils.cpp ) \ $(addprefix suggest/policyimpl/dictionary/structure/v2/, \ patricia_trie_policy.cpp \ ver2_patricia_trie_node_reader.cpp \ ver2_pt_node_array_reader.cpp) \ $(addprefix suggest/policyimpl/dictionary/structure/v4/, \ + bigram/ver4_bigram_list_policy.cpp \ ver4_dict_buffers.cpp \ ver4_dict_constants.cpp \ ver4_patricia_trie_node_reader.cpp \ @@ -96,9 +96,31 @@ LATIN_IME_CORE_SRC_FILES := \ $(addprefix utils/, \ autocorrection_threshold_utils.cpp \ char_utils.cpp \ + jni_data_utils.cpp \ log_utils.cpp \ time_keeper.cpp) +LATIN_IME_CORE_SRC_FILES_BACKWARD_V402 := \ + $(addprefix suggest/policyimpl/dictionary/structure/backward/v402/, \ + ver4_dict_buffers.cpp \ + ver4_dict_constants.cpp \ + ver4_patricia_trie_node_reader.cpp \ + ver4_patricia_trie_node_writer.cpp \ + ver4_patricia_trie_policy.cpp \ + ver4_patricia_trie_reading_utils.cpp \ + ver4_patricia_trie_writing_helper.cpp \ + ver4_pt_node_array_reader.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/backward/v402/content/, \ + bigram_dict_content.cpp \ + probability_dict_content.cpp \ + shortcut_dict_content.cpp \ + sparse_table_dict_content.cpp \ + terminal_position_lookup_table.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/backward/v402/bigram/, \ + ver4_bigram_list_policy.cpp) + +LATIN_IME_CORE_SRC_FILES += $(LATIN_IME_CORE_SRC_FILES_BACKWARD_V402) + LATIN_IME_CORE_TEST_FILES := \ defines_test.cpp \ suggest/core/layout/normal_distribution_2d_test.cpp \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 9016cae69..6e2219d87 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -28,10 +28,12 @@ #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" #include "suggest/core/result/suggestion_results.h" +#include "suggest/core/session/prev_words_info.h" #include "suggest/core/suggest_options.h" #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h" #include "utils/char_utils.h" #include "utils/jni_data_utils.h" +#include "utils/log_utils.h" #include "utils/time_keeper.h" namespace latinime { @@ -93,15 +95,15 @@ static jlong latinime_BinaryDictionary_createOnMemory(JNIEnv *env, jclass clazz, return reinterpret_cast<jlong>(dictionary); } -static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict, +static bool latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict, jstring filePath) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); - if (!dictionary) return; + if (!dictionary) return false; const jsize filePathUtf8Length = env->GetStringUTFLength(filePath); char filePathChars[filePathUtf8Length + 1]; env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars); filePathChars[filePathUtf8Length] = '\0'; - dictionary->flush(filePathChars); + return dictionary->flush(filePathChars); } static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz, @@ -111,15 +113,15 @@ static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz, return dictionary->needsToRunGC(mindsBlockByGC == JNI_TRUE); } -static void latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict, +static bool latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict, jstring filePath) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); - if (!dictionary) return; + if (!dictionary) return false; const jsize filePathUtf8Length = env->GetStringUTFLength(filePath); char filePathChars[filePathUtf8Length + 1]; env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars); filePathChars[filePathUtf8Length] = '\0'; - dictionary->flushWithGC(filePathChars); + return dictionary->flushWithGC(filePathChars); } static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) { @@ -135,10 +137,9 @@ static void latinime_BinaryDictionary_getHeaderInfo(JNIEnv *env, jclass clazz, j if (!dictionary) return; const DictionaryHeaderStructurePolicy *const headerPolicy = dictionary->getDictionaryStructurePolicy()->getHeaderStructurePolicy(); - const int headerSize = headerPolicy->getSize(); - env->SetIntArrayRegion(outHeaderSize, 0 /* start */, 1 /* len */, &headerSize); - const int formatVersion = headerPolicy->getFormatVersionNumber(); - env->SetIntArrayRegion(outFormatVersion, 0 /* start */, 1 /* len */, &formatVersion); + JniDataUtils::putIntToArray(env, outHeaderSize, 0 /* index */, headerPolicy->getSize()); + JniDataUtils::putIntToArray(env, outFormatVersion, 0 /* index */, + headerPolicy->getFormatVersionNumber()); // Output attribute map jclass arrayListClass = env->FindClass("java/util/ArrayList"); jmethodID addMethodId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); @@ -148,14 +149,16 @@ static void latinime_BinaryDictionary_getHeaderInfo(JNIEnv *env, jclass clazz, j it != attributeMap->end(); ++it) { // Output key jintArray keyCodePointArray = env->NewIntArray(it->first.size()); - env->SetIntArrayRegion( - keyCodePointArray, 0 /* start */, it->first.size(), &it->first.at(0)); + JniDataUtils::outputCodePoints(env, keyCodePointArray, 0 /* start */, + it->first.size(), it->first.data(), it->first.size(), + false /* needsNullTermination */); env->CallBooleanMethod(outAttributeKeys, addMethodId, keyCodePointArray); env->DeleteLocalRef(keyCodePointArray); // Output value jintArray valueCodePointArray = env->NewIntArray(it->second.size()); - env->SetIntArrayRegion( - valueCodePointArray, 0 /* start */, it->second.size(), &it->second.at(0)); + JniDataUtils::outputCodePoints(env, valueCodePointArray, 0 /* start */, + it->second.size(), it->second.data(), it->second.size(), + false /* needsNullTermination */); env->CallBooleanMethod(outAttributeValues, addMethodId, valueCodePointArray); env->DeleteLocalRef(valueCodePointArray); } @@ -175,21 +178,22 @@ static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, jintArray inputCodePointsArray, jint inputSize, jintArray suggestOptions, - jintArray prevWordCodePointsForBigrams, jintArray outSuggestionCount, - jintArray outCodePointsArray, jintArray outScoresArray, jintArray outSpaceIndicesArray, - jintArray outTypesArray, jintArray outAutoCommitFirstWordConfidenceArray, - jfloatArray inOutLanguageWeight) { + jintArray prevWordCodePointsForBigrams, jboolean isBeginningOfSentence, + jintArray outSuggestionCount, jintArray outCodePointsArray, jintArray outScoresArray, + jintArray outSpaceIndicesArray, jintArray outTypesArray, + jintArray outAutoCommitFirstWordConfidenceArray, jfloatArray inOutLanguageWeight) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); // Assign 0 to outSuggestionCount here in case of returning earlier in this method. - int count = 0; - env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count); + JniDataUtils::putIntToArray(env, outSuggestionCount, 0 /* index */, 0); if (!dictionary) { return; } ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo); DicTraverseSession *traverseSession = reinterpret_cast<DicTraverseSession *>(dicTraverseSession); - + if (!traverseSession) { + return; + } // Input values int xCoordinates[inputSize]; int yCoordinates[inputSize]; @@ -244,15 +248,15 @@ static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, float languageWeight; env->GetFloatArrayRegion(inOutLanguageWeight, 0, 1 /* len */, &languageWeight); SuggestionResults suggestionResults(MAX_RESULTS); + const PrevWordsInfo prevWordsInfo(prevWordCodePoints, prevWordCodePointsLength, + isBeginningOfSentence); if (givenSuggestOptions.isGesture() || inputSize > 0) { // TODO: Use SuggestionResults to return suggestions. dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, - times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints, - prevWordCodePointsLength, &givenSuggestOptions, languageWeight, - &suggestionResults); + times, pointerIds, inputCodePoints, inputSize, &prevWordsInfo, + &givenSuggestOptions, languageWeight, &suggestionResults); } else { - dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength, - &suggestionResults); + dictionary->getPredictions(&prevWordsInfo, &suggestionResults); } suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray, outScoresArray, outSpaceIndicesArray, outTypesArray, @@ -269,8 +273,18 @@ static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, return dictionary->getProbability(codePoints, wordLength); } +static jint latinime_BinaryDictionary_getMaxProbabilityOfExactMatches( + JNIEnv *env, jclass clazz, jlong dict, jintArray word) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) return NOT_A_PROBABILITY; + const jsize wordLength = env->GetArrayLength(word); + int codePoints[wordLength]; + env->GetIntArrayRegion(word, 0, wordLength, codePoints); + return dictionary->getMaxProbabilityOfExactMatches(codePoints, wordLength); +} + static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz, - jlong dict, jintArray word0, jintArray word1) { + jlong dict, jintArray word0, jboolean isBeginningOfSentence, jintArray word1) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) return JNI_FALSE; const jsize word0Length = env->GetArrayLength(word0); @@ -279,8 +293,8 @@ static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass c int word1CodePoints[word1Length]; env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); - return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints, - word1Length); + const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence); + return dictionary->getBigramProbability(&prevWordsInfo, word1CodePoints, word1Length); } // Method to iterate all words in the dictionary for makedict. @@ -299,7 +313,9 @@ static jint latinime_BinaryDictionary_getNextWord(JNIEnv *env, jclass clazz, int wordCodePoints[outCodePointsLength]; memset(wordCodePoints, 0, sizeof(wordCodePoints)); const int nextToken = dictionary->getNextWordAndNextToken(token, wordCodePoints); - env->SetIntArrayRegion(outCodePoints, 0, outCodePointsLength, wordCodePoints); + JniDataUtils::outputCodePoints(env, outCodePoints, 0 /* start */, + MAX_WORD_LENGTH /* maxLength */, wordCodePoints, outCodePointsLength, + false /* needsNullTermination */); return nextToken; } @@ -318,12 +334,13 @@ static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz, outShortcutProbabilities); } -static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict, +static bool latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict, jintArray word, jint probability, jintArray shortcutTarget, jint shortcutProbability, - jboolean isNotAWord, jboolean isBlacklisted, jint timestamp) { + jboolean isBeginningOfSentence, jboolean isNotAWord, jboolean isBlacklisted, + jint timestamp) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { - return; + return false; } jsize codePointCount = env->GetArrayLength(word); int codePoints[codePointCount]; @@ -334,16 +351,30 @@ static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, if (!shortcutTargetCodePoints.empty()) { shortcuts.emplace_back(&shortcutTargetCodePoints, shortcutProbability); } - const UnigramProperty unigramProperty(isNotAWord, isBlacklisted, - probability, timestamp, 0 /* level */, 0 /* count */, &shortcuts); - dictionary->addUnigramWord(codePoints, codePointCount, &unigramProperty); + // Use 1 for count to indicate the word has inputted. + const UnigramProperty unigramProperty(isBeginningOfSentence, isNotAWord, + isBlacklisted, probability, timestamp, 0 /* level */, 1 /* count */, &shortcuts); + return dictionary->addUnigramEntry(codePoints, codePointCount, &unigramProperty); } -static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict, - jintArray word0, jintArray word1, jint probability, jint timestamp) { +static bool latinime_BinaryDictionary_removeUnigramWord(JNIEnv *env, jclass clazz, jlong dict, + jintArray word) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { - return; + return false; + } + jsize codePointCount = env->GetArrayLength(word); + int codePoints[codePointCount]; + env->GetIntArrayRegion(word, 0, codePointCount, codePoints); + return dictionary->removeUnigramEntry(codePoints, codePointCount); +} + +static bool latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict, + jintArray word0, jboolean isBeginningOfSentence, jintArray word1, jint probability, + jint timestamp) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) { + return false; } jsize word0Length = env->GetArrayLength(word0); int word0CodePoints[word0Length]; @@ -351,15 +382,20 @@ static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jsize word1Length = env->GetArrayLength(word1); int word1CodePoints[word1Length]; env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); - dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, - word1Length, probability, timestamp); + const std::vector<int> bigramTargetCodePoints( + word1CodePoints, word1CodePoints + word1Length); + // Use 1 for count to indicate the bigram has inputted. + const BigramProperty bigramProperty(&bigramTargetCodePoints, probability, + timestamp, 0 /* level */, 1 /* count */); + const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence); + return dictionary->addNgramEntry(&prevWordsInfo, &bigramProperty); } -static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict, - jintArray word0, jintArray word1) { +static bool latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict, + jintArray word0, jboolean isBeginningOfSentence, jintArray word1) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { - return; + return false; } jsize word0Length = env->GetArrayLength(word0); int word0CodePoints[word0Length]; @@ -367,8 +403,8 @@ static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass claz jsize word1Length = env->GetArrayLength(word1); int word1CodePoints[word1Length]; env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); - dictionary->removeBigramWords(word0CodePoints, word0Length, word1CodePoints, - word1Length); + const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, isBeginningOfSentence); + return dictionary->removeNgramEntry(&prevWordsInfo, word1CodePoints, word1Length); } // Returns how many language model params are processed. @@ -435,13 +471,21 @@ static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, j env->GetIntField(languageModelParam, shortcutProbabilityFieldId); shortcuts.emplace_back(&shortcutTargetCodePoints, shortcutProbability); } - const UnigramProperty unigramProperty(isNotAWord, isBlacklisted, - unigramProbability, timestamp, 0 /* level */, 0 /* count */, &shortcuts); - dictionary->addUnigramWord(word1CodePoints, word1Length, &unigramProperty); + // Use 1 for count to indicate the word has inputted. + const UnigramProperty unigramProperty(false /* isBeginningOfSentence */, isNotAWord, + isBlacklisted, unigramProbability, timestamp, 0 /* level */, 1 /* count */, + &shortcuts); + dictionary->addUnigramEntry(word1CodePoints, word1Length, &unigramProperty); if (word0) { jint bigramProbability = env->GetIntField(languageModelParam, bigramProbabilityFieldId); - dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, word1Length, - bigramProbability, timestamp); + const std::vector<int> bigramTargetCodePoints( + word1CodePoints, word1CodePoints + word1Length); + // Use 1 for count to indicate the bigram has inputted. + const BigramProperty bigramProperty(&bigramTargetCodePoints, bigramProbability, + timestamp, 0 /* level */, 1 /* count */); + const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, + false /* isBeginningOfSentence */); + dictionary->addNgramEntry(&prevWordsInfo, &bigramProperty); } if (dictionary->needsToRunGC(true /* mindsBlockByGC */)) { return i + 1; @@ -454,16 +498,6 @@ static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, j return languageModelParamCount; } -static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz, - jlong dict, jint unigramProbability, jint bigramProbability) { - Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); - if (!dictionary) { - return NOT_A_PROBABILITY; - } - return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability, - bigramProbability); -} - static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, jlong dict, jstring query) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); @@ -489,6 +523,87 @@ static bool latinime_BinaryDictionary_isCorruptedNative(JNIEnv *env, jclass claz return dictionary->getDictionaryStructurePolicy()->isCorrupted(); } +static DictionaryStructureWithBufferPolicy::StructurePolicyPtr runGCAndGetNewStructurePolicy( + DictionaryStructureWithBufferPolicy::StructurePolicyPtr structurePolicy, + const char *const dictFilePath) { + structurePolicy->flushWithGC(dictFilePath); + structurePolicy.release(); + return DictionaryStructureWithBufferPolicyFactory::newPolicyForExistingDictFile( + dictFilePath, 0 /* offset */, 0 /* size */, true /* isUpdatable */); +} + +static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, jlong dict, + jstring dictFilePath, jlong newFormatVersion) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) { + return false; + } + const jsize filePathUtf8Length = env->GetStringUTFLength(dictFilePath); + char dictFilePathChars[filePathUtf8Length + 1]; + env->GetStringUTFRegion(dictFilePath, 0, env->GetStringLength(dictFilePath), dictFilePathChars); + dictFilePathChars[filePathUtf8Length] = '\0'; + + const DictionaryHeaderStructurePolicy *const headerPolicy = + dictionary->getDictionaryStructurePolicy()->getHeaderStructurePolicy(); + DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy = + DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryDict( + newFormatVersion, *headerPolicy->getLocale(), headerPolicy->getAttributeMap()); + if (!dictionaryStructureWithBufferPolicy) { + LogUtils::logToJava(env, "Cannot migrate header."); + return false; + } + + // TODO: Migrate historical information. + int wordCodePoints[MAX_WORD_LENGTH]; + int token = 0; + // Add unigrams. + do { + token = dictionary->getNextWordAndNextToken(token, wordCodePoints); + const int wordLength = CharUtils::getCodePointCount(MAX_WORD_LENGTH, wordCodePoints); + const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, wordLength); + if (dictionaryStructureWithBufferPolicy->needsToRunGC(true /* mindsBlockByGC */)) { + dictionaryStructureWithBufferPolicy = runGCAndGetNewStructurePolicy( + std::move(dictionaryStructureWithBufferPolicy), dictFilePathChars); + if (!dictionaryStructureWithBufferPolicy) { + LogUtils::logToJava(env, "Cannot open dict after GC."); + return false; + } + } + if (!dictionaryStructureWithBufferPolicy->addUnigramEntry(wordCodePoints, wordLength, + wordProperty.getUnigramProperty())) { + LogUtils::logToJava(env, "Cannot add unigram to the new dict."); + return false; + } + } while (token != 0); + + // Add bigrams. + do { + token = dictionary->getNextWordAndNextToken(token, wordCodePoints); + const int wordLength = CharUtils::getCodePointCount(MAX_WORD_LENGTH, wordCodePoints); + const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, wordLength); + if (dictionaryStructureWithBufferPolicy->needsToRunGC(true /* mindsBlockByGC */)) { + dictionaryStructureWithBufferPolicy = runGCAndGetNewStructurePolicy( + std::move(dictionaryStructureWithBufferPolicy), dictFilePathChars); + if (!dictionaryStructureWithBufferPolicy) { + LogUtils::logToJava(env, "Cannot open dict after GC."); + return false; + } + } + const PrevWordsInfo prevWordsInfo(wordCodePoints, wordLength, + false /* isStartOfSentence */); + for (const BigramProperty &bigramProperty : *wordProperty.getBigramProperties()) { + if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&prevWordsInfo, + &bigramProperty)) { + LogUtils::logToJava(env, "Cannot add bigram to the new dict."); + return false; + } + } + } while (token != 0); + // Save to File. + dictionaryStructureWithBufferPolicy->flushWithGC(dictFilePathChars); + return true; +} + static const JNINativeMethod sMethods[] = { { const_cast<char *>("openNative"), @@ -517,7 +632,7 @@ static const JNINativeMethod sMethods[] = { }, { const_cast<char *>("flushNative"), - const_cast<char *>("(JLjava/lang/String;)V"), + const_cast<char *>("(JLjava/lang/String;)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_flush) }, { @@ -527,12 +642,12 @@ static const JNINativeMethod sMethods[] = { }, { const_cast<char *>("flushWithGCNative"), - const_cast<char *>("(JLjava/lang/String;)V"), + const_cast<char *>("(JLjava/lang/String;)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_flushWithGC) }, { const_cast<char *>("getSuggestionsNative"), - const_cast<char *>("(JJJ[I[I[I[I[II[I[I[I[I[I[I[I[I[F)V"), + const_cast<char *>("(JJJ[I[I[I[I[II[I[IZ[I[I[I[I[I[I[F)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions) }, { @@ -541,8 +656,13 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability) }, { + const_cast<char *>("getMaxProbabilityOfExactMatchesNative"), + const_cast<char *>("(J[I)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_getMaxProbabilityOfExactMatches) + }, + { const_cast<char *>("getBigramProbabilityNative"), - const_cast<char *>("(J[I[I)I"), + const_cast<char *>("(J[IZ[I)I"), reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability) }, { @@ -558,17 +678,22 @@ static const JNINativeMethod sMethods[] = { }, { const_cast<char *>("addUnigramWordNative"), - const_cast<char *>("(J[II[IIZZI)V"), + const_cast<char *>("(J[II[IIZZZI)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord) }, { + const_cast<char *>("removeUnigramWordNative"), + const_cast<char *>("(J[I)Z"), + reinterpret_cast<void *>(latinime_BinaryDictionary_removeUnigramWord) + }, + { const_cast<char *>("addBigramWordsNative"), - const_cast<char *>("(J[I[III)V"), + const_cast<char *>("(J[IZ[III)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords) }, { const_cast<char *>("removeBigramWordsNative"), - const_cast<char *>("(J[I[I)V"), + const_cast<char *>("(J[IZ[I)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords) }, { @@ -578,11 +703,6 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_addMultipleDictionaryEntries) }, { - const_cast<char *>("calculateProbabilityNative"), - const_cast<char *>("(JII)I"), - reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative) - }, - { const_cast<char *>("getPropertyNative"), const_cast<char *>("(JLjava/lang/String;)Ljava/lang/String;"), reinterpret_cast<void *>(latinime_BinaryDictionary_getProperty) @@ -591,6 +711,11 @@ static const JNINativeMethod sMethods[] = { const_cast<char *>("isCorruptedNative"), const_cast<char *>("(J)Z"), reinterpret_cast<void *>(latinime_BinaryDictionary_isCorruptedNative) + }, + { + const_cast<char *>("migrateNative"), + const_cast<char *>("(JLjava/lang/String;J)Z"), + reinterpret_cast<void *>(latinime_BinaryDictionary_migrateNative) } }; diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index 386643332..766064153 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -22,6 +22,7 @@ #include "jni.h" #include "jni_common.h" #include "suggest/core/session/dic_traverse_session.h" +#include "suggest/core/session/prev_words_info.h" namespace latinime { class Dictionary; @@ -34,16 +35,19 @@ static jlong latinime_setDicTraverseSession(JNIEnv *env, jclass clazz, jstring l static void latinime_initDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession, jlong dictionary, jintArray previousWord, jint previousWordLength) { DicTraverseSession *ts = reinterpret_cast<DicTraverseSession *>(traverseSession); + if (!ts) { + return; + } Dictionary *dict = reinterpret_cast<Dictionary *>(dictionary); if (!previousWord) { - DicTraverseSession::initSessionInstance( - ts, dict, 0 /* prevWord */, 0 /* prevWordLength*/, 0 /* suggestOptions */); + PrevWordsInfo prevWordsInfo; + ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */); return; } int prevWord[previousWordLength]; env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); - DicTraverseSession::initSessionInstance( - ts, dict, prevWord, previousWordLength, 0 /* suggestOptions */); + PrevWordsInfo prevWordsInfo(prevWord, previousWordLength, false /* isStartOfSentence */); + ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */); } static void latinime_releaseDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession) { diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 2fe2bd8fa..24d04e51f 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -293,13 +293,6 @@ static inline void prof_out(void) { #define M_PI_F 3.14159265f #define MAX_PERCENTILE 100 -// Number of base-10 digits in the largest integer + 1 to leave room for a zero terminator. -// As such, this is the maximum number of characters will be needed to represent an int as a -// string, including the terminator; this is used as the size of a string buffer large enough to -// hold any value that is intended to fit in an integer, e.g. in the code that reads the header -// of the binary dictionary where a {key,value} string pair scheme is used. -#define LARGEST_INT_DIGIT_COUNT 11 - #define NOT_A_CODE_POINT (-1) #define NOT_A_DISTANCE (-1) #define NOT_A_COORDINATE (-1) @@ -325,6 +318,8 @@ static inline void prof_out(void) { #define KEYCODE_SPACE ' ' #define KEYCODE_SINGLE_QUOTE '\'' #define KEYCODE_HYPHEN_MINUS '-' +// Code point to indicate beginning-of-sentence. This is not in the code point space of unicode. +#define CODE_POINT_BEGINNING_OF_SENTENCE 0x110000 #define SUGGEST_INTERFACE_OUTPUT_SCALE 1000000.0f #define MAX_PROBABILITY 255 @@ -341,6 +336,9 @@ static inline void prof_out(void) { #define MAX_POINTER_COUNT 1 #define MAX_POINTER_COUNT_G 2 +// (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported. +#define MAX_PREV_WORD_COUNT_FOR_N_GRAM 1 + #define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \ TypeName() = delete diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 47f5ec0d7..92f39ea25 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -103,10 +103,10 @@ class DicNode { PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - // Init for root with prevWordPtNodePos which is used for bigram - void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) { + // Init for root with prevWordsPtNodePos which is used for n-gram + void initAsRoot(const int rootPtNodeArrayPos, const int *const prevWordsPtNodePos) { mIsCachedForNextSuggestion = false; - mDicNodeProperties.init(rootPtNodeArrayPos, prevWordPtNodePos); + mDicNodeProperties.init(rootPtNodeArrayPos, prevWordsPtNodePos); mDicNodeState.init(); PROF_NODE_RESET(mProfiler); } @@ -114,13 +114,18 @@ class DicNode { // Init for root with previous word void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) { mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; - mDicNodeProperties.init(rootPtNodeArrayPos, dicNode->mDicNodeProperties.getPtNodePos()); + int newPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + newPrevWordsPtNodePos[0] = dicNode->mDicNodeProperties.getPtNodePos(); + for (size_t i = 1; i < NELEMS(newPrevWordsPtNodePos); ++i) { + newPrevWordsPtNodePos[i] = dicNode->getNthPrevWordTerminalPtNodePos(i); + } + mDicNodeProperties.init(rootPtNodeArrayPos, newPrevWordsPtNodePos); mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState, dicNode->mDicNodeProperties.getDepth()); PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - void initAsPassingChild(DicNode *parentDicNode) { + void initAsPassingChild(const DicNode *parentDicNode) { mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion; const int codePoint = parentDicNode->mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt( @@ -140,7 +145,7 @@ class DicNode { dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount); mDicNodeProperties.init(ptNodePos, childrenPtNodeArrayPos, mergedNodeCodePoints[0], probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth, - newLeavingDepth, dicNode->mDicNodeProperties.getPrevWordTerminalPtNodePos()); + newLeavingDepth, dicNode->mDicNodeProperties.getPrevWordsTerminalPtNodePos()); mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount, mergedNodeCodePoints); PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); @@ -198,14 +203,17 @@ class DicNode { return mDicNodeState.mDicNodeStateInput.getInputIndex(0) < inputSize - 1; } - // Used to get bigram probability in DicNodeUtils + // Used to get n-gram probability in DicNodeUtils. int getPtNodePos() const { return mDicNodeProperties.getPtNodePos(); } - // Used to get bigram probability in DicNodeUtils - int getPrevWordTerminalPtNodePos() const { - return mDicNodeProperties.getPrevWordTerminalPtNodePos(); + // Used to get n-gram probability in DicNodeUtils. n is 1-indexed. + int getNthPrevWordTerminalPtNodePos(const int n) const { + if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + return NOT_A_DICT_POS; + } + return mDicNodeProperties.getPrevWordsTerminalPtNodePos()[n - 1]; } // Used in DicNodeUtils diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 2d02a7d9c..4445f4aaf 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -29,8 +29,8 @@ namespace latinime { /* static */ void DicNodeUtils::initAsRoot( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int prevWordPtNodePos, DicNode *const newRootDicNode) { - newRootDicNode->initAsRoot(dictionaryStructurePolicy->getRootPosition(), prevWordPtNodePos); + const int *const prevWordsPtNodePos, DicNode *const newRootDicNode) { + newRootDicNode->initAsRoot(dictionaryStructurePolicy->getRootPosition(), prevWordsPtNodePos); } /*static */ void DicNodeUtils::initAsRootWithPreviousWord( @@ -48,7 +48,7 @@ namespace latinime { /////////////////////////////////// // Traverse node expansion utils // /////////////////////////////////// -/* static */ void DicNodeUtils::getAllChildDicNodes(DicNode *dicNode, +/* static */ void DicNodeUtils::getAllChildDicNodes(const DicNode *dicNode, const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, DicNodeVector *const childDicNodes) { if (dicNode->isTotalInputSizeExceedingLimit()) { @@ -86,7 +86,7 @@ namespace latinime { const DicNode *const dicNode, MultiBigramMap *const multiBigramMap) { const int unigramProbability = dicNode->getProbability(); const int ptNodePos = dicNode->getPtNodePos(); - const int prevWordTerminalPtNodePos = dicNode->getPrevWordTerminalPtNodePos(); + const int prevWordTerminalPtNodePos = dicNode->getNthPrevWordTerminalPtNodePos(1 /* n */); if (NOT_A_DICT_POS == ptNodePos || NOT_A_DICT_POS == prevWordTerminalPtNodePos) { // Note: Normally wordPos comes from the dictionary and should never equal // NOT_A_VALID_WORD_POS. diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h index 4c0f1f15d..00e80c604 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h @@ -30,12 +30,12 @@ class DicNodeUtils { public: static void initAsRoot( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int prevWordPtNodePos, DicNode *const newRootDicNode); + const int *const prevWordPtNodePos, DicNode *const newRootDicNode); static void initAsRootWithPreviousWord( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, const DicNode *const prevWordLastDicNode, DicNode *const newRootDicNode); static void initByCopy(const DicNode *const srcDicNode, DicNode *const destDicNode); - static void getAllChildDicNodes(DicNode *dicNode, + static void getAllChildDicNodes(const DicNode *dicNode, const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, DicNodeVector *childDicNodes); static float getBigramNodeImprobability( diff --git a/native/jni/src/suggest/core/dicnode/dic_node_vector.h b/native/jni/src/suggest/core/dicnode/dic_node_vector.h index cb28e57d8..54cde1988 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_vector.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_vector.h @@ -52,7 +52,7 @@ class DicNodeVector { return static_cast<int>(mDicNodes.size()); } - void pushPassingChild(DicNode *dicNode) { + void pushPassingChild(const DicNode *dicNode) { ASSERT(!mLock); mDicNodes.emplace_back(); mDicNodes.back().initAsPassingChild(dicNode); diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h index 11f8c2905..8202176f7 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h @@ -32,15 +32,14 @@ class DicNodeProperties { : mPtNodePos(NOT_A_DICT_POS), mChildrenPtNodeArrayPos(NOT_A_DICT_POS), mProbability(NOT_A_PROBABILITY), mDicNodeCodePoint(NOT_A_CODE_POINT), mIsTerminal(false), mHasChildrenPtNodes(false), - mIsBlacklistedOrNotAWord(false), mDepth(0), mLeavingDepth(0), - mPrevWordTerminalPtNodePos(NOT_A_DICT_POS) {} + mIsBlacklistedOrNotAWord(false), mDepth(0), mLeavingDepth(0) {} ~DicNodeProperties() {} // Should be called only once per DicNode is initialized. void init(const int pos, const int childrenPos, const int nodeCodePoint, const int probability, const bool isTerminal, const bool hasChildren, const bool isBlacklistedOrNotAWord, - const uint16_t depth, const uint16_t leavingDepth, const int prevWordNodePos) { + const uint16_t depth, const uint16_t leavingDepth, const int *const prevWordsNodePos) { mPtNodePos = pos; mChildrenPtNodeArrayPos = childrenPos; mDicNodeCodePoint = nodeCodePoint; @@ -50,11 +49,11 @@ class DicNodeProperties { mIsBlacklistedOrNotAWord = isBlacklistedOrNotAWord; mDepth = depth; mLeavingDepth = leavingDepth; - mPrevWordTerminalPtNodePos = prevWordNodePos; + memmove(mPrevWordsTerminalPtNodePos, prevWordsNodePos, sizeof(mPrevWordsTerminalPtNodePos)); } - // Init for root with prevWordPtNodePos which is used for bigram - void init(const int rootPtNodeArrayPos, const int prevWordNodePos) { + // Init for root with prevWordsPtNodePos which is used for n-gram + void init(const int rootPtNodeArrayPos, const int *const prevWordsNodePos) { mPtNodePos = NOT_A_DICT_POS; mChildrenPtNodeArrayPos = rootPtNodeArrayPos; mDicNodeCodePoint = NOT_A_CODE_POINT; @@ -64,7 +63,7 @@ class DicNodeProperties { mIsBlacklistedOrNotAWord = false; mDepth = 0; mLeavingDepth = 0; - mPrevWordTerminalPtNodePos = prevWordNodePos; + memmove(mPrevWordsTerminalPtNodePos, prevWordsNodePos, sizeof(mPrevWordsTerminalPtNodePos)); } void initByCopy(const DicNodeProperties *const dicNodeProp) { @@ -77,7 +76,8 @@ class DicNodeProperties { mIsBlacklistedOrNotAWord = dicNodeProp->mIsBlacklistedOrNotAWord; mDepth = dicNodeProp->mDepth; mLeavingDepth = dicNodeProp->mLeavingDepth; - mPrevWordTerminalPtNodePos = dicNodeProp->mPrevWordTerminalPtNodePos; + memmove(mPrevWordsTerminalPtNodePos, dicNodeProp->mPrevWordsTerminalPtNodePos, + sizeof(mPrevWordsTerminalPtNodePos)); } // Init as passing child @@ -91,7 +91,8 @@ class DicNodeProperties { mIsBlacklistedOrNotAWord = dicNodeProp->mIsBlacklistedOrNotAWord; mDepth = dicNodeProp->mDepth + 1; // Increment the depth of a passing child mLeavingDepth = dicNodeProp->mLeavingDepth; - mPrevWordTerminalPtNodePos = dicNodeProp->mPrevWordTerminalPtNodePos; + memmove(mPrevWordsTerminalPtNodePos, dicNodeProp->mPrevWordsTerminalPtNodePos, + sizeof(mPrevWordsTerminalPtNodePos)); } int getPtNodePos() const { @@ -131,8 +132,8 @@ class DicNodeProperties { return mIsBlacklistedOrNotAWord; } - int getPrevWordTerminalPtNodePos() const { - return mPrevWordTerminalPtNodePos; + const int *getPrevWordsTerminalPtNodePos() const { + return mPrevWordsTerminalPtNodePos; } private: @@ -148,7 +149,7 @@ class DicNodeProperties { bool mIsBlacklistedOrNotAWord; uint16_t mDepth; uint16_t mLeavingDepth; - int mPrevWordTerminalPtNodePos; + int mPrevWordsTerminalPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; }; } // namespace latinime #endif // LATINIME_DIC_NODE_PROPERTIES_H diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index f793363a8..295e760d6 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -26,6 +26,7 @@ #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "suggest/core/result/suggestion_results.h" +#include "suggest/core/session/prev_words_info.h" #include "utils/char_utils.h" namespace latinime { @@ -42,27 +43,15 @@ BigramDictionary::~BigramDictionary() { } /* Parameters : - * prevWord: the word before, the one for which we need to look up bigrams. - * prevWordLength: its length. + * prevWordsInfo: Information of previous words to get the predictions. * outSuggestionResults: SuggestionResults to put the predictions. */ -void BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength, +void BigramDictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const { - int pos = getBigramListPositionForWord(prevWord, prevWordLength, - false /* forceLowerCaseSearch */); - // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams - if (NOT_A_DICT_POS == pos) { - // If no bigrams for this exact word, search again in lower case. - pos = getBigramListPositionForWord(prevWord, prevWordLength, - true /* forceLowerCaseSearch */); - } - // If still no bigrams, we really don't have them! - if (NOT_A_DICT_POS == pos) return; - int unigramProbability = 0; int bigramCodePoints[MAX_WORD_LENGTH]; - BinaryDictionaryBigramsIterator bigramsIt( - mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos); + BinaryDictionaryBigramsIterator bigramsIt = + prevWordsInfo->getBigramsIteratorForPrediction(mDictionaryStructurePolicy); while (bigramsIt.hasNext()) { bigramsIt.next(); if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) { @@ -96,17 +85,13 @@ int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const in return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos); } -int BigramDictionary::getBigramProbability(const int *word0, int length0, const int *word1, - int length1) const { - int pos = getBigramListPositionForWord(word0, length0, false /* forceLowerCaseSearch */); - // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams - if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY; +int BigramDictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo, + const int *word1, int length1) const { int nextWordPos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(word1, length1, false /* forceLowerCaseSearch */); if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY; - - BinaryDictionaryBigramsIterator bigramsIt( - mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos); + BinaryDictionaryBigramsIterator bigramsIt = + prevWordsInfo->getBigramsIteratorForPrediction(mDictionaryStructurePolicy); while (bigramsIt.hasNext()) { bigramsIt.next(); if (bigramsIt.getBigramPos() == nextWordPos diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h index 12aaf20d3..bd3aed1bd 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h @@ -22,15 +22,17 @@ namespace latinime { class DictionaryStructureWithBufferPolicy; +class PrevWordsInfo; class SuggestionResults; class BigramDictionary { public: BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy); - void getPredictions(const int *word, int length, + void getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const; - int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const; + int getBigramProbability(const PrevWordsInfo *const prevWordsInfo, + const int *word1, int length1) const; ~BigramDictionary(); private: diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h index d16ac47fe..bc9d57671 100644 --- a/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h +++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h @@ -30,6 +30,11 @@ class BinaryDictionaryBigramsIterator { mBigramPos(NOT_A_DICT_POS), mProbability(NOT_A_PROBABILITY), mHasNext(pos != NOT_A_DICT_POS) {} + BinaryDictionaryBigramsIterator(BinaryDictionaryBigramsIterator &&bigramsIterator) + : mBigramsStructurePolicy(bigramsIterator.mBigramsStructurePolicy), + mPos(bigramsIterator.mPos), mBigramPos(bigramsIterator.mBigramPos), + mProbability(bigramsIterator.mProbability), mHasNext(bigramsIterator.mHasNext) {} + AK_FORCE_INLINE bool hasNext() const { return mHasNext; } diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index e288413a3..0bcde2294 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -19,6 +19,7 @@ #include "suggest/core/dictionary/dictionary.h" #include "defines.h" +#include "suggest/core/dictionary/dictionary_utils.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/result/suggestion_results.h" #include "suggest/core/session/dic_traverse_session.h" @@ -44,12 +45,11 @@ Dictionary::Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::Structu void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, - int inputSize, int *prevWordCodePoints, int prevWordLength, + int inputSize, const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions, const float languageWeight, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - DicTraverseSession::initSessionInstance( - traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); + traverseSession->init(this, prevWordsInfo, suggestOptions); const auto &suggest = suggestOptions->isGesture() ? mGestureSuggest : mTypingSuggest; suggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, @@ -59,11 +59,10 @@ void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } } -void Dictionary::getPredictions(const int *word, int length, +void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - if (length <= 0) return; - mBigramDictionary.getPredictions(word, length, outSuggestionResults); + mBigramDictionary.getPredictions(prevWordsInfo, outSuggestionResults); } int Dictionary::getProbability(const int *word, int length) const { @@ -76,39 +75,55 @@ int Dictionary::getProbability(const int *word, int length) const { return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos); } -int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1, - int length1) const { +int Dictionary::getMaxProbabilityOfExactMatches(const int *word, int length) const { TimeKeeper::setCurrentTime(); - return mBigramDictionary.getBigramProbability(word0, length0, word1, length1); + return DictionaryUtils::getMaxProbabilityOfExactMatches( + mDictionaryStructureWithBufferPolicy.get(), word, length); } -void Dictionary::addUnigramWord(const int *const word, const int length, +int Dictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo, const int *word, + int length) const { + TimeKeeper::setCurrentTime(); + return mBigramDictionary.getBigramProbability(prevWordsInfo, word, length); +} + +bool Dictionary::addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty) { + if (unigramProperty->representsBeginningOfSentence() + && !mDictionaryStructureWithBufferPolicy->getHeaderStructurePolicy() + ->supportsBeginningOfSentence()) { + AKLOGE("The dictionary doesn't support Beginning-of-Sentence."); + return false; + } + TimeKeeper::setCurrentTime(); + return mDictionaryStructureWithBufferPolicy->addUnigramEntry(word, length, unigramProperty); +} + +bool Dictionary::removeUnigramEntry(const int *const codePoints, const int codePointCount) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy->addUnigramWord(word, length, unigramProperty); + return mDictionaryStructureWithBufferPolicy->removeUnigramEntry(codePoints, codePointCount); } -void Dictionary::addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability, const int timestamp) { +bool Dictionary::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy->addBigramWords(word0, length0, word1, length1, - probability, timestamp); + return mDictionaryStructureWithBufferPolicy->addNgramEntry(prevWordsInfo, bigramProperty); } -void Dictionary::removeBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1) { +bool Dictionary::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const int *const word, const int length) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy->removeBigramWords(word0, length0, word1, length1); + return mDictionaryStructureWithBufferPolicy->removeNgramEntry(prevWordsInfo, word, length); } -void Dictionary::flush(const char *const filePath) { +bool Dictionary::flush(const char *const filePath) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy->flush(filePath); + return mDictionaryStructureWithBufferPolicy->flush(filePath); } -void Dictionary::flushWithGC(const char *const filePath) { +bool Dictionary::flushWithGC(const char *const filePath) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy->flushWithGC(filePath); + return mDictionaryStructureWithBufferPolicy->flushWithGC(filePath); } bool Dictionary::needsToRunGC(const bool mindsBlockByGC) { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index b6149b338..542ba7291 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -31,6 +31,7 @@ namespace latinime { class DictionaryStructureWithBufferPolicy; class DicTraverseSession; +class PrevWordsInfo; class ProximityInfo; class SuggestionResults; class SuggestOptions; @@ -56,35 +57,41 @@ class Dictionary { static const int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; static const int KIND_FLAG_EXACT_MATCH = 0x40000000; + static const int KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = 0x20000000; Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy); void getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, - int inputSize, int *prevWordCodePoints, int prevWordLength, + int inputSize, const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions, const float languageWeight, SuggestionResults *const outSuggestionResults) const; - void getPredictions(const int *word, int length, + void getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const; int getProbability(const int *word, int length) const; - int getBigramProbability(const int *word0, int length0, const int *word1, int length1) const; + int getMaxProbabilityOfExactMatches(const int *word, int length) const; - void addUnigramWord(const int *const codePoints, const int codePointCount, + int getBigramProbability(const PrevWordsInfo *const prevWordsInfo, + const int *word, int length) const; + + bool addUnigramEntry(const int *const codePoints, const int codePointCount, const UnigramProperty *const unigramProperty); - void addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability, const int timestamp); + bool removeUnigramEntry(const int *const codePoints, const int codePointCount); + + bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty); - void removeBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1); + bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word, + const int length); - void flush(const char *const filePath); + bool flush(const char *const filePath); - void flushWithGC(const char *const filePath); + bool flushWithGC(const char *const filePath); bool needsToRunGC(const bool mindsBlockByGC); diff --git a/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp new file mode 100644 index 000000000..b94966cbe --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/dictionary_utils.cpp @@ -0,0 +1,96 @@ +/* + * 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 "suggest/core/dictionary/dictionary_utils.h" + +#include "suggest/core/dicnode/dic_node.h" +#include "suggest/core/dicnode/dic_node_priority_queue.h" +#include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/dictionary.h" +#include "suggest/core/dictionary/digraph_utils.h" +#include "suggest/core/session/prev_words_info.h" +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" + +namespace latinime { + +/* static */ int DictionaryUtils::getMaxProbabilityOfExactMatches( + const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, + const int *const codePoints, const int codePointCount) { + std::vector<DicNode> current; + std::vector<DicNode> next; + + // No prev words information. + PrevWordsInfo emptyPrevWordsInfo; + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + emptyPrevWordsInfo.getPrevWordsTerminalPtNodePos(dictionaryStructurePolicy, + prevWordsPtNodePos, false /* tryLowerCaseSearch */); + current.emplace_back(); + DicNodeUtils::initAsRoot(dictionaryStructurePolicy, prevWordsPtNodePos, ¤t.front()); + for (int i = 0; i < codePointCount; ++i) { + // The base-lower input is used to ignore case errors and accent errors. + const int codePoint = CharUtils::toBaseLowerCase(codePoints[i]); + for (const DicNode &dicNode : current) { + if (dicNode.isInDigraph() && dicNode.getNodeCodePoint() == codePoint) { + next.emplace_back(dicNode); + next.back().advanceDigraphIndex(); + continue; + } + processChildDicNodes(dictionaryStructurePolicy, codePoint, &dicNode, &next); + } + current.clear(); + current.swap(next); + } + + int maxProbability = NOT_A_PROBABILITY; + for (const DicNode &dicNode : current) { + if (!dicNode.isTerminalDicNode()) { + continue; + } + // dicNode can contain case errors, accent errors, intentional omissions or digraphs. + maxProbability = std::max(maxProbability, dicNode.getProbability()); + } + return maxProbability; +} + +/* static */ void DictionaryUtils::processChildDicNodes( + const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, + const int inputCodePoint, const DicNode *const parentDicNode, + std::vector<DicNode> *const outDicNodes) { + DicNodeVector childDicNodes; + DicNodeUtils::getAllChildDicNodes(parentDicNode, dictionaryStructurePolicy, &childDicNodes); + for (int childIndex = 0; childIndex < childDicNodes.getSizeAndLock(); ++childIndex) { + DicNode *const childDicNode = childDicNodes[childIndex]; + const int codePoint = CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint()); + if (inputCodePoint == codePoint) { + outDicNodes->emplace_back(*childDicNode); + } + if (childDicNode->canBeIntentionalOmission()) { + processChildDicNodes(dictionaryStructurePolicy, inputCodePoint, childDicNode, + outDicNodes); + } + if (DigraphUtils::hasDigraphForCodePoint( + dictionaryStructurePolicy->getHeaderStructurePolicy(), + childDicNode->getNodeCodePoint())) { + childDicNode->advanceDigraphIndex(); + if (childDicNode->getNodeCodePoint() == codePoint) { + childDicNode->advanceDigraphIndex(); + outDicNodes->emplace_back(*childDicNode); + } + } + } +} + +} // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/dictionary_utils.h b/native/jni/src/suggest/core/dictionary/dictionary_utils.h new file mode 100644 index 000000000..358ebf674 --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/dictionary_utils.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_DICTIONARY_UTILS_H +#define LATINIME_DICTIONARY_UTILS_H + +#include <vector> + +#include "defines.h" + +namespace latinime { + +class DictionaryStructureWithBufferPolicy; +class DicNode; + +class DictionaryUtils { + public: + static int getMaxProbabilityOfExactMatches( + const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, + const int *const codePoints, const int codePointCount); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryUtils); + + static void processChildDicNodes( + const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, + const int inputCodePoint, const DicNode *const parentDicNode, + std::vector<DicNode> *const outDicNodes); +}; +} // namespace latinime +#endif // LATINIME_DICTIONARY_UTILS_H 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 0635fef7e..b6bf7a98c 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp @@ -31,4 +31,8 @@ const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x80; const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH = NOT_AN_ERROR | MATCH_WITH_CASE_ERROR | MATCH_WITH_ACCENT_ERROR | MATCH_WITH_DIGRAPH; +const ErrorTypeUtils::ErrorType + ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = + ERRORS_TREATED_AS_AN_EXACT_MATCH | INTENTIONAL_OMISSION; + } // namespace latinime 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 0e8e5b635..e3e76b238 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.h +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h @@ -51,6 +51,11 @@ class ErrorTypeUtils { return (containedErrorTypes & ~ERRORS_TREATED_AS_AN_EXACT_MATCH) == 0; } + static bool isExactMatchWithIntentionalOmission(const ErrorType containedErrorTypes) { + return (containedErrorTypes + & ~ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) == 0; + } + static bool isEditCorrectionError(const ErrorType errorType) { return (errorType & EDIT_CORRECTION) != 0; } @@ -67,6 +72,7 @@ class ErrorTypeUtils { DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorTypeUtils); static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH; + static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION; }; } // namespace latinime #endif // LATINIME_ERROR_TYPE_UTILS_H diff --git a/native/jni/src/suggest/core/dictionary/property/bigram_property.h b/native/jni/src/suggest/core/dictionary/property/bigram_property.h index 8d3429b5b..343af143c 100644 --- a/native/jni/src/suggest/core/dictionary/property/bigram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/bigram_property.h @@ -23,6 +23,7 @@ namespace latinime { +// TODO: Change to NgramProperty. class BigramProperty { public: BigramProperty(const std::vector<int> *const targetCodePoints, diff --git a/native/jni/src/suggest/core/dictionary/property/unigram_property.h b/native/jni/src/suggest/core/dictionary/property/unigram_property.h index d2551057b..902eb000f 100644 --- a/native/jni/src/suggest/core/dictionary/property/unigram_property.h +++ b/native/jni/src/suggest/core/dictionary/property/unigram_property.h @@ -48,15 +48,21 @@ class UnigramProperty { }; UnigramProperty() - : mIsNotAWord(false), mIsBlacklisted(false), mProbability(NOT_A_PROBABILITY), - mTimestamp(NOT_A_TIMESTAMP), mLevel(0), mCount(0), mShortcuts() {} - - UnigramProperty(const bool isNotAWord, const bool isBlacklisted, const int probability, - const int timestamp, const int level, const int count, - const std::vector<ShortcutProperty> *const shortcuts) - : mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability), + : mRepresentsBeginningOfSentence(false), mIsNotAWord(false), mIsBlacklisted(false), + mProbability(NOT_A_PROBABILITY), mTimestamp(NOT_A_TIMESTAMP), mLevel(0), mCount(0), + mShortcuts() {} + + UnigramProperty(const bool representsBeginningOfSentence, const bool isNotAWord, + const bool isBlacklisted, const int probability, const int timestamp, const int level, + const int count, const std::vector<ShortcutProperty> *const shortcuts) + : mRepresentsBeginningOfSentence(representsBeginningOfSentence), + mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mProbability(probability), mTimestamp(timestamp), mLevel(level), mCount(count), mShortcuts(*shortcuts) {} + bool representsBeginningOfSentence() const { + return mRepresentsBeginningOfSentence; + } + bool isNotAWord() const { return mIsNotAWord; } @@ -94,6 +100,7 @@ class UnigramProperty { DISALLOW_ASSIGNMENT_OPERATOR(UnigramProperty); // TODO: Make members const. + bool mRepresentsBeginningOfSentence; bool mIsNotAWord; bool mIsBlacklisted; int mProbability; diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.cpp b/native/jni/src/suggest/core/dictionary/property/word_property.cpp index 95608dcf8..6f5f808f8 100644 --- a/native/jni/src/suggest/core/dictionary/property/word_property.cpp +++ b/native/jni/src/suggest/core/dictionary/property/word_property.cpp @@ -16,14 +16,17 @@ #include "suggest/core/dictionary/property/word_property.h" +#include "utils/jni_data_utils.h" + namespace latinime { void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags, jintArray outProbabilityInfo, jobject outBigramTargets, jobject outBigramProbabilities, jobject outShortcutTargets, jobject outShortcutProbabilities) const { - env->SetIntArrayRegion(outCodePoints, 0 /* start */, mCodePoints.size(), &mCodePoints[0]); - + JniDataUtils::outputCodePoints(env, outCodePoints, 0 /* start */, + MAX_WORD_LENGTH /* maxLength */, mCodePoints.data(), mCodePoints.size(), + false /* needsNullTermination */); jboolean flags[] = {mUnigramProperty.isNotAWord(), mUnigramProperty.isBlacklisted(), !mBigrams.empty(), mUnigramProperty.hasShortcuts()}; env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags); @@ -41,8 +44,9 @@ void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, for (const auto &bigramProperty : mBigrams) { const std::vector<int> *const word1CodePoints = bigramProperty.getTargetCodePoints(); jintArray bigramWord1CodePointArray = env->NewIntArray(word1CodePoints->size()); - env->SetIntArrayRegion(bigramWord1CodePointArray, 0 /* start */, - word1CodePoints->size(), word1CodePoints->data()); + JniDataUtils::outputCodePoints(env, bigramWord1CodePointArray, 0 /* start */, + word1CodePoints->size(), word1CodePoints->data(), word1CodePoints->size(), + false /* needsNullTermination */); env->CallBooleanMethod(outBigramTargets, addMethodId, bigramWord1CodePointArray); env->DeleteLocalRef(bigramWord1CodePointArray); @@ -62,6 +66,9 @@ void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, jintArray shortcutTargetCodePointArray = env->NewIntArray(targetCodePoints->size()); env->SetIntArrayRegion(shortcutTargetCodePointArray, 0 /* start */, targetCodePoints->size(), targetCodePoints->data()); + JniDataUtils::outputCodePoints(env, shortcutTargetCodePointArray, 0 /* start */, + targetCodePoints->size(), targetCodePoints->data(), targetCodePoints->size(), + false /* needsNullTermination */); env->CallBooleanMethod(outShortcutTargets, addMethodId, shortcutTargetCodePointArray); env->DeleteLocalRef(shortcutTargetCodePointArray); jobject integerProbability = env->NewObject(integerClass, intToIntegerConstructorId, 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 5519a917c..aa3e0b68a 100644 --- a/native/jni/src/suggest/core/dictionary/property/word_property.h +++ b/native/jni/src/suggest/core/dictionary/property/word_property.h @@ -42,6 +42,14 @@ class WordProperty { jintArray outProbabilityInfo, jobject outBigramTargets, jobject outBigramProbabilities, jobject outShortcutTargets, jobject outShortcutProbabilities) const; + const UnigramProperty *getUnigramProperty() const { + return &mUnigramProperty; + } + + const std::vector<BigramProperty> *getBigramProperties() const { + return &mBigrams; + } + private: // Default copy constructor is used for using as a return value. DISALLOW_ASSIGNMENT_OPERATOR(WordProperty); diff --git a/native/jni/src/suggest/core/layout/proximity_info.cpp b/native/jni/src/suggest/core/layout/proximity_info.cpp index c40a2bdca..4c75a188e 100644 --- a/native/jni/src/suggest/core/layout/proximity_info.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info.cpp @@ -226,7 +226,7 @@ int ProximityInfo::getKeyCenterXOfKeyIdG( // When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without // using the line segment. int ProximityInfo::getKeyCenterYOfKeyIdG( - const int keyId, const int referencePointY, const bool isGeometric) const { + const int keyId, const int referencePointY, const bool isGeometric) const { // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing. if (keyId < 0) { return 0; diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h index 71e83a80c..211a79737 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h @@ -56,7 +56,7 @@ class ProximityInfoStateUtils { const std::vector<int> *const sampledLengthCache, const std::vector<int> *const sampledInputIndice, std::vector<float> *sampledSpeedRates, std::vector<float> *sampledDirections); - static void refreshBeelineSpeedRates(const int mostCommonKeyWidth, const float averageSpeed, + static void refreshBeelineSpeedRates(const int mostCommonKeyWidth, const float averageSpeed, const int inputSize, const int *const xCoordinates, const int *const yCoordinates, const int *times, const int sampledInputSize, const std::vector<int> *const sampledInputXs, diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h index a8dab9fcd..a61227626 100644 --- a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h @@ -49,6 +49,10 @@ class DictionaryHeaderStructurePolicy { virtual bool shouldBoostExactMatches() const = 0; + virtual const std::vector<int> *getLocale() const = 0; + + virtual bool supportsBeginningOfSentence() const = 0; + protected: DictionaryHeaderStructurePolicy() {} diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h index 807f9b8dd..e2771f97c 100644 --- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h @@ -29,6 +29,7 @@ class DicNodeVector; class DictionaryBigramsStructurePolicy; class DictionaryHeaderStructurePolicy; class DictionaryShortcutsStructurePolicy; +class PrevWordsInfo; class UnigramProperty; /* @@ -69,20 +70,25 @@ class DictionaryStructureWithBufferPolicy { virtual const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const = 0; // Returns whether the update was success or not. - virtual bool addUnigramWord(const int *const word, const int length, + virtual bool addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty) = 0; // Returns whether the update was success or not. - virtual bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability, const int timestamp) = 0; + virtual bool removeUnigramEntry(const int *const word, const int length) = 0; // Returns whether the update was success or not. - virtual bool removeBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1) = 0; + virtual bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty) = 0; - virtual void flush(const char *const filePath) = 0; + // Returns whether the update was success or not. + virtual bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const int *const word, const int length) = 0; + + // Returns whether the flush was success or not. + virtual bool flush(const char *const filePath) = 0; - virtual void flushWithGC(const char *const filePath) = 0; + // Returns whether the GC and flush were success or not. + virtual bool flushWithGC(const char *const filePath) = 0; virtual bool needsToRunGC(const bool mindsBlockByGC) const = 0; diff --git a/native/jni/src/suggest/core/result/suggestion_results.cpp b/native/jni/src/suggest/core/result/suggestion_results.cpp index 088a55f6f..4c10bd08a 100644 --- a/native/jni/src/suggest/core/result/suggestion_results.cpp +++ b/native/jni/src/suggest/core/result/suggestion_results.cpp @@ -16,6 +16,8 @@ #include "suggest/core/result/suggestion_results.h" +#include "utils/jni_data_utils.h" + namespace latinime { void SuggestionResults::outputSuggestions(JNIEnv *env, jintArray outSuggestionCount, @@ -27,31 +29,22 @@ void SuggestionResults::outputSuggestions(JNIEnv *env, jintArray outSuggestionCo const SuggestedWord &suggestedWord = mSuggestedWords.top(); suggestedWord.getCodePointCount(); const int start = outputIndex * MAX_WORD_LENGTH; - env->SetIntArrayRegion(outputCodePointsArray, start, suggestedWord.getCodePointCount(), - suggestedWord.getCodePoint()); - if (suggestedWord.getCodePointCount() < MAX_WORD_LENGTH) { - const int terminal = 0; - env->SetIntArrayRegion(outputCodePointsArray, start + suggestedWord.getCodePointCount(), - 1 /* len */, &terminal); - } - const int score = suggestedWord.getScore(); - env->SetIntArrayRegion(outScoresArray, outputIndex, 1 /* len */, &score); - const int indexToPartialCommit = suggestedWord.getIndexToPartialCommit(); - env->SetIntArrayRegion(outSpaceIndicesArray, outputIndex, 1 /* len */, - &indexToPartialCommit); - const int type = suggestedWord.getType(); - env->SetIntArrayRegion(outTypesArray, outputIndex, 1 /* len */, &type); + JniDataUtils::outputCodePoints(env, outputCodePointsArray, start, + MAX_WORD_LENGTH /* maxLength */, suggestedWord.getCodePoint(), + suggestedWord.getCodePointCount(), true /* needsNullTermination */); + JniDataUtils::putIntToArray(env, outScoresArray, outputIndex, suggestedWord.getScore()); + JniDataUtils::putIntToArray(env, outSpaceIndicesArray, outputIndex, + suggestedWord.getIndexToPartialCommit()); + JniDataUtils::putIntToArray(env, outTypesArray, outputIndex, suggestedWord.getType()); if (mSuggestedWords.size() == 1) { - const int autoCommitFirstWordConfidence = - suggestedWord.getAutoCommitFirstWordConfidence(); - env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0 /* start */, - 1 /* len */, &autoCommitFirstWordConfidence); + JniDataUtils::putIntToArray(env, outAutoCommitFirstWordConfidenceArray, 0 /* index */, + suggestedWord.getAutoCommitFirstWordConfidence()); } ++outputIndex; mSuggestedWords.pop(); } - env->SetIntArrayRegion(outSuggestionCount, 0 /* start */, 1 /* len */, &outputIndex); - env->SetFloatArrayRegion(outLanguageWeight, 0 /* start */, 1 /* len */, &mLanguageWeight); + JniDataUtils::putIntToArray(env, outSuggestionCount, 0 /* index */, outputIndex); + JniDataUtils::putFloatToArray(env, outLanguageWeight, 0 /* index */, mLanguageWeight); } void SuggestionResults::addPrediction(const int *const codePoints, const int codePointCount, 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 a307cb45d..7b0e7e1b4 100644 --- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp @@ -26,6 +26,7 @@ #include "suggest/core/policy/scoring.h" #include "suggest/core/result/suggestion_results.h" #include "suggest/core/session/dic_traverse_session.h" +#include "suggest/core/suggest_options.h" namespace latinime { @@ -89,6 +90,9 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0; const bool isExactMatch = ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes()); + 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") @@ -96,10 +100,17 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; && !(isPossiblyOffensiveWord && isFirstCharUppercase); const int outputTypeFlags = (isPossiblyOffensiveWord ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0) - | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0); + | ((isSafeExactMatch && 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 = !terminalDicNode->isBlacklistedOrNotAWord(); + // 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 && isPossiblyOffensiveWord + && !isSafeExactMatch; // Increase output score of top typing suggestion to ensure autocorrection. // TODO: Better integration with java side autocorrection logic. @@ -110,8 +121,9 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()), boostExactMatches); - // Don't output invalid words. However, we still need to submit their shortcuts if any. - if (isValidWord) { + // Don't output invalid or blocked offensive words. However, we still need to submit their + // shortcuts if any. + if (isValidWord && !isBlockedOffensiveWord) { int codePoints[MAX_WORD_LENGTH]; terminalDicNode->outputResult(codePoints); const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ? diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 77b634e07..f1e411f38 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -20,6 +20,7 @@ #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/core/session/prev_words_info.h" namespace latinime { @@ -28,25 +29,14 @@ namespace latinime { const int DicTraverseSession::DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION = 256 * 1024; -void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord, - int prevWordLength, const SuggestOptions *const suggestOptions) { +void DicTraverseSession::init(const Dictionary *const dictionary, + const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions) { mDictionary = dictionary; mMultiWordCostMultiplier = getDictionaryStructurePolicy()->getHeaderStructurePolicy() ->getMultiWordCostMultiplier(); mSuggestOptions = suggestOptions; - if (!prevWord) { - mPrevWordPtNodePos = NOT_A_DICT_POS; - return; - } - // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call. - mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord( - prevWord, prevWordLength, false /* forceLowerCaseSearch */); - if (mPrevWordPtNodePos == NOT_A_DICT_POS) { - // Check bigrams for lower-cased previous word if original was not found. Useful for - // auto-capitalized words like "The [current_word]". - mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord( - prevWord, prevWordLength, true /* forceLowerCaseSearch */); - } + prevWordsInfo->getPrevWordsTerminalPtNodePos( + getDictionaryStructurePolicy(), mPrevWordsPtNodePos, true /* tryLowerCaseSearch */); } void DicTraverseSession::setupForGetSuggestions(const ProximityInfo *pInfo, diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index 843ca85a0..5a51a112d 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -29,6 +29,7 @@ namespace latinime { class Dictionary; class DictionaryStructureWithBufferPolicy; +class PrevWordsInfo; class ProximityInfo; class SuggestOptions; @@ -44,32 +45,25 @@ class DicTraverseSession { dictSize >= DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION); } - static AK_FORCE_INLINE void initSessionInstance(DicTraverseSession *traverseSession, - const Dictionary *const dictionary, const int *prevWord, const int prevWordLength, - const SuggestOptions *const suggestOptions) { - if (traverseSession) { - DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession); - tSession->init(dictionary, prevWord, prevWordLength, suggestOptions); - } - } - static AK_FORCE_INLINE void releaseSessionInstance(DicTraverseSession *traverseSession) { delete traverseSession; } AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache) - : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(nullptr), - mDictionary(nullptr), mSuggestOptions(nullptr), mDicNodesCache(usesLargeCache), - mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1), + : mProximityInfo(nullptr), mDictionary(nullptr), mSuggestOptions(nullptr), + mDicNodesCache(usesLargeCache), mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. // No need to initialize it explicitly here. + for (size_t i = 0; i < NELEMS(mPrevWordsPtNodePos); ++i) { + mPrevWordsPtNodePos[i] = NOT_A_DICT_POS; + } } // Non virtual inline destructor -- never inherit this class AK_FORCE_INLINE ~DicTraverseSession() {} - void init(const Dictionary *dictionary, const int *prevWord, int prevWordLength, + void init(const Dictionary *dictionary, const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions); // TODO: Remove and merge into init void setupForGetSuggestions(const ProximityInfo *pInfo, const int *inputCodePoints, @@ -85,9 +79,7 @@ class DicTraverseSession { //-------------------- const ProximityInfo *getProximityInfo() const { return mProximityInfo; } const SuggestOptions *getSuggestOptions() const { return mSuggestOptions; } - int getPrevWordPtNodePos() const { return mPrevWordPtNodePos; } - // TODO: REMOVE - void setPrevWordPtNodePos(const int ptNodePos) { mPrevWordPtNodePos = ptNodePos; } + const int *getPrevWordsPtNodePos() const { return mPrevWordsPtNodePos; } DicNodesCache *getDicTraverseCache() { return &mDicNodesCache; } MultiBigramMap *getMultiBigramMap() { return &mMultiBigramMap; } const ProximityInfoState *getProximityInfoState(int id) const { @@ -174,7 +166,7 @@ class DicTraverseSession { const int *const inputYs, const int *const times, const int *const pointerIds, const int inputSize, const float maxSpatialDistance, const int maxPointerCount); - int mPrevWordPtNodePos; + int mPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; const ProximityInfo *mProximityInfo; const Dictionary *mDictionary; const SuggestOptions *mSuggestOptions; diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/prev_words_info.h new file mode 100644 index 000000000..640f6a2fc --- /dev/null +++ b/native/jni/src/suggest/core/session/prev_words_info.h @@ -0,0 +1,182 @@ +/* + * 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_PREV_WORDS_INFO_H +#define LATINIME_PREV_WORDS_INFO_H + +#include "defines.h" +#include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "utils/char_utils.h" + +namespace latinime { + +// TODO: Support n-gram. +// This class does not take ownership of any code point buffers. +class PrevWordsInfo { + public: + // No prev word information. + PrevWordsInfo() { + clear(); + } + + PrevWordsInfo(const int *const prevWordCodePoints, const int prevWordCodePointCount, + const bool isBeginningOfSentence) { + clear(); + mPrevWordCodePoints[0] = prevWordCodePoints; + mPrevWordCodePointCount[0] = prevWordCodePointCount; + mIsBeginningOfSentence[0] = isBeginningOfSentence; + } + + bool isValid() const { + for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { + if (mPrevWordCodePointCount[i] > MAX_WORD_LENGTH) { + return false; + } + } + return true; + } + + void getPrevWordsTerminalPtNodePos( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + int *const outPrevWordsTerminalPtNodePos, const bool tryLowerCaseSearch) const { + for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { + outPrevWordsTerminalPtNodePos[i] = getTerminalPtNodePosOfWord(dictStructurePolicy, + mPrevWordCodePoints[i], mPrevWordCodePointCount[i], + mIsBeginningOfSentence[i], tryLowerCaseSearch); + } + } + + BinaryDictionaryBigramsIterator getBigramsIteratorForPrediction( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy) const { + const int bigramListPos = getBigramListPositionForWordWithTryingLowerCaseSearch( + dictStructurePolicy, mPrevWordCodePoints[0], mPrevWordCodePointCount[0], + mIsBeginningOfSentence[0]); + return BinaryDictionaryBigramsIterator(dictStructurePolicy->getBigramsStructurePolicy(), + bigramListPos); + } + + // n is 1-indexed. + const int *getNthPrevWordCodePoints(const int n) const { + if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + return nullptr; + } + return mPrevWordCodePoints[n - 1]; + } + + // n is 1-indexed. + int getNthPrevWordCodePointCount(const int n) const { + if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + return 0; + } + return mPrevWordCodePointCount[n - 1]; + } + + // n is 1-indexed. + bool isNthPrevWordBeginningOfSentence(const int n) const { + if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { + return false; + } + return mIsBeginningOfSentence[n - 1]; + } + + private: + DISALLOW_COPY_AND_ASSIGN(PrevWordsInfo); + + static int getTerminalPtNodePosOfWord( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + const int *const wordCodePoints, const int wordCodePointCount, + const bool isBeginningOfSentence, const bool tryLowerCaseSearch) { + if (!dictStructurePolicy || !wordCodePoints || wordCodePointCount > MAX_WORD_LENGTH) { + return NOT_A_DICT_POS; + } + int codePoints[MAX_WORD_LENGTH]; + int codePointCount = wordCodePointCount; + memmove(codePoints, wordCodePoints, sizeof(int) * codePointCount); + if (isBeginningOfSentence) { + codePointCount = CharUtils::attachBeginningOfSentenceMarker(codePoints, + codePointCount, MAX_WORD_LENGTH); + if (codePointCount <= 0) { + return NOT_A_DICT_POS; + } + } + const int wordPtNodePos = dictStructurePolicy->getTerminalPtNodePositionOfWord( + codePoints, codePointCount, false /* forceLowerCaseSearch */); + if (wordPtNodePos != NOT_A_DICT_POS || !tryLowerCaseSearch) { + // Return the position when when the word was found or doesn't try lower case + // search. + return wordPtNodePos; + } + // Check bigrams for lower-cased previous word if original was not found. Useful for + // auto-capitalized words like "The [current_word]". + return dictStructurePolicy->getTerminalPtNodePositionOfWord( + codePoints, codePointCount, true /* forceLowerCaseSearch */); + } + + static int getBigramListPositionForWordWithTryingLowerCaseSearch( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + const int *const wordCodePoints, const int wordCodePointCount, + const bool isBeginningOfSentence) { + if (!dictStructurePolicy || !wordCodePoints || wordCodePointCount > MAX_WORD_LENGTH) { + return NOT_A_DICT_POS; + } + int codePoints[MAX_WORD_LENGTH]; + int codePointCount = wordCodePointCount; + memmove(codePoints, wordCodePoints, sizeof(int) * codePointCount); + if (isBeginningOfSentence) { + codePointCount = CharUtils::attachBeginningOfSentenceMarker(codePoints, + codePointCount, MAX_WORD_LENGTH); + if (codePointCount <= 0) { + return NOT_A_DICT_POS; + } + } + int pos = getBigramListPositionForWord(dictStructurePolicy, codePoints, + codePointCount, false /* forceLowerCaseSearch */); + // getBigramListPositionForWord returns NOT_A_DICT_POS if this word isn't in the + // dictionary or has no bigrams + if (NOT_A_DICT_POS == pos) { + // If no bigrams for this exact word, search again in lower case. + pos = getBigramListPositionForWord(dictStructurePolicy, codePoints, + codePointCount, true /* forceLowerCaseSearch */); + } + return pos; + } + + static int getBigramListPositionForWord( + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, + const int *wordCodePoints, const int wordCodePointCount, + const bool forceLowerCaseSearch) { + if (!wordCodePoints || wordCodePointCount <= 0) return NOT_A_DICT_POS; + const int terminalPtNodePos = dictStructurePolicy->getTerminalPtNodePositionOfWord( + wordCodePoints, wordCodePointCount, forceLowerCaseSearch); + if (NOT_A_DICT_POS == terminalPtNodePos) return NOT_A_DICT_POS; + return dictStructurePolicy->getBigramsPositionOfPtNode(terminalPtNodePos); + } + + void clear() { + for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { + mPrevWordCodePoints[i] = nullptr; + mPrevWordCodePointCount[i] = 0; + mIsBeginningOfSentence[i] = false; + } + } + + const int *mPrevWordCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + int mPrevWordCodePointCount[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + bool mIsBeginningOfSentence[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; +}; +} // namespace latinime +#endif // LATINIME_PREV_WORDS_INFO_H diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index e675e0bb3..0cd305f5a 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -92,7 +92,7 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession) const { // Create a new dic node here DicNode rootNode; DicNodeUtils::initAsRoot(traverseSession->getDictionaryStructurePolicy(), - traverseSession->getPrevWordPtNodePos(), &rootNode); + traverseSession->getPrevWordsPtNodePos(), &rootNode); traverseSession->getDicTraverseCache()->copyPushActive(&rootNode); } } diff --git a/native/jni/src/suggest/core/suggest_options.h b/native/jni/src/suggest/core/suggest_options.h index 1b21aafcf..2e22a7ac3 100644 --- a/native/jni/src/suggest/core/suggest_options.h +++ b/native/jni/src/suggest/core/suggest_options.h @@ -34,6 +34,10 @@ class SuggestOptions{ return getBoolOption(USE_FULL_EDIT_DISTANCE); } + AK_FORCE_INLINE bool blockOffensiveWords() const { + return getBoolOption(BLOCK_OFFENSIVE_WORDS); + } + AK_FORCE_INLINE bool getAdditionalFeaturesBoolOption(const int key) const { return getBoolOption(key + ADDITIONAL_FEATURES_OPTIONS); } @@ -45,9 +49,10 @@ class SuggestOptions{ // reorder options. static const int IS_GESTURE = 0; static const int USE_FULL_EDIT_DISTANCE = 1; + static const int BLOCK_OFFENSIVE_WORDS = 2; // Additional features options are stored after the other options and used as setting values of // experimental features. - static const int ADDITIONAL_FEATURES_OPTIONS = 2; + static const int ADDITIONAL_FEATURES_OPTIONS = 3; const int *const mOptions; const int mLength; 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 251a71941..87cf0cd3b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -139,8 +139,12 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { switch (mDictFormatVersion) { case FormatUtils::VERSION_2: return FormatUtils::VERSION_2; + 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; default: return FormatUtils::UNKNOWN_VERSION; } @@ -238,6 +242,14 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { const int unigramCount, const int bigramCount, const int extendedRegionSize, DictionaryHeaderStructurePolicy::AttributeMap *outAttributeMap) const; + AK_FORCE_INLINE const std::vector<int> *getLocale() const { + return &mLocale; + } + + bool supportsBeginningOfSentence() const { + return mDictFormatVersion >= FormatUtils::VERSION_4; + } + private: DISALLOW_COPY_AND_ASSIGN(HeaderPolicy); 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 d20accfbc..a8f8f284b 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 @@ -26,6 +26,13 @@ namespace latinime { +// Number of base-10 digits in the largest integer + 1 to leave room for a zero terminator. +// As such, this is the maximum number of characters will be needed to represent an int as a +// string, including the terminator; this is used as the size of a string buffer large enough to +// hold any value that is intended to fit in an integer, e.g. in the code that reads the header +// of the binary dictionary where a {key,value} string pair scheme is used. +const int HeaderReadWriteUtils::LARGEST_INT_DIGIT_COUNT = 11; + const int HeaderReadWriteUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256; const int HeaderReadWriteUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256; @@ -91,8 +98,10 @@ typedef DictionaryHeaderStructurePolicy::AttributeMap AttributeMap; case FormatUtils::VERSION_2: // Version 2 dictionary writing is not supported. return false; + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: - return buffer->writeUintAndAdvancePosition(FormatUtils::VERSION_4 /* data */, + case FormatUtils::VERSION_4_DEV: + return buffer->writeUintAndAdvancePosition(version /* data */, HEADER_DICTIONARY_VERSION_SIZE, writingPos); default: return false; @@ -154,8 +163,8 @@ typedef DictionaryHeaderStructurePolicy::AttributeMap AttributeMap; /* static */ void HeaderReadWriteUtils::setIntAttributeInner(AttributeMap *const headerAttributes, const AttributeMap::key_type *const key, const int value) { AttributeMap::mapped_type valueVector; - char charBuf[LARGEST_INT_DIGIT_COUNT + 1]; - snprintf(charBuf, LARGEST_INT_DIGIT_COUNT + 1, "%d", value); + char charBuf[LARGEST_INT_DIGIT_COUNT]; + snprintf(charBuf, sizeof(charBuf), "%d", value); insertCharactersIntoVector(charBuf, &valueVector); (*headerAttributes)[*key] = valueVector; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h index a6b4c4e14..9b90488fc 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h @@ -92,6 +92,7 @@ class HeaderReadWriteUtils { private: DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadWriteUtils); + static const int LARGEST_INT_DIGIT_COUNT; static const int MAX_ATTRIBUTE_KEY_LENGTH; static const int MAX_ATTRIBUTE_VALUE_LENGTH; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/Readme.txt b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/Readme.txt new file mode 100644 index 000000000..9e29e836c --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/Readme.txt @@ -0,0 +1 @@ +Files under this directory have been auto generated. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp new file mode 100644 index 000000000..3e8e059f2 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT CHANGE THE LOGIC IN THIS FILE !!!!! + * Do not edit this file other than updating policy's interface. + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h" + +#include "suggest/core/dictionary/property/bigram_property.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +void Ver4BigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability, + bool *const outHasNext, int *const bigramEntryPos) const { + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(bigramEntryPos); + if (outBigramPos) { + // Lookup target PtNode position. + *outBigramPos = mTerminalPositionLookupTable->getTerminalPtNodePosition( + bigramEntry.getTargetTerminalId()); + } + if (outProbability) { + if (bigramEntry.hasHistoricalInfo()) { + *outProbability = + ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo(), + mHeaderPolicy); + } else { + *outProbability = bigramEntry.getProbability(); + } + } + if (outHasNext) { + *outHasNext = bigramEntry.hasNext(); + } +} + +bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTargetTerminalId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) { + // 1. The word has no bigrams yet. + // 2. The word has bigrams, and there is the target in the list. + // 3. The word has bigrams, and there is an invalid entry that can be reclaimed. + // 4. The word has bigrams. We have to append new bigram entry to the list. + // 5. Same as 4, but the list is the last entry of the content file. + if (outAddedNewEntry) { + *outAddedNewEntry = false; + } + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Case 1. PtNode that doesn't have a bigram list. + // Create new bigram list. + if (!mBigramDictContent->createNewBigramList(terminalId)) { + return false; + } + const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY, + newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(&newBigramEntry, + bigramProperty); + // Write an entry. + const int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (!mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, writingPos)) { + return false; + } + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + return true; + } + + int tailEntryPos = NOT_A_DICT_POS; + const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos, + &tailEntryPos); + if (tailEntryPos != NOT_A_DICT_POS || entryPosToUpdate == NOT_A_DICT_POS) { + // Case 4, 5. + // Add new entry to the bigram list. + if (tailEntryPos == NOT_A_DICT_POS) { + // Case 4. Create new bigram list. + if (!mBigramDictContent->createNewBigramList(terminalId)) { + return false; + } + const int destPos = mBigramDictContent->getBigramListHeadPos(terminalId); + // Copy existing bigram list. + if (!mBigramDictContent->copyBigramList(bigramListPos, destPos, &tailEntryPos)) { + return false; + } + } + // Write new entry at the tail position of the bigram content. + const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY, + newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( + &newBigramEntry, bigramProperty); + if (!mBigramDictContent->writeBigramEntryAtTail(&bigramEntryToWrite)) { + return false; + } + // Update has next flag of the tail entry. + if (!updateHasNextFlag(true /* hasNext */, tailEntryPos)) { + return false; + } + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + return true; + } + + // Case 2. Overwrite the existing entry. Case 3. Reclaim and reuse the existing invalid entry. + const BigramEntry originalBigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate); + if (!originalBigramEntry.isValid()) { + // Case 3. Reuse the existing invalid entry. outAddedNewEntry is false when an existing + // entry is updated. + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + } + const BigramEntry updatedBigramEntry = + originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( + &updatedBigramEntry, bigramProperty); + return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate); +} + +bool Ver4BigramListPolicy::removeEntry(const int terminalId, const int targetTerminalId) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return false; + } + const int entryPosToUpdate = getEntryPosToUpdate(targetTerminalId, bigramListPos, + nullptr /* outTailEntryPos */); + if (entryPosToUpdate == NOT_A_DICT_POS) { + // Bigram entry doesn't exist. + return false; + } + const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate); + if (targetTerminalId != bigramEntry.getTargetTerminalId()) { + // Bigram entry doesn't exist. + return false; + } + // Remove bigram entry by marking it as invalid entry and overwriting the original entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPosToUpdate); +} + +bool Ver4BigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId, + int *const outBigramCount) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return true; + } + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!bigramEntry.isValid()) { + continue; + } + const int targetPtNodePos = mTerminalPositionLookupTable->getTerminalPtNodePosition( + bigramEntry.getTargetTerminalId()); + if (targetPtNodePos == NOT_A_DICT_POS) { + // Invalidate bigram entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + } else if (bigramEntry.hasHistoricalInfo()) { + const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( + bigramEntry.getHistoricalInfo(), mHeaderPolicy); + if (ForgettingCurveUtils::needsToKeep(&historicalInfo, mHeaderPolicy)) { + const BigramEntry updatedBigramEntry = + bigramEntry.updateHistoricalInfoAndGetEntry(&historicalInfo); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + *outBigramCount += 1; + } else { + // Remove entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + } + } else { + *outBigramCount += 1; + } + } + return true; +} + +int Ver4BigramListPolicy::getBigramEntryConut(const int terminalId) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return 0; + } + int bigramCount = 0; + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (bigramEntry.isValid()) { + bigramCount++; + } + } + return bigramCount; +} + +int Ver4BigramListPolicy::getEntryPosToUpdate(const int targetTerminalIdToFind, + const int bigramListPos, int *const outTailEntryPos) const { + if (outTailEntryPos) { + *outTailEntryPos = NOT_A_DICT_POS; + } + bool hasNext = true; + int invalidEntryPos = NOT_A_DICT_POS; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (bigramEntry.getTargetTerminalId() == targetTerminalIdToFind) { + // Entry with same target is found. + return entryPos; + } else if (!bigramEntry.isValid()) { + // Invalid entry that can be reused is found. + invalidEntryPos = entryPos; + } + if (!hasNext && mBigramDictContent->isContentTailPos(readingPos)) { + if (outTailEntryPos) { + *outTailEntryPos = entryPos; + } + } + } + return invalidEntryPos; +} + +const BigramEntry Ver4BigramListPolicy::createUpdatedBigramEntryFrom( + const BigramEntry *const originalBigramEntry, + const BigramProperty *const bigramProperty) const { + // TODO: Consolidate historical info and probability. + if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + const HistoricalInfo historicalInfoForUpdate(bigramProperty->getTimestamp(), + bigramProperty->getLevel(), bigramProperty->getCount()); + const HistoricalInfo updatedHistoricalInfo = + ForgettingCurveUtils::createUpdatedHistoricalInfo( + originalBigramEntry->getHistoricalInfo(), bigramProperty->getProbability(), + &historicalInfoForUpdate, mHeaderPolicy); + return originalBigramEntry->updateHistoricalInfoAndGetEntry(&updatedHistoricalInfo); + } else { + return originalBigramEntry->updateProbabilityAndGetEntry(bigramProperty->getProbability()); + } +} + +bool Ver4BigramListPolicy::updateHasNextFlag(const bool hasNext, const int bigramEntryPos) { + const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(bigramEntryPos); + const BigramEntry updatedBigramEntry = bigramEntry.updateHasNextAndGetEntry(hasNext); + return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, bigramEntryPos); +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h new file mode 100644 index 000000000..61623468e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT CHANGE THE LOGIC IN THIS FILE !!!!! + * Do not edit this file other than updating policy's interface. + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_BIGRAM_LIST_POLICY_H +#define LATINIME_BACKWARD_V402_VER4_BIGRAM_LIST_POLICY_H + +#include "defines.h" +#include "suggest/core/policy/dictionary_bigrams_structure_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class BigramDictContent; +} // namespace v402 +} // namespace backward +class BigramProperty; +namespace backward { +namespace v402 { +} // namespace v402 +} // namespace backward +class HeaderPolicy; +namespace backward { +namespace v402 { +class TerminalPositionLookupTable; + +class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { + public: + Ver4BigramListPolicy(BigramDictContent *const bigramDictContent, + const TerminalPositionLookupTable *const terminalPositionLookupTable, + const HeaderPolicy *const headerPolicy) + : mBigramDictContent(bigramDictContent), + mTerminalPositionLookupTable(terminalPositionLookupTable), + mHeaderPolicy(headerPolicy) {} + + void getNextBigram(int *const outBigramPos, int *const outProbability, + bool *const outHasNext, int *const bigramEntryPos) const; + + void skipAllBigrams(int *const pos) const { + // Do nothing because we don't need to skip bigram lists in ver4 dictionaries. + } + + bool addNewEntry(const int terminalId, const int newTargetTerminalId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); + + bool removeEntry(const int terminalId, const int targetTerminalId); + + bool updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId, + int *const outBigramCount); + + int getBigramEntryConut(const int terminalId); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4BigramListPolicy); + + int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos, + int *const outTailEntryPos) const; + + const BigramEntry createUpdatedBigramEntryFrom(const BigramEntry *const originalBigramEntry, + const BigramProperty *const bigramProperty) const; + + bool updateHasNextFlag(const bool hasNext, const int bigramEntryPos); + + BigramDictContent *const mBigramDictContent; + const TerminalPositionLookupTable *const mTerminalPositionLookupTable; + const HeaderPolicy *const mHeaderPolicy; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_BIGRAM_LIST_POLICY_H */ 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 new file mode 100644 index 000000000..e2dd93c5e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h" + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( + int *const bigramEntryPos) const { + const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer(); + const int bigramEntryTailPos = (*bigramEntryPos) + getBigramEntrySize(); + if (*bigramEntryPos < 0 || bigramEntryTailPos > bigramListBuffer->getTailPosition()) { + AKLOGE("Invalid bigram entry position. bigramEntryPos: %d, bigramEntryTailPos: %d, " + "bufSize: %d", *bigramEntryPos, bigramEntryTailPos, + bigramListBuffer->getTailPosition()); + ASSERT(false); + return BigramEntry(false /* hasNext */, NOT_A_PROBABILITY, + Ver4DictConstants::NOT_A_TERMINAL_ID); + } + const int bigramFlags = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, bigramEntryPos); + const bool hasNext = (bigramFlags & Ver4DictConstants::BIGRAM_HAS_NEXT_MASK) != 0; + int probability = NOT_A_PROBABILITY; + int timestamp = NOT_A_TIMESTAMP; + int level = 0; + int count = 0; + if (mHasHistoricalInfo) { + timestamp = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos); + level = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, bigramEntryPos); + count = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos); + } else { + probability = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos); + } + const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos); + const int targetTerminalId = + (encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ? + Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId; + if (mHasHistoricalInfo) { + const HistoricalInfo historicalInfo(timestamp, level, count); + return BigramEntry(hasNext, probability, &historicalInfo, targetTerminalId); + } else { + return BigramEntry(hasNext, probability, targetTerminalId); + } +} + +bool BigramDictContent::writeBigramEntryAndAdvancePosition( + const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) { + BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer(); + const int bigramFlags = createAndGetBigramFlags(bigramEntryToWrite->hasNext()); + if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags, + Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags); + return false; + } + if (mHasHistoricalInfo) { + const HistoricalInfo *const historicalInfo = bigramEntryToWrite->getHistoricalInfo(); + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram timestamps. pos: %d, timestamp: %d", *entryWritingPos, + historicalInfo->getTimeStamp()); + return false; + } + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getLevel(), + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram level. pos: %d, level: %d", *entryWritingPos, + historicalInfo->getLevel()); + return false; + } + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getCount(), + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram count. pos: %d, count: %d", *entryWritingPos, + historicalInfo->getCount()); + return false; + } + } else { + if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(), + Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos, + bigramEntryToWrite->getProbability()); + return false; + } + } + const int targetTerminalIdToWrite = + (bigramEntryToWrite->getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) ? + Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID : + bigramEntryToWrite->getTargetTerminalId(); + if (!bigramListBuffer->writeUintAndAdvancePosition(targetTerminalIdToWrite, + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram target terminal id. pos: %d, target terminal id: %d", + *entryWritingPos, bigramEntryToWrite->getTargetTerminalId()); + return false; + } + return true; +} + +bool BigramDictContent::copyBigramList(const int bigramListPos, const int toPos, + int *const outTailEntryPos) { + int readingPos = bigramListPos; + int writingPos = toPos; + bool hasNext = true; + while (hasNext) { + const BigramEntry bigramEntry = getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!hasNext) { + *outTailEntryPos = writingPos; + } + if (!writeBigramEntryAndAdvancePosition(&bigramEntry, &writingPos)) { + AKLOGE("Cannot write bigram entry to copy. pos: %d", writingPos); + return false; + } + } + return true; +} + +bool BigramDictContent::runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const BigramDictContent *const originalBigramDictContent, + int *const outBigramEntryCount) { + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const int originalBigramListPos = + originalBigramDictContent->getBigramListHeadPos(it->first); + if (originalBigramListPos == NOT_A_DICT_POS) { + // This terminal does not have a bigram list. + continue; + } + const int bigramListPos = getContentBuffer()->getTailPosition(); + int bigramEntryCount = 0; + // Copy bigram list with GC from original content. + if (!runGCBigramList(originalBigramListPos, originalBigramDictContent, bigramListPos, + terminalIdMap, &bigramEntryCount)) { + AKLOGE("Cannot complete GC for the bigram list. original pos: %d, pos: %d", + originalBigramListPos, bigramListPos); + return false; + } + if (bigramEntryCount == 0) { + // All bigram entries are useless. This terminal does not have a bigram list. + continue; + } + *outBigramEntryCount += bigramEntryCount; + // Set bigram list position to the lookup table. + if (!getUpdatableAddressLookupTable()->set(it->second, bigramListPos)) { + AKLOGE("Cannot set bigram list position. terminal id: %d, pos: %d", + it->second, bigramListPos); + return false; + } + } + return true; +} + +// Returns whether GC for the bigram list was succeeded or not. +bool BigramDictContent::runGCBigramList(const int bigramListPos, + const BigramDictContent *const sourceBigramDictContent, const int toPos, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + int *const outEntrycount) { + bool hasNext = true; + int readingPos = bigramListPos; + int writingPos = toPos; + int lastEntryPos = NOT_A_DICT_POS; + while (hasNext) { + const BigramEntry originalBigramEntry = + sourceBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = originalBigramEntry.hasNext(); + if (originalBigramEntry.getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) { + continue; + } + TerminalPositionLookupTable::TerminalIdMap::const_iterator it = + terminalIdMap->find(originalBigramEntry.getTargetTerminalId()); + if (it == terminalIdMap->end()) { + // Target word has been removed. + continue; + } + lastEntryPos = hasNext ? writingPos : NOT_A_DICT_POS; + const BigramEntry updatedBigramEntry = + originalBigramEntry.updateTargetTerminalIdAndGetEntry(it->second); + if (!writeBigramEntryAndAdvancePosition(&updatedBigramEntry, &writingPos)) { + AKLOGE("Cannot write bigram entry to run GC. pos: %d", writingPos); + return false; + } + *outEntrycount += 1; + } + if (lastEntryPos != NOT_A_DICT_POS) { + // Update has next flag in the last written entry. + const BigramEntry bigramEntry = getBigramEntry(lastEntryPos).updateHasNextAndGetEntry( + false /* hasNext */); + if (!writeBigramEntry(&bigramEntry, lastEntryPos)) { + AKLOGE("Cannot write bigram entry to set hasNext flag after GC. pos: %d", writingPos); + return false; + } + } + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h new file mode 100644 index 000000000..b554e5676 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_BIGRAM_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_BIGRAM_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class BigramDictContent : public SparseTableDictContent { + public: + BigramDictContent(const char *const dictPath, const bool hasHistoricalInfo, + const bool isUpdatable) + : SparseTableDictContent(dictPath, + Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_FILE_EXTENSION, isUpdatable, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE), + mHasHistoricalInfo(hasHistoricalInfo) {} + + BigramDictContent(const bool hasHistoricalInfo) + : SparseTableDictContent(Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE), + mHasHistoricalInfo(hasHistoricalInfo) {} + + const BigramEntry getBigramEntry(const int bigramEntryPos) const { + int readingPos = bigramEntryPos; + return getBigramEntryAndAdvancePosition(&readingPos); + } + + const BigramEntry getBigramEntryAndAdvancePosition(int *const bigramEntryPos) const; + + // Returns head position of bigram list for a PtNode specified by terminalId. + int getBigramListHeadPos(const int terminalId) const { + const SparseTable *const addressLookupTable = getAddressLookupTable(); + if (!addressLookupTable->contains(terminalId)) { + return NOT_A_DICT_POS; + } + return addressLookupTable->get(terminalId); + } + + bool writeBigramEntryAtTail(const BigramEntry *const bigramEntryToWrite) { + int writingPos = getContentBuffer()->getTailPosition(); + return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos); + } + + bool writeBigramEntry(const BigramEntry *const bigramEntryToWrite, const int entryWritingPos) { + int writingPos = entryWritingPos; + return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos); + } + + bool writeBigramEntryAndAdvancePosition(const BigramEntry *const bigramEntryToWrite, + int *const entryWritingPos); + + bool createNewBigramList(const int terminalId) { + const int bigramListPos = getContentBuffer()->getTailPosition(); + return getUpdatableAddressLookupTable()->set(terminalId, bigramListPos); + } + + bool copyBigramList(const int bigramListPos, const int toPos, int *const outTailEntryPos); + + bool flushToFile(const char *const dictPath) const { + return flush(dictPath, Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_FILE_EXTENSION); + } + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const BigramDictContent *const originalBigramDictContent, + int *const outBigramEntryCount); + + bool isContentTailPos(const int pos) const { + return pos == getContentBuffer()->getTailPosition(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(BigramDictContent); + + int createAndGetBigramFlags(const bool hasNext) const { + return hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0; + } + + int getBigramEntrySize() const { + if (mHasHistoricalInfo) { + return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE + + Ver4DictConstants::TIME_STAMP_FIELD_SIZE + + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE + + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + } else { + return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE + + Ver4DictConstants::PROBABILITY_SIZE + + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + } + } + + bool runGCBigramList(const int bigramListPos, + const BigramDictContent *const sourceBigramDictContent, const int toPos, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + int *const outEntryCount); + + bool mHasHistoricalInfo; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_BIGRAM_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h new file mode 100644 index 000000000..40968b4d8 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_entry.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h + */ + +#ifndef LATINIME_BACKWARD_V402_BIGRAM_ENTRY_H +#define LATINIME_BACKWARD_V402_BIGRAM_ENTRY_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/historical_info.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class BigramEntry { + public: + BigramEntry(const BigramEntry& bigramEntry) + : mHasNext(bigramEntry.mHasNext), mProbability(bigramEntry.mProbability), + mHistoricalInfo(), mTargetTerminalId(bigramEntry.mTargetTerminalId) {} + + // Entry with historical information. + BigramEntry(const bool hasNext, const int probability, const int targetTerminalId) + : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(), + mTargetTerminalId(targetTerminalId) {} + + // Entry with historical information. + BigramEntry(const bool hasNext, const int probability, + const HistoricalInfo *const historicalInfo, const int targetTerminalId) + : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(*historicalInfo), + mTargetTerminalId(targetTerminalId) {} + + const BigramEntry getInvalidatedEntry() const { + return updateTargetTerminalIdAndGetEntry(Ver4DictConstants::NOT_A_TERMINAL_ID); + } + + const BigramEntry updateHasNextAndGetEntry(const bool hasNext) const { + return BigramEntry(hasNext, mProbability, &mHistoricalInfo, mTargetTerminalId); + } + + const BigramEntry updateTargetTerminalIdAndGetEntry(const int newTargetTerminalId) const { + return BigramEntry(mHasNext, mProbability, &mHistoricalInfo, newTargetTerminalId); + } + + const BigramEntry updateProbabilityAndGetEntry(const int probability) const { + return BigramEntry(mHasNext, probability, &mHistoricalInfo, mTargetTerminalId); + } + + const BigramEntry updateHistoricalInfoAndGetEntry( + const HistoricalInfo *const historicalInfo) const { + return BigramEntry(mHasNext, mProbability, historicalInfo, mTargetTerminalId); + } + + bool isValid() const { + return mTargetTerminalId != Ver4DictConstants::NOT_A_TERMINAL_ID; + } + + bool hasNext() const { + return mHasNext; + } + + int getProbability() const { + return mProbability; + } + + bool hasHistoricalInfo() const { + return mHistoricalInfo.isValid(); + } + + const HistoricalInfo *getHistoricalInfo() const { + return &mHistoricalInfo; + } + + int getTargetTerminalId() const { + return mTargetTerminalId; + } + + private: + // Copy constructor is public to use this class as a type of return value. + DISALLOW_DEFAULT_CONSTRUCTOR(BigramEntry); + DISALLOW_ASSIGNMENT_OPERATOR(BigramEntry); + + const bool mHasNext; + const int mProbability; + const HistoricalInfo mHistoricalInfo; + const int mTargetTerminalId; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_BIGRAM_ENTRY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/dict_content.h new file mode 100644 index 000000000..0f2f25534 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/dict_content.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_DICT_CONTENT_H + +#include "defines.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class DictContent { + public: + virtual ~DictContent() {} + virtual bool isValid() const = 0; + + protected: + DictContent() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DictContent); +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_DICT_CONTENT_H */ 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 new file mode 100644 index 000000000..c671647d4 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h" + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +const ProbabilityEntry ProbabilityDictContent::getProbabilityEntry(const int terminalId) const { + if (terminalId < 0 || terminalId >= mSize) { + // This method can be called with invalid terminal id during GC. + return ProbabilityEntry(0 /* flags */, NOT_A_PROBABILITY); + } + const BufferWithExtendableBuffer *const buffer = getBuffer(); + int entryPos = getEntryPos(terminalId); + const int flags = buffer->readUintAndAdvancePosition( + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &entryPos); + const int probability = buffer->readUintAndAdvancePosition( + Ver4DictConstants::PROBABILITY_SIZE, &entryPos); + if (mHasHistoricalInfo) { + const int timestamp = buffer->readUintAndAdvancePosition( + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &entryPos); + const int level = buffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos); + const int count = buffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos); + const HistoricalInfo historicalInfo(timestamp, level, count); + return ProbabilityEntry(flags, probability, &historicalInfo); + } else { + return ProbabilityEntry(flags, probability); + } +} + +bool ProbabilityDictContent::setProbabilityEntry(const int terminalId, + const ProbabilityEntry *const probabilityEntry) { + if (terminalId < 0) { + return false; + } + const int entryPos = getEntryPos(terminalId); + if (terminalId >= mSize) { + ProbabilityEntry dummyEntry; + // Write new entry. + int writingPos = getBuffer()->getTailPosition(); + while (writingPos <= entryPos) { + // Fulfilling with dummy entries until writingPos. + if (!writeEntry(&dummyEntry, writingPos)) { + AKLOGE("Cannot write dummy entry. pos: %d, mSize: %d", writingPos, mSize); + return false; + } + writingPos += getEntrySize(); + mSize++; + } + } + return writeEntry(probabilityEntry, entryPos); +} + +bool ProbabilityDictContent::flushToFile(const char *const dictPath) const { + if (getEntryPos(mSize) < getBuffer()->getTailPosition()) { + ProbabilityDictContent probabilityDictContentToWrite(mHasHistoricalInfo); + for (int i = 0; i < mSize; ++i) { + const ProbabilityEntry probabilityEntry = getProbabilityEntry(i); + if (!probabilityDictContentToWrite.setProbabilityEntry(i, &probabilityEntry)) { + AKLOGE("Cannot set probability entry in flushToFile. terminalId: %d", i); + return false; + } + } + return probabilityDictContentToWrite.flush(dictPath, + Ver4DictConstants::FREQ_FILE_EXTENSION); + } else { + return flush(dictPath, Ver4DictConstants::FREQ_FILE_EXTENSION); + } +} + +bool ProbabilityDictContent::runGC( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ProbabilityDictContent *const originalProbabilityDictContent) { + mSize = 0; + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const ProbabilityEntry probabilityEntry = + originalProbabilityDictContent->getProbabilityEntry(it->first); + if (!setProbabilityEntry(it->second, &probabilityEntry)) { + AKLOGE("Cannot set probability entry in runGC. terminalId: %d", it->second); + return false; + } + mSize++; + } + return true; +} + +int ProbabilityDictContent::getEntrySize() const { + if (mHasHistoricalInfo) { + return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE + + Ver4DictConstants::PROBABILITY_SIZE + + Ver4DictConstants::TIME_STAMP_FIELD_SIZE + + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE; + } else { + return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE + + Ver4DictConstants::PROBABILITY_SIZE; + } +} + +int ProbabilityDictContent::getEntryPos(const int terminalId) const { + return terminalId * getEntrySize(); +} + +bool ProbabilityDictContent::writeEntry(const ProbabilityEntry *const probabilityEntry, + const int entryPos) { + BufferWithExtendableBuffer *const bufferToWrite = getWritableBuffer(); + int writingPos = entryPos; + if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getFlags(), + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &writingPos)) { + AKLOGE("Cannot write flags in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getProbability(), + Ver4DictConstants::PROBABILITY_SIZE, &writingPos)) { + AKLOGE("Cannot write probability in probability dict content. pos: %d", writingPos); + return false; + } + if (mHasHistoricalInfo) { + const HistoricalInfo *const historicalInfo = probabilityEntry->getHistoricalInfo(); + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write timestamp in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getLevel(), + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write level in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getCount(), + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write count in probability dict content. pos: %d", writingPos); + return false; + } + } + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h new file mode 100644 index 000000000..3734797d4 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_PROBABILITY_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_PROBABILITY_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class ProbabilityEntry; + +class ProbabilityDictContent : public SingleDictContent { + public: + ProbabilityDictContent(const char *const dictPath, const bool hasHistoricalInfo, + const bool isUpdatable) + : SingleDictContent(dictPath, Ver4DictConstants::FREQ_FILE_EXTENSION, isUpdatable), + mHasHistoricalInfo(hasHistoricalInfo), + mSize(getBuffer()->getTailPosition() / getEntrySize()) {} + + ProbabilityDictContent(const bool hasHistoricalInfo) + : mHasHistoricalInfo(hasHistoricalInfo), mSize(0) {} + + const ProbabilityEntry getProbabilityEntry(const int terminalId) const; + + bool setProbabilityEntry(const int terminalId, const ProbabilityEntry *const probabilityEntry); + + bool flushToFile(const char *const dictPath) const; + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ProbabilityDictContent *const originalProbabilityDictContent); + + private: + DISALLOW_COPY_AND_ASSIGN(ProbabilityDictContent); + + int getEntrySize() const; + + int getEntryPos(const int terminalId) const; + + bool writeEntry(const ProbabilityEntry *const probabilityEntry, const int entryPos); + + bool mHasHistoricalInfo; + int mSize; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_PROBABILITY_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h new file mode 100644 index 000000000..8ccfa33dc --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h + */ + +#ifndef LATINIME_BACKWARD_V402_PROBABILITY_ENTRY_H +#define LATINIME_BACKWARD_V402_PROBABILITY_ENTRY_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/historical_info.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class ProbabilityEntry { + public: + ProbabilityEntry(const ProbabilityEntry &probabilityEntry) + : mFlags(probabilityEntry.mFlags), mProbability(probabilityEntry.mProbability), + mHistoricalInfo(probabilityEntry.mHistoricalInfo) {} + + // Dummy entry + ProbabilityEntry() + : mFlags(0), mProbability(NOT_A_PROBABILITY), mHistoricalInfo() {} + + // Entry without historical information + ProbabilityEntry(const int flags, const int probability) + : mFlags(flags), mProbability(probability), mHistoricalInfo() {} + + // Entry with historical information. + ProbabilityEntry(const int flags, const int probability, + const HistoricalInfo *const historicalInfo) + : mFlags(flags), mProbability(probability), mHistoricalInfo(*historicalInfo) {} + + const ProbabilityEntry createEntryWithUpdatedProbability(const int probability) const { + return ProbabilityEntry(mFlags, probability, &mHistoricalInfo); + } + + const ProbabilityEntry createEntryWithUpdatedHistoricalInfo( + const HistoricalInfo *const historicalInfo) const { + return ProbabilityEntry(mFlags, mProbability, historicalInfo); + } + + bool hasHistoricalInfo() const { + return mHistoricalInfo.isValid(); + } + + int getFlags() const { + return mFlags; + } + + int getProbability() const { + return mProbability; + } + + const HistoricalInfo *getHistoricalInfo() const { + return &mHistoricalInfo; + } + + private: + // Copy constructor is public to use this class as a type of return value. + DISALLOW_ASSIGNMENT_OPERATOR(ProbabilityEntry); + + const int mFlags; + const int mProbability; + const HistoricalInfo mHistoricalInfo; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_PROBABILITY_ENTRY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.cpp new file mode 100644 index 000000000..56bc8b98d --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h" + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +void ShortcutDictContent::getShortcutEntryAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoint, int *const outCodePointCount, int *const outProbability, + bool *const outhasNext, int *const shortcutEntryPos) const { + const BufferWithExtendableBuffer *const shortcutListBuffer = getContentBuffer(); + if (*shortcutEntryPos < 0 || *shortcutEntryPos >= shortcutListBuffer->getTailPosition()) { + AKLOGE("Invalid shortcut entry position. shortcutEntryPos: %d, bufSize: %d", + *shortcutEntryPos, shortcutListBuffer->getTailPosition()); + ASSERT(false); + if (outhasNext) { + *outhasNext = false; + } + if (outCodePointCount) { + *outCodePointCount = 0; + } + return; + } + + const int shortcutFlags = shortcutListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); + if (outProbability) { + *outProbability = shortcutFlags & Ver4DictConstants::SHORTCUT_PROBABILITY_MASK; + } + if (outhasNext) { + *outhasNext = shortcutFlags & Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK; + } + if (outCodePoint && outCodePointCount) { + shortcutListBuffer->readCodePointsAndAdvancePosition( + maxCodePointCount, outCodePoint, outCodePointCount, shortcutEntryPos); + } +} + +int ShortcutDictContent::getShortcutListHeadPos(const int terminalId) const { + const SparseTable *const addressLookupTable = getAddressLookupTable(); + if (!addressLookupTable->contains(terminalId)) { + return NOT_A_DICT_POS; + } + return addressLookupTable->get(terminalId); +} + +bool ShortcutDictContent::flushToFile(const char *const dictPath) const { + return flush(dictPath, Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_FILE_EXTENSION); +} + +bool ShortcutDictContent::runGC( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ShortcutDictContent *const originalShortcutDictContent) { + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const int originalShortcutListPos = + originalShortcutDictContent->getShortcutListHeadPos(it->first); + if (originalShortcutListPos == NOT_A_DICT_POS) { + continue; + } + const int shortcutListPos = getContentBuffer()->getTailPosition(); + // Copy shortcut list from original content. + if (!copyShortcutListFromDictContent(originalShortcutListPos, originalShortcutDictContent, + shortcutListPos)) { + AKLOGE("Cannot copy shortcut list during GC. original pos: %d, pos: %d", + originalShortcutListPos, shortcutListPos); + return false; + } + // Set shortcut list position to the lookup table. + if (!getUpdatableAddressLookupTable()->set(it->second, shortcutListPos)) { + AKLOGE("Cannot set shortcut list position. terminal id: %d, pos: %d", + it->second, shortcutListPos); + return false; + } + } + return true; +} + +bool ShortcutDictContent::createNewShortcutList(const int terminalId) { + const int shortcutListListPos = getContentBuffer()->getTailPosition(); + return getUpdatableAddressLookupTable()->set(terminalId, shortcutListListPos); +} + +bool ShortcutDictContent::copyShortcutList(const int shortcutListPos, const int toPos) { + return copyShortcutListFromDictContent(shortcutListPos, this, toPos); +} + +bool ShortcutDictContent::copyShortcutListFromDictContent(const int shortcutListPos, + const ShortcutDictContent *const sourceShortcutDictContent, const int toPos) { + bool hasNext = true; + int readingPos = shortcutListPos; + int writingPos = toPos; + int codePoints[MAX_WORD_LENGTH]; + while (hasNext) { + int probability = 0; + int codePointCount = 0; + sourceShortcutDictContent->getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, + codePoints, &codePointCount, &probability, &hasNext, &readingPos); + if (!writeShortcutEntryAndAdvancePosition(codePoints, codePointCount, probability, + hasNext, &writingPos)) { + AKLOGE("Cannot write shortcut entry to copy. pos: %d", writingPos); + return false; + } + } + return true; +} + +bool ShortcutDictContent::setProbability(const int probability, const int shortcutEntryPos) { + BufferWithExtendableBuffer *const shortcutListBuffer = getWritableContentBuffer(); + const int shortcutFlags = shortcutListBuffer->readUint( + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); + const bool hasNext = shortcutFlags & Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK; + const int shortcutFlagsToWrite = createAndGetShortcutFlags(probability, hasNext); + return shortcutListBuffer->writeUint(shortcutFlagsToWrite, + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); +} + +bool ShortcutDictContent::writeShortcutEntryAndAdvancePosition(const int *const codePoint, + const int codePointCount, const int probability, const bool hasNext, + int *const shortcutEntryPos) { + BufferWithExtendableBuffer *const shortcutListBuffer = getWritableContentBuffer(); + const int shortcutFlags = createAndGetShortcutFlags(probability, hasNext); + if (!shortcutListBuffer->writeUintAndAdvancePosition(shortcutFlags, + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos)) { + AKLOGE("Cannot write shortcut flags. flags; %x, pos: %d", shortcutFlags, *shortcutEntryPos); + return false; + } + if (!shortcutListBuffer->writeCodePointsAndAdvancePosition(codePoint, codePointCount, + true /* writesTerminator */, shortcutEntryPos)) { + AKLOGE("Cannot write shortcut target code points. pos: %d", *shortcutEntryPos); + return false; + } + return true; +} + +// Find a shortcut entry that has specified target and return its position. +int ShortcutDictContent::findShortcutEntryAndGetPos(const int shortcutListPos, + const int *const targetCodePointsToFind, const int codePointCount) const { + bool hasNext = true; + int readingPos = shortcutListPos; + int targetCodePoints[MAX_WORD_LENGTH]; + while (hasNext) { + const int entryPos = readingPos; + int probability = 0; + int targetCodePointCount = 0; + getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, targetCodePoints, &targetCodePointCount, + &probability, &hasNext, &readingPos); + if (targetCodePointCount != codePointCount) { + continue; + } + bool matched = true; + for (int i = 0; i < codePointCount; ++i) { + if (targetCodePointsToFind[i] != targetCodePoints[i]) { + matched = false; + break; + } + } + if (matched) { + return entryPos; + } + } + return NOT_A_DICT_POS; +} + +int ShortcutDictContent::createAndGetShortcutFlags(const int probability, + const bool hasNext) const { + return (probability & Ver4DictConstants::SHORTCUT_PROBABILITY_MASK) + | (hasNext ? Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK : 0); +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h new file mode 100644 index 000000000..179cec5bb --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_SHORTCUT_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_SHORTCUT_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class ShortcutDictContent : public SparseTableDictContent { + public: + ShortcutDictContent(const char *const dictPath, const bool isUpdatable) + : SparseTableDictContent(dictPath, + Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_FILE_EXTENSION, isUpdatable, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {} + + ShortcutDictContent() + : SparseTableDictContent(Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {} + + void getShortcutEntry(const int maxCodePointCount, int *const outCodePoint, + int *const outCodePointCount, int *const outProbability, bool *const outhasNext, + const int shortcutEntryPos) { + int readingPos = shortcutEntryPos; + return getShortcutEntryAndAdvancePosition(maxCodePointCount, outCodePoint, + outCodePointCount, outProbability, outhasNext, &readingPos); + } + + void getShortcutEntryAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoint, int *const outCodePointCount, int *const outProbability, + bool *const outhasNext, int *const shortcutEntryPos) const; + + // Returns head position of shortcut list for a PtNode specified by terminalId. + int getShortcutListHeadPos(const int terminalId) const; + + bool flushToFile(const char *const dictPath) const; + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ShortcutDictContent *const originalShortcutDictContent); + + bool createNewShortcutList(const int terminalId); + + bool copyShortcutList(const int shortcutListPos, const int toPos); + + bool setProbability(const int probability, const int shortcutEntryPos); + + bool writeShortcutEntry(const int *const codePoint, const int codePointCount, + const int probability, const bool hasNext, const int shortcutEntryPos) { + int writingPos = shortcutEntryPos; + return writeShortcutEntryAndAdvancePosition(codePoint, codePointCount, probability, + hasNext, &writingPos); + } + + bool writeShortcutEntryAndAdvancePosition(const int *const codePoint, + const int codePointCount, const int probability, const bool hasNext, + int *const shortcutEntryPos); + + int findShortcutEntryAndGetPos(const int shortcutListPos, + const int *const targetCodePointsToFind, const int codePointCount) const; + + private: + DISALLOW_COPY_AND_ASSIGN(ShortcutDictContent); + + bool copyShortcutListFromDictContent(const int shortcutListPos, + const ShortcutDictContent *const sourceShortcutDictContent, const int toPos); + + int createAndGetShortcutFlags(const int probability, const bool hasNext) const; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_SHORTCUT_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h new file mode 100644 index 000000000..6433650b0 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_SINGLE_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_SINGLE_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class SingleDictContent : public DictContent { + public: + SingleDictContent(const char *const dictPath, const char *const contentFileName, + const bool isUpdatable) + : mMmappedBuffer(MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), + mExpandableContentBuffer(mMmappedBuffer ? mMmappedBuffer->getBuffer() : nullptr, + mMmappedBuffer ? mMmappedBuffer->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mIsValid(mMmappedBuffer) {} + + SingleDictContent() + : mMmappedBuffer(nullptr), + mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mIsValid(true) {} + + virtual ~SingleDictContent() {} + + virtual bool isValid() const { + return mIsValid; + } + + bool isNearSizeLimit() const { + return mExpandableContentBuffer.isNearSizeLimit(); + } + + protected: + BufferWithExtendableBuffer *getWritableBuffer() { + return &mExpandableContentBuffer; + } + + const BufferWithExtendableBuffer *getBuffer() const { + return &mExpandableContentBuffer; + } + + bool flush(const char *const dictPath, const char *const contentFileNameSuffix) const { + return DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + contentFileNameSuffix, &mExpandableContentBuffer); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SingleDictContent); + + const MmappedBuffer::MmappedBufferPtr mMmappedBuffer; + BufferWithExtendableBuffer mExpandableContentBuffer; + const bool mIsValid; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_SINGLE_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.cpp new file mode 100644 index 000000000..7c9b4967a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h" + +namespace latinime { +namespace backward { +namespace v402 { + +bool SparseTableDictContent::flush(const char *const dictPath, + const char *const lookupTableFileNameSuffix, const char *const addressTableFileNameSuffix, + const char *const contentFileNameSuffix) const { + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, lookupTableFileNameSuffix, + &mExpandableLookupTableBuffer)){ + return false; + } + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, addressTableFileNameSuffix, + &mExpandableAddressTableBuffer)) { + return false; + } + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, contentFileNameSuffix, + &mExpandableContentBuffer)) { + return false; + } + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h new file mode 100644 index 000000000..c7233edd3 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h + */ + +#ifndef LATINIME_BACKWARD_V402_SPARSE_TABLE_DICT_CONTENT_H +#define LATINIME_BACKWARD_V402_SPARSE_TABLE_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" +#include "suggest/policyimpl/dictionary/utils/sparse_table.h" + +namespace latinime { +namespace backward { +namespace v402 { + +// TODO: Support multiple contents. +class SparseTableDictContent : public DictContent { + public: + AK_FORCE_INLINE SparseTableDictContent(const char *const dictPath, + const char *const lookupTableFileName, const char *const addressTableFileName, + const char *const contentFileName, const bool isUpdatable, + const int sparseTableBlockSize, const int sparseTableDataSize) + : mLookupTableBuffer( + MmappedBuffer::openBuffer(dictPath, lookupTableFileName, isUpdatable)), + mAddressTableBuffer( + MmappedBuffer::openBuffer(dictPath, addressTableFileName, isUpdatable)), + mContentBuffer( + MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), + mExpandableLookupTableBuffer( + mLookupTableBuffer ? mLookupTableBuffer->getBuffer() : nullptr, + mLookupTableBuffer ? mLookupTableBuffer->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableAddressTableBuffer( + mAddressTableBuffer ? mAddressTableBuffer->getBuffer() : nullptr, + mAddressTableBuffer ? mAddressTableBuffer->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableContentBuffer(mContentBuffer ? mContentBuffer->getBuffer() : nullptr, + mContentBuffer ? mContentBuffer->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, + sparseTableBlockSize, sparseTableDataSize), + mIsValid(mLookupTableBuffer && mAddressTableBuffer && mContentBuffer) {} + + SparseTableDictContent(const int sparseTableBlockSize, const int sparseTableDataSize) + : mLookupTableBuffer(), mAddressTableBuffer(), mContentBuffer(), + mExpandableLookupTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableAddressTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, + sparseTableBlockSize, sparseTableDataSize), mIsValid(true) {} + + virtual ~SparseTableDictContent() {} + + virtual bool isValid() const { + return mIsValid; + } + + bool isNearSizeLimit() const { + return mExpandableLookupTableBuffer.isNearSizeLimit() + || mExpandableAddressTableBuffer.isNearSizeLimit() + || mExpandableContentBuffer.isNearSizeLimit(); + } + + protected: + SparseTable *getUpdatableAddressLookupTable() { + return &mAddressLookupTable; + } + + const SparseTable *getAddressLookupTable() const { + return &mAddressLookupTable; + } + + BufferWithExtendableBuffer *getWritableContentBuffer() { + return &mExpandableContentBuffer; + } + + const BufferWithExtendableBuffer *getContentBuffer() const { + return &mExpandableContentBuffer; + } + + bool flush(const char *const dictDirPath, const char *const lookupTableFileName, + const char *const addressTableFileName, const char *const contentFileName) const; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SparseTableDictContent); + + const MmappedBuffer::MmappedBufferPtr mLookupTableBuffer; + const MmappedBuffer::MmappedBufferPtr mAddressTableBuffer; + const MmappedBuffer::MmappedBufferPtr mContentBuffer; + BufferWithExtendableBuffer mExpandableLookupTableBuffer; + BufferWithExtendableBuffer mExpandableAddressTableBuffer; + BufferWithExtendableBuffer mExpandableContentBuffer; + SparseTable mAddressLookupTable; + const bool mIsValid; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_SPARSE_TABLE_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.cpp new file mode 100644 index 000000000..a9f841779 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +int TerminalPositionLookupTable::getTerminalPtNodePosition(const int terminalId) const { + if (terminalId < 0 || terminalId >= mSize) { + return NOT_A_DICT_POS; + } + const int terminalPos = getBuffer()->readUint( + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(terminalId)); + return (terminalPos == Ver4DictConstants::NOT_A_TERMINAL_ADDRESS) ? + NOT_A_DICT_POS : terminalPos; +} + +bool TerminalPositionLookupTable::setTerminalPtNodePosition( + const int terminalId, const int terminalPtNodePos) { + if (terminalId < 0) { + return NOT_A_DICT_POS; + } + while (terminalId >= mSize) { + // Write new entry. + if (!getWritableBuffer()->writeUint(Ver4DictConstants::NOT_A_TERMINAL_ADDRESS, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(mSize))) { + return false; + } + mSize++; + } + const int terminalPos = (terminalPtNodePos != NOT_A_DICT_POS) ? + terminalPtNodePos : Ver4DictConstants::NOT_A_TERMINAL_ADDRESS; + return getWritableBuffer()->writeUint(terminalPos, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(terminalId)); +} + +bool TerminalPositionLookupTable::flushToFile(const char *const dictPath) const { + // If the used buffer size is smaller than the actual buffer size, regenerate the lookup + // table and write the new table to the file. + if (getEntryPos(mSize) < getBuffer()->getTailPosition()) { + TerminalPositionLookupTable lookupTableToWrite; + for (int i = 0; i < mSize; ++i) { + const int terminalPtNodePosition = getTerminalPtNodePosition(i); + if (!lookupTableToWrite.setTerminalPtNodePosition(i, terminalPtNodePosition)) { + AKLOGE("Cannot set terminal position to lookupTableToWrite." + " terminalId: %d, position: %d", i, terminalPtNodePosition); + return false; + } + } + return lookupTableToWrite.flush(dictPath, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } else { + // We can simply use this lookup table because the buffer size has not been + // changed. + return flush(dictPath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } +} + +bool TerminalPositionLookupTable::runGCTerminalIds(TerminalIdMap *const terminalIdMap) { + int removedEntryCount = 0; + int nextNewTerminalId = 0; + for (int i = 0; i < mSize; ++i) { + const int terminalPos = getBuffer()->readUint( + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(i)); + if (terminalPos == Ver4DictConstants::NOT_A_TERMINAL_ADDRESS) { + // This entry is a garbage. + removedEntryCount++; + } else { + // Give a new terminal id to the entry. + if (!getWritableBuffer()->writeUint(terminalPos, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, + getEntryPos(nextNewTerminalId))) { + return false; + } + // Memorize the mapping to the old terminal id to the new terminal id. + terminalIdMap->insert(TerminalIdMap::value_type(i, nextNewTerminalId)); + nextNewTerminalId++; + } + } + mSize = nextNewTerminalId; + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h new file mode 100644 index 000000000..eadfe0faa --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h + */ + +#ifndef LATINIME_BACKWARD_V402_TERMINAL_POSITION_LOOKUP_TABLE_H +#define LATINIME_BACKWARD_V402_TERMINAL_POSITION_LOOKUP_TABLE_H + +#include <unordered_map> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class TerminalPositionLookupTable : public SingleDictContent { + public: + typedef std::unordered_map<int, int> TerminalIdMap; + + TerminalPositionLookupTable(const char *const dictPath, const bool isUpdatable) + : SingleDictContent(dictPath, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION, isUpdatable), + mSize(getBuffer()->getTailPosition() + / Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE) {} + + TerminalPositionLookupTable() : mSize(0) {} + + int getTerminalPtNodePosition(const int terminalId) const; + + bool setTerminalPtNodePosition(const int terminalId, const int terminalPtNodePos); + + int getNextTerminalId() const { + return mSize; + } + + bool flushToFile(const char *const dictPath) const; + + bool runGCTerminalIds(TerminalIdMap *const terminalIdMap); + + private: + DISALLOW_COPY_AND_ASSIGN(TerminalPositionLookupTable); + + int getEntryPos(const int terminalId) const { + return terminalId * Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE; + } + + int mSize; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif // LATINIME_BACKWARD_V402_TERMINAL_POSITION_LOOKUP_TABLE_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h new file mode 100644 index 000000000..941fda748 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT CHANGE THE LOGIC IN THIS FILE !!!!! + * Do not edit this file other than updating policy's interface. + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_SHORTCUT_LIST_POLICY_H +#define LATINIME_BACKWARD_V402_VER4_SHORTCUT_LIST_POLICY_H + +#include "defines.h" +#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class Ver4ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { + public: + Ver4ShortcutListPolicy(ShortcutDictContent *const shortcutDictContent, + const TerminalPositionLookupTable *const terminalPositionLookupTable) + : mShortcutDictContent(shortcutDictContent) {} + + ~Ver4ShortcutListPolicy() {} + + int getStartPos(const int pos) const { + // The first shortcut entry is located at the head position of the shortcut list. + return pos; + } + + void getNextShortcut(const int maxCodePointCount, int *const outCodePoint, + int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext, + int *const pos) const { + int probability = 0; + mShortcutDictContent->getShortcutEntryAndAdvancePosition(maxCodePointCount, + outCodePoint, outCodePointCount, &probability, outHasNext, pos); + if (outIsWhitelist) { + *outIsWhitelist = ShortcutListReadingUtils::isWhitelist(probability); + } + } + + void skipAllShortcuts(int *const pos) const { + // Do nothing because we don't need to skip shortcut lists in ver4 dictionaries. + } + + bool addNewShortcut(const int terminalId, const int *const codePoints, const int codePointCount, + const int probability) { + const int shortcutListPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + if (shortcutListPos == NOT_A_DICT_POS) { + // Create shortcut list. + if (!mShortcutDictContent->createNewShortcutList(terminalId)) { + AKLOGE("Cannot create new shortcut list. terminal id: %d", terminalId); + return false; + } + const int writingPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + return mShortcutDictContent->writeShortcutEntry(codePoints, codePointCount, probability, + false /* hasNext */, writingPos); + } + const int entryPos = mShortcutDictContent->findShortcutEntryAndGetPos(shortcutListPos, + codePoints, codePointCount); + if (entryPos == NOT_A_DICT_POS) { + // Add new entry to the shortcut list. + // Create new shortcut list. + if (!mShortcutDictContent->createNewShortcutList(terminalId)) { + AKLOGE("Cannot create new shortcut list. terminal id: %d", terminalId); + return false; + } + int writingPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + if (!mShortcutDictContent->writeShortcutEntryAndAdvancePosition(codePoints, + codePointCount, probability, true /* hasNext */, &writingPos)) { + AKLOGE("Cannot write shortcut entry. terminal id: %d, pos: %d", terminalId, + writingPos); + return false; + } + return mShortcutDictContent->copyShortcutList(shortcutListPos, writingPos); + } + // Overwrite existing entry. + bool hasNext = false; + mShortcutDictContent->getShortcutEntry(MAX_WORD_LENGTH, 0 /* outCodePoint */, + 0 /* outCodePointCount */ , 0 /* probability */, &hasNext, entryPos); + if (!mShortcutDictContent->writeShortcutEntry(codePoints, + codePointCount, probability, hasNext, entryPos)) { + AKLOGE("Cannot overwrite shortcut entry. terminal id: %d, pos: %d", terminalId, + entryPos); + return false; + } + return true; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4ShortcutListPolicy); + + ShortcutDictContent *const mShortcutDictContent; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif // LATINIME_BACKWARD_V402_VER4_SHORTCUT_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp new file mode 100644 index 000000000..93f192976 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" + +#include <cerrno> +#include <cstring> +#include <sys/stat.h> +#include <sys/types.h> + +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +/* static */ Ver4DictBuffers::Ver4DictBuffersPtr Ver4DictBuffers::openVer4DictBuffers( + const char *const dictPath, MmappedBuffer::MmappedBufferPtr headerBuffer, + const FormatUtils::FORMAT_VERSION formatVersion) { + if (!headerBuffer) { + ASSERT(false); + AKLOGE("The header buffer must be valid to open ver4 dict buffers."); + return Ver4DictBuffersPtr(nullptr); + } + // TODO: take only dictDirPath, and open both header and trie files in the constructor below + const bool isUpdatable = headerBuffer->isUpdatable(); + return Ver4DictBuffersPtr(new Ver4DictBuffers(dictPath, std::move(headerBuffer), isUpdatable, + formatVersion)); +} + +bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const { + // Create temporary directory. + const int tmpDirPathBufSize = FileUtils::getFilePathWithSuffixBufSize(dictDirPath, + DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); + char tmpDirPath[tmpDirPathBufSize]; + FileUtils::getFilePathWithSuffix(dictDirPath, + DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE, tmpDirPathBufSize, + tmpDirPath); + if (FileUtils::existsDir(tmpDirPath)) { + if (!FileUtils::removeDirAndFiles(tmpDirPath)) { + AKLOGE("Existing directory %s cannot be removed.", tmpDirPath); + ASSERT(false); + return false; + } + } + umask(S_IWGRP | S_IWOTH); + if (mkdir(tmpDirPath, S_IRWXU) == -1) { + AKLOGE("Cannot create directory: %s. errno: %d.", tmpDirPath, errno); + return false; + } + // Get dictionary base path. + const int dictNameBufSize = strlen(dictDirPath) + 1 /* terminator */; + char dictName[dictNameBufSize]; + FileUtils::getBasename(dictDirPath, dictNameBufSize, dictName); + const int dictPathBufSize = FileUtils::getFilePathBufSize(tmpDirPath, dictName); + char dictPath[dictPathBufSize]; + FileUtils::getFilePath(tmpDirPath, dictName, dictPathBufSize, dictPath); + + // Write header file. + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + Ver4DictConstants::HEADER_FILE_EXTENSION, headerBuffer)) { + AKLOGE("Dictionary header file %s%s cannot be written.", tmpDirPath, + Ver4DictConstants::HEADER_FILE_EXTENSION); + return false; + } + // Write trie file. + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + Ver4DictConstants::TRIE_FILE_EXTENSION, &mExpandableTrieBuffer)) { + AKLOGE("Dictionary trie file %s%s cannot be written.", tmpDirPath, + Ver4DictConstants::TRIE_FILE_EXTENSION); + return false; + } + // Write dictionary contents. + if (!mTerminalPositionLookupTable.flushToFile(dictPath)) { + AKLOGE("Terminal position lookup table cannot be written. %s", tmpDirPath); + return false; + } + if (!mProbabilityDictContent.flushToFile(dictPath)) { + AKLOGE("Probability dict content cannot be written. %s", tmpDirPath); + return false; + } + if (!mBigramDictContent.flushToFile(dictPath)) { + AKLOGE("Bigram dict content cannot be written. %s", tmpDirPath); + return false; + } + if (!mShortcutDictContent.flushToFile(dictPath)) { + AKLOGE("Shortcut dict content cannot be written. %s", tmpDirPath); + return false; + } + // Remove existing dictionary. + if (!FileUtils::removeDirAndFiles(dictDirPath)) { + AKLOGE("Existing directory %s cannot be removed.", dictDirPath); + ASSERT(false); + return false; + } + // Rename temporary directory. + if (rename(tmpDirPath, dictDirPath) != 0) { + AKLOGE("%s cannot be renamed to %s", tmpDirPath, dictDirPath); + ASSERT(false); + return false; + } + return true; +} + +Ver4DictBuffers::Ver4DictBuffers(const char *const dictPath, + MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable, + const FormatUtils::FORMAT_VERSION formatVersion) + : mHeaderBuffer(std::move(headerBuffer)), + mDictBuffer(MmappedBuffer::openBuffer(dictPath, + Ver4DictConstants::TRIE_FILE_EXTENSION, isUpdatable)), + mHeaderPolicy(mHeaderBuffer->getBuffer(), formatVersion), + mExpandableHeaderBuffer(mHeaderBuffer ? mHeaderBuffer->getBuffer() : nullptr, + mHeaderPolicy.getSize(), + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableTrieBuffer(mDictBuffer ? mDictBuffer->getBuffer() : nullptr, + mDictBuffer ? mDictBuffer->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mTerminalPositionLookupTable(dictPath, isUpdatable), + mProbabilityDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), isUpdatable), + mBigramDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), isUpdatable), + mShortcutDictContent(dictPath, isUpdatable), + mIsUpdatable(isUpdatable) {} + +Ver4DictBuffers::Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const int maxTrieSize) + : mHeaderBuffer(nullptr), mDictBuffer(nullptr), mHeaderPolicy(headerPolicy), + mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableTrieBuffer(maxTrieSize), mTerminalPositionLookupTable(), + mProbabilityDictContent(headerPolicy->hasHistoricalInfoOfWords()), + mBigramDictContent(headerPolicy->hasHistoricalInfoOfWords()), mShortcutDictContent(), + mIsUpdatable(true) {} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h new file mode 100644 index 000000000..e775be52e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_DICT_BUFFER_H +#define LATINIME_BACKWARD_V402_VER4_DICT_BUFFER_H + +#include <memory> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/shortcut_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +class Ver4DictBuffers { + public: + typedef std::unique_ptr<Ver4DictBuffers> Ver4DictBuffersPtr; + + static Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath, + MmappedBuffer::MmappedBufferPtr headerBuffer, + const FormatUtils::FORMAT_VERSION formatVersion); + + static AK_FORCE_INLINE Ver4DictBuffersPtr createVer4DictBuffers( + const HeaderPolicy *const headerPolicy, const int maxTrieSize) { + return Ver4DictBuffersPtr(new Ver4DictBuffers(headerPolicy, maxTrieSize)); + } + + AK_FORCE_INLINE bool isValid() const { + return mHeaderBuffer && mDictBuffer && mHeaderPolicy.isValid() + && mProbabilityDictContent.isValid() && mTerminalPositionLookupTable.isValid() + && mBigramDictContent.isValid() && mShortcutDictContent.isValid(); + } + + AK_FORCE_INLINE bool isNearSizeLimit() const { + return mExpandableTrieBuffer.isNearSizeLimit() + || mTerminalPositionLookupTable.isNearSizeLimit() + || mProbabilityDictContent.isNearSizeLimit() + || mBigramDictContent.isNearSizeLimit() + || mShortcutDictContent.isNearSizeLimit(); + } + + AK_FORCE_INLINE const HeaderPolicy *getHeaderPolicy() const { + return &mHeaderPolicy; + } + + AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableHeaderBuffer() { + return &mExpandableHeaderBuffer; + } + + AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableTrieBuffer() { + return &mExpandableTrieBuffer; + } + + AK_FORCE_INLINE const BufferWithExtendableBuffer *getTrieBuffer() const { + return &mExpandableTrieBuffer; + } + + AK_FORCE_INLINE TerminalPositionLookupTable *getMutableTerminalPositionLookupTable() { + return &mTerminalPositionLookupTable; + } + + AK_FORCE_INLINE const TerminalPositionLookupTable *getTerminalPositionLookupTable() const { + return &mTerminalPositionLookupTable; + } + + AK_FORCE_INLINE ProbabilityDictContent *getMutableProbabilityDictContent() { + return &mProbabilityDictContent; + } + + AK_FORCE_INLINE const ProbabilityDictContent *getProbabilityDictContent() const { + return &mProbabilityDictContent; + } + + AK_FORCE_INLINE BigramDictContent *getMutableBigramDictContent() { + return &mBigramDictContent; + } + + AK_FORCE_INLINE const BigramDictContent *getBigramDictContent() const { + return &mBigramDictContent; + } + + AK_FORCE_INLINE ShortcutDictContent *getMutableShortcutDictContent() { + return &mShortcutDictContent; + } + + AK_FORCE_INLINE const ShortcutDictContent *getShortcutDictContent() const { + return &mShortcutDictContent; + } + + AK_FORCE_INLINE bool isUpdatable() const { + return mIsUpdatable; + } + + bool flush(const char *const dictDirPath) const { + return flushHeaderAndDictBuffers(dictDirPath, &mExpandableHeaderBuffer); + } + + bool flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const; + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers); + + Ver4DictBuffers(const char *const dictDirPath, + const MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable, + const FormatUtils::FORMAT_VERSION formatVersion); + + Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const int maxTrieSize); + + const MmappedBuffer::MmappedBufferPtr mHeaderBuffer; + const MmappedBuffer::MmappedBufferPtr mDictBuffer; + const HeaderPolicy mHeaderPolicy; + BufferWithExtendableBuffer mExpandableHeaderBuffer; + BufferWithExtendableBuffer mExpandableTrieBuffer; + TerminalPositionLookupTable mTerminalPositionLookupTable; + ProbabilityDictContent mProbabilityDictContent; + BigramDictContent mBigramDictContent; + ShortcutDictContent mShortcutDictContent; + const int mIsUpdatable; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_DICT_BUFFER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.cpp new file mode 100644 index 000000000..81d85f495 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" + +namespace latinime { +namespace backward { +namespace v402 { + +// These values MUST match the definitions in FormatSpec.java. +const char *const Ver4DictConstants::TRIE_FILE_EXTENSION = ".trie"; +const char *const Ver4DictConstants::HEADER_FILE_EXTENSION = ".header"; +const char *const Ver4DictConstants::FREQ_FILE_EXTENSION = ".freq"; +// tat = Terminal Address Table +const char *const Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat"; +const char *const Ver4DictConstants::BIGRAM_FILE_EXTENSION = ".bigram_freq"; +const char *const Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION = ".bigram_lookup"; +const char *const Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION = ".bigram_index_freq"; +const char *const Ver4DictConstants::SHORTCUT_FILE_EXTENSION = ".shortcut_shortcut"; +const char *const Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION = ".shortcut_lookup"; +const char *const Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION = + ".shortcut_index_shortcut"; + +// Version 4 dictionary size is implicitly limited to 8MB due to 3-byte offsets. +const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 8 * 1024 * 1024; +// Extended region size, which is not GCed region size in dict file + additional buffer size, is +// limited to 1MB to prevent from inefficient traversing. +const int Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE = 1 * 1024 * 1024; + +const int Ver4DictConstants::NOT_A_TERMINAL_ID = -1; +const int Ver4DictConstants::PROBABILITY_SIZE = 1; +const int Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE = 1; +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::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16; +const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE = 4; +const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64; +const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE = 4; + +const int Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE = 3; +// Unsigned int max value of BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE-byte is used for representing +// invalid terminal ID in bigram lists. +const int Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID = + (1 << (BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE * 8)) - 1; +const int Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE = 1; +const int Ver4DictConstants::BIGRAM_PROBABILITY_MASK = 0x0F; +const int Ver4DictConstants::BIGRAM_HAS_NEXT_MASK = 0x80; +const int Ver4DictConstants::BIGRAM_LARGE_PROBABILITY_FIELD_SIZE = 1; + +const int Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE = 1; +const int Ver4DictConstants::SHORTCUT_PROBABILITY_MASK = 0x0F; +const int Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK = 0x80; + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h new file mode 100644 index 000000000..88ebd6a75 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_DICT_CONSTANTS_H +#define LATINIME_BACKWARD_V402_VER4_DICT_CONSTANTS_H + +#include "defines.h" + +namespace latinime { +namespace backward { +namespace v402 { + +// TODO: Create PtConstants under the pt_common and move some constant values there. +// Note that there are corresponding definitions in FormatSpec.java. +class Ver4DictConstants { + public: + static const char *const TRIE_FILE_EXTENSION; + static const char *const HEADER_FILE_EXTENSION; + static const char *const FREQ_FILE_EXTENSION; + static const char *const TERMINAL_ADDRESS_TABLE_FILE_EXTENSION; + static const char *const BIGRAM_FILE_EXTENSION; + static const char *const BIGRAM_LOOKUP_TABLE_FILE_EXTENSION; + static const char *const BIGRAM_CONTENT_TABLE_FILE_EXTENSION; + static const char *const SHORTCUT_FILE_EXTENSION; + static const char *const SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION; + static const char *const SHORTCUT_CONTENT_TABLE_FILE_EXTENSION; + + static const int MAX_DICTIONARY_SIZE; + static const int MAX_DICT_EXTENDED_REGION_SIZE; + + static const int NOT_A_TERMINAL_ID; + static const int PROBABILITY_SIZE; + static const int FLAGS_IN_PROBABILITY_FILE_SIZE; + static const int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE; + static const int NOT_A_TERMINAL_ADDRESS; + static const int TERMINAL_ID_FIELD_SIZE; + static const int TIME_STAMP_FIELD_SIZE; + static const int WORD_LEVEL_FIELD_SIZE; + static const int WORD_COUNT_FIELD_SIZE; + + static const int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE; + static const int BIGRAM_ADDRESS_TABLE_DATA_SIZE; + static const int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE; + static const int SHORTCUT_ADDRESS_TABLE_DATA_SIZE; + + static const int BIGRAM_FLAGS_FIELD_SIZE; + static const int BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + static const int INVALID_BIGRAM_TARGET_TERMINAL_ID; + static const int BIGRAM_PROBABILITY_MASK; + static const int BIGRAM_HAS_NEXT_MASK; + // Used when bigram list has time stamp. + static const int BIGRAM_LARGE_PROBABILITY_FIELD_SIZE; + + static const int SHORTCUT_FLAGS_FIELD_SIZE; + static const int SHORTCUT_PROBABILITY_MASK; + static const int SHORTCUT_HAS_NEXT_MASK; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4DictConstants); +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_DICT_CONSTANTS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.cpp new file mode 100644 index 000000000..82399f190 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +const PtNodeParams Ver4PatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProcessMovedPtNode( + const int ptNodePos, const int siblingNodePos) const { + if (ptNodePos < 0 || ptNodePos >= mBuffer->getTailPosition()) { + // Reading invalid position because of bug or broken dictionary. + AKLOGE("Fetching PtNode info from invalid dictionary position: %d, dictionary size: %d", + ptNodePos, mBuffer->getTailPosition()); + ASSERT(false); + return PtNodeParams(); + } + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodePos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + int pos = ptNodePos; + const int headPos = ptNodePos; + if (usesAdditionalBuffer) { + pos -= mBuffer->getOriginalBufferSize(); + } + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const int parentPosOffset = + DynamicPtReadingUtils::getParentPtNodePosOffsetAndAdvancePosition( + dictBuf, &pos); + const int parentPos = + DynamicPtReadingUtils::getParentPtNodePos(parentPosOffset, headPos); + int codePoints[MAX_WORD_LENGTH]; + const int codePonitCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( + dictBuf, flags, MAX_WORD_LENGTH, codePoints, &pos); + int terminalIdFieldPos = NOT_A_DICT_POS; + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + int probability = NOT_A_PROBABILITY; + if (PatriciaTrieReadingUtils::isTerminal(flags)) { + terminalIdFieldPos = pos; + if (usesAdditionalBuffer) { + terminalIdFieldPos += mBuffer->getOriginalBufferSize(); + } + terminalId = Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition(dictBuf, &pos); + const ProbabilityEntry probabilityEntry = + mProbabilityDictContent->getProbabilityEntry(terminalId); + if (probabilityEntry.hasHistoricalInfo()) { + probability = ForgettingCurveUtils::decodeProbability( + probabilityEntry.getHistoricalInfo(), mHeaderPolicy); + } else { + probability = probabilityEntry.getProbability(); + } + } + int childrenPosFieldPos = pos; + if (usesAdditionalBuffer) { + childrenPosFieldPos += mBuffer->getOriginalBufferSize(); + } + int childrenPos = DynamicPtReadingUtils::readChildrenPositionAndAdvancePosition( + dictBuf, &pos); + if (usesAdditionalBuffer && childrenPos != NOT_A_DICT_POS) { + childrenPos += mBuffer->getOriginalBufferSize(); + } + if (usesAdditionalBuffer) { + pos += mBuffer->getOriginalBufferSize(); + } + // Sibling position is the tail position of original PtNode. + int newSiblingNodePos = (siblingNodePos == NOT_A_DICT_POS) ? pos : siblingNodePos; + // Read destination node if the read node is a moved node. + if (DynamicPtReadingUtils::isMoved(flags)) { + // The destination position is stored at the same place as the parent position. + return fetchPtNodeInfoFromBufferAndProcessMovedPtNode(parentPos, newSiblingNodePos); + } else { + return PtNodeParams(headPos, flags, parentPos, codePonitCount, codePoints, + terminalIdFieldPos, terminalId, probability, childrenPosFieldPos, childrenPos, + newSiblingNodePos); + } +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h new file mode 100644 index 000000000..4032a67fa --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_READER_H +#define LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_READER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class BufferWithExtendableBuffer; +namespace backward { +namespace v402 { +} // namespace v402 +} // namespace backward +class HeaderPolicy; +namespace backward { +namespace v402 { +class ProbabilityDictContent; + +/* + * This class is used for helping to read nodes of ver4 patricia trie. This class handles moved + * node and reads node attributes including probability form probabilityBuffer. + */ +class Ver4PatriciaTrieNodeReader : public PtNodeReader { + public: + Ver4PatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer, + const ProbabilityDictContent *const probabilityDictContent, + const HeaderPolicy *const headerPolicy) + : mBuffer(buffer), mProbabilityDictContent(probabilityDictContent), + mHeaderPolicy(headerPolicy) {} + + ~Ver4PatriciaTrieNodeReader() {} + + virtual const PtNodeParams fetchNodeInfoInBufferFromPtNodePos(const int ptNodePos) const { + return fetchPtNodeInfoFromBufferAndProcessMovedPtNode(ptNodePos, + NOT_A_DICT_POS /* siblingNodePos */); + } + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeReader); + + const BufferWithExtendableBuffer *const mBuffer; + const ProbabilityDictContent *const mProbabilityDictContent; + const HeaderPolicy *const mHeaderPolicy; + + const PtNodeParams fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos, + const int siblingNodePos) const; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_READER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp new file mode 100644 index 000000000..4220a9561 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h" + +#include "suggest/core/dictionary/property/unigram_property.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +const int Ver4PatriciaTrieNodeWriter::CHILDREN_POSITION_FIELD_SIZE = 3; + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsDeleted( + const PtNodeParams *const toBeUpdatedPtNodeParams) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */, + true /* isDeleted */, false /* willBecomeNonTerminal */); + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + // Update flags. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos)) { + return false; + } + if (toBeUpdatedPtNodeParams->isTerminal()) { + // The PtNode is a terminal. Delete entry from the terminal position lookup table. + return mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + toBeUpdatedPtNodeParams->getTerminalId(), NOT_A_DICT_POS /* ptNodePos */); + } else { + return true; + } +} + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsMoved( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const int movedPos, const int bigramLinkedNodePos) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */, + false /* isDeleted */, false /* willBecomeNonTerminal */); + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + // Update flags. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos)) { + return false; + } + // Update moved position, which is stored in the parent offset field. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition( + mTrieBuffer, movedPos, toBeUpdatedPtNodeParams->getHeadPos(), &writingPos)) { + return false; + } + if (toBeUpdatedPtNodeParams->hasChildren()) { + // Update children's parent position. + mReadingHelper.initWithPtNodeArrayPos(toBeUpdatedPtNodeParams->getChildrenPos()); + while (!mReadingHelper.isEnd()) { + const PtNodeParams childPtNodeParams(mReadingHelper.getPtNodeParams()); + int parentOffsetFieldPos = childPtNodeParams.getHeadPos() + + DynamicPtWritingUtils::NODE_FLAG_FIELD_SIZE; + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition( + mTrieBuffer, bigramLinkedNodePos, childPtNodeParams.getHeadPos(), + &parentOffsetFieldPos)) { + // Parent offset cannot be written because of a bug or a broken dictionary; thus, + // we give up to update dictionary. + return false; + } + mReadingHelper.readNextSiblingNode(childPtNodeParams); + } + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsWillBecomeNonTerminal( + const PtNodeParams *const toBeUpdatedPtNodeParams) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */, + false /* isDeleted */, true /* willBecomeNonTerminal */); + if (!mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + toBeUpdatedPtNodeParams->getTerminalId(), NOT_A_DICT_POS /* ptNodePos */)) { + AKLOGE("Cannot update terminal position lookup table. terminal id: %d", + toBeUpdatedPtNodeParams->getTerminalId()); + return false; + } + // Update flags. + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + return DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos); +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeUnigramProperty( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const UnigramProperty *const unigramProperty) { + // Update probability and historical information. + // TODO: Update other information in the unigram property. + if (!toBeUpdatedPtNodeParams->isTerminal()) { + return false; + } + const ProbabilityEntry originalProbabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId()); + const ProbabilityEntry probabilityEntry = createUpdatedEntryFrom(&originalProbabilityEntry, + unigramProperty); + return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry); +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + const PtNodeParams *const toBeUpdatedPtNodeParams, bool *const outNeedsToKeepPtNode) { + if (!toBeUpdatedPtNodeParams->isTerminal()) { + AKLOGE("updatePtNodeProbabilityAndGetNeedsToSaveForGC is called for non-terminal PtNode."); + return false; + } + const ProbabilityEntry originalProbabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId()); + if (originalProbabilityEntry.hasHistoricalInfo()) { + const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( + originalProbabilityEntry.getHistoricalInfo(), mHeaderPolicy); + const ProbabilityEntry probabilityEntry = + originalProbabilityEntry.createEntryWithUpdatedHistoricalInfo(&historicalInfo); + if (!mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry)) { + AKLOGE("Cannot write updated probability entry. terminalId: %d", + toBeUpdatedPtNodeParams->getTerminalId()); + return false; + } + const bool isValid = ForgettingCurveUtils::needsToKeep(&historicalInfo, mHeaderPolicy); + if (!isValid) { + if (!markPtNodeAsWillBecomeNonTerminal(toBeUpdatedPtNodeParams)) { + AKLOGE("Cannot mark PtNode as willBecomeNonTerminal."); + return false; + } + } + *outNeedsToKeepPtNode = isValid; + } else { + // No need to update probability. + *outNeedsToKeepPtNode = true; + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::updateChildrenPosition( + const PtNodeParams *const toBeUpdatedPtNodeParams, const int newChildrenPosition) { + int childrenPosFieldPos = toBeUpdatedPtNodeParams->getChildrenPosFieldPos(); + return DynamicPtWritingUtils::writeChildrenPositionAndAdvancePosition(mTrieBuffer, + newChildrenPosition, &childrenPosFieldPos); +} + +bool Ver4PatriciaTrieNodeWriter::updateTerminalId(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newTerminalId) { + return mTrieBuffer->writeUint(newTerminalId, Ver4DictConstants::TERMINAL_ID_FIELD_SIZE, + toBeUpdatedPtNodeParams->getTerminalIdFieldPos()); +} + +bool Ver4PatriciaTrieNodeWriter::writePtNodeAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const ptNodeWritingPos) { + return writePtNodeAndGetTerminalIdAndAdvancePosition(ptNodeParams, 0 /* outTerminalId */, + ptNodeWritingPos); +} + + +bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( + const PtNodeParams *const ptNodeParams, const UnigramProperty *const unigramProperty, + int *const ptNodeWritingPos) { + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + if (!writePtNodeAndGetTerminalIdAndAdvancePosition(ptNodeParams, &terminalId, + ptNodeWritingPos)) { + return false; + } + // Write probability. + ProbabilityEntry newProbabilityEntry; + const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( + &newProbabilityEntry, unigramProperty); + return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry(terminalId, + &probabilityEntryToWrite); +} + +bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry( + const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam, + const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { + if (!mBigramPolicy->addNewEntry(sourcePtNodeParams->getTerminalId(), + targetPtNodeParam->getTerminalId(), bigramProperty, outAddedNewBigram)) { + AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", + sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); + return false; + } + if (!sourcePtNodeParams->hasBigrams()) { + // Update has bigrams flag. + return updatePtNodeFlags(sourcePtNodeParams->getHeadPos(), + sourcePtNodeParams->isBlacklisted(), sourcePtNodeParams->isNotAWord(), + sourcePtNodeParams->isTerminal(), sourcePtNodeParams->hasShortcutTargets(), + true /* hasBigrams */, + sourcePtNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::removeBigramEntry( + const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam) { + return mBigramPolicy->removeEntry(sourcePtNodeParams->getTerminalId(), + targetPtNodeParam->getTerminalId()); +} + +bool Ver4PatriciaTrieNodeWriter::updateAllBigramEntriesAndDeleteUselessEntries( + const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount) { + return mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries( + sourcePtNodeParams->getTerminalId(), outBigramEntryCount); +} + +bool Ver4PatriciaTrieNodeWriter::updateAllPositionFields( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const DictPositionRelocationMap *const dictPositionRelocationMap, + int *const outBigramEntryCount) { + int parentPos = toBeUpdatedPtNodeParams->getParentPos(); + if (parentPos != NOT_A_DICT_POS) { + PtNodeWriter::PtNodePositionRelocationMap::const_iterator it = + dictPositionRelocationMap->mPtNodePositionRelocationMap.find(parentPos); + if (it != dictPositionRelocationMap->mPtNodePositionRelocationMap.end()) { + parentPos = it->second; + } + } + int writingPos = toBeUpdatedPtNodeParams->getHeadPos() + + DynamicPtWritingUtils::NODE_FLAG_FIELD_SIZE; + // Write updated parent offset. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition(mTrieBuffer, + parentPos, toBeUpdatedPtNodeParams->getHeadPos(), &writingPos)) { + return false; + } + + // Updates children position. + int childrenPos = toBeUpdatedPtNodeParams->getChildrenPos(); + if (childrenPos != NOT_A_DICT_POS) { + PtNodeWriter::PtNodeArrayPositionRelocationMap::const_iterator it = + dictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.find(childrenPos); + if (it != dictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.end()) { + childrenPos = it->second; + } + } + if (!updateChildrenPosition(toBeUpdatedPtNodeParams, childrenPos)) { + return false; + } + + // Counts bigram entries. + if (outBigramEntryCount) { + *outBigramEntryCount = mBigramPolicy->getBigramEntryConut( + toBeUpdatedPtNodeParams->getTerminalId()); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::addShortcutTarget(const PtNodeParams *const ptNodeParams, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability) { + if (!mShortcutPolicy->addNewShortcut(ptNodeParams->getTerminalId(), + targetCodePoints, targetCodePointCount, shortcutProbability)) { + AKLOGE("Cannot add new shortuct entry. terminalId: %d", ptNodeParams->getTerminalId()); + return false; + } + if (!ptNodeParams->hasShortcutTargets()) { + // Update has shortcut targets flag. + return updatePtNodeFlags(ptNodeParams->getHeadPos(), + ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), + ptNodeParams->isTerminal(), true /* hasShortcutTargets */, + ptNodeParams->hasBigrams(), + ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeHasBigramsAndShortcutTargetsFlags( + const PtNodeParams *const ptNodeParams) { + const bool hasBigrams = mBuffers->getBigramDictContent()->getBigramListHeadPos( + ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; + const bool hasShortcutTargets = mBuffers->getShortcutDictContent()->getShortcutListHeadPos( + ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; + return updatePtNodeFlags(ptNodeParams->getHeadPos(), ptNodeParams->isBlacklisted(), + ptNodeParams->isNotAWord(), ptNodeParams->isTerminal(), hasShortcutTargets, + hasBigrams, ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); +} + +bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const outTerminalId, + int *const ptNodeWritingPos) { + const int nodePos = *ptNodeWritingPos; + // Write dummy flags. The Node flags are updated with appropriate flags at the last step of the + // PtNode writing. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, + 0 /* nodeFlags */, ptNodeWritingPos)) { + return false; + } + // Calculate a parent offset and write the offset. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition(mTrieBuffer, + ptNodeParams->getParentPos(), nodePos, ptNodeWritingPos)) { + return false; + } + // Write code points + if (!DynamicPtWritingUtils::writeCodePointsAndAdvancePosition(mTrieBuffer, + ptNodeParams->getCodePoints(), ptNodeParams->getCodePointCount(), ptNodeWritingPos)) { + return false; + } + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + if (!ptNodeParams->willBecomeNonTerminal()) { + if (ptNodeParams->getTerminalId() != Ver4DictConstants::NOT_A_TERMINAL_ID) { + terminalId = ptNodeParams->getTerminalId(); + } else if (ptNodeParams->isTerminal()) { + // Write terminal information using a new terminal id. + // Get a new unused terminal id. + terminalId = mBuffers->getTerminalPositionLookupTable()->getNextTerminalId(); + } + } + const int isTerminal = terminalId != Ver4DictConstants::NOT_A_TERMINAL_ID; + if (isTerminal) { + // Update the lookup table. + if (!mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + terminalId, nodePos)) { + return false; + } + // Write terminal Id. + if (!mTrieBuffer->writeUintAndAdvancePosition(terminalId, + Ver4DictConstants::TERMINAL_ID_FIELD_SIZE, ptNodeWritingPos)) { + return false; + } + if (outTerminalId) { + *outTerminalId = terminalId; + } + } + // Write children position + if (!DynamicPtWritingUtils::writeChildrenPositionAndAdvancePosition(mTrieBuffer, + ptNodeParams->getChildrenPos(), ptNodeWritingPos)) { + return false; + } + return updatePtNodeFlags(nodePos, ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), + isTerminal, ptNodeParams->hasShortcutTargets(), ptNodeParams->hasBigrams(), + ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); +} + +const ProbabilityEntry Ver4PatriciaTrieNodeWriter::createUpdatedEntryFrom( + const ProbabilityEntry *const originalProbabilityEntry, + const UnigramProperty *const unigramProperty) const { + // TODO: Consolidate historical info and probability. + if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + const HistoricalInfo historicalInfoForUpdate(unigramProperty->getTimestamp(), + unigramProperty->getLevel(), unigramProperty->getCount()); + const HistoricalInfo updatedHistoricalInfo = + ForgettingCurveUtils::createUpdatedHistoricalInfo( + originalProbabilityEntry->getHistoricalInfo(), + unigramProperty->getProbability(), &historicalInfoForUpdate, mHeaderPolicy); + return originalProbabilityEntry->createEntryWithUpdatedHistoricalInfo( + &updatedHistoricalInfo); + } else { + return originalProbabilityEntry->createEntryWithUpdatedProbability( + unigramProperty->getProbability()); + } +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, + const bool isBlacklisted, const bool isNotAWord, const bool isTerminal, + const bool hasShortcutTargets, const bool hasBigrams, const bool hasMultipleChars) { + // Create node flags and write them. + PatriciaTrieReadingUtils::NodeFlags nodeFlags = + PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord, isTerminal, + hasShortcutTargets, hasBigrams, hasMultipleChars, + CHILDREN_POSITION_FIELD_SIZE); + if (!DynamicPtWritingUtils::writeFlags(mTrieBuffer, nodeFlags, ptNodePos)) { + AKLOGE("Cannot write PtNode flags. flags: %x, pos: %d", nodeFlags, ptNodePos); + return false; + } + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h new file mode 100644 index 000000000..08226ea26 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_WRITER_H +#define LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_WRITER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class BufferWithExtendableBuffer; +namespace backward { +namespace v402 { +} // namespace v402 +} // namespace backward +class HeaderPolicy; +namespace backward { +namespace v402 { +class Ver4BigramListPolicy; +class Ver4DictBuffers; +class Ver4PatriciaTrieNodeReader; +class Ver4PtNodeArrayReader; +class Ver4ShortcutListPolicy; + +/* + * This class is used for helping to writes nodes of ver4 patricia trie. + */ +class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { + public: + Ver4PatriciaTrieNodeWriter(BufferWithExtendableBuffer *const trieBuffer, + Ver4DictBuffers *const buffers, const HeaderPolicy *const headerPolicy, + const PtNodeReader *const ptNodeReader, + const PtNodeArrayReader *const ptNodeArrayReader, + Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy) + : mTrieBuffer(trieBuffer), mBuffers(buffers), mHeaderPolicy(headerPolicy), + mReadingHelper(ptNodeReader, ptNodeArrayReader), mBigramPolicy(bigramPolicy), + mShortcutPolicy(shortcutPolicy) {} + + virtual ~Ver4PatriciaTrieNodeWriter() {} + + virtual bool markPtNodeAsDeleted(const PtNodeParams *const toBeUpdatedPtNodeParams); + + virtual bool markPtNodeAsMoved(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int movedPos, const int bigramLinkedNodePos); + + virtual bool markPtNodeAsWillBecomeNonTerminal( + const PtNodeParams *const toBeUpdatedPtNodeParams); + + virtual bool updatePtNodeUnigramProperty(const PtNodeParams *const toBeUpdatedPtNodeParams, + const UnigramProperty *const unigramProperty); + + virtual bool updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + const PtNodeParams *const toBeUpdatedPtNodeParams, bool *const outNeedsToKeepPtNode); + + virtual bool updateChildrenPosition(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newChildrenPosition); + + bool updateTerminalId(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newTerminalId); + + virtual bool writePtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + int *const ptNodeWritingPos); + + virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos); + + virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, + bool *const outAddedNewBigram); + + virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam); + + virtual bool updateAllBigramEntriesAndDeleteUselessEntries( + const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount); + + virtual bool updateAllPositionFields(const PtNodeParams *const toBeUpdatedPtNodeParams, + const DictPositionRelocationMap *const dictPositionRelocationMap, + int *const outBigramEntryCount); + + virtual bool addShortcutTarget(const PtNodeParams *const ptNodeParams, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability); + + bool updatePtNodeHasBigramsAndShortcutTargetsFlags(const PtNodeParams *const ptNodeParams); + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeWriter); + + bool writePtNodeAndGetTerminalIdAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const outTerminalId, + int *const ptNodeWritingPos); + + // Create updated probability entry using given unigram property. In addition to the + // probability, this method updates historical information if needed. + // TODO: Update flags belonging to the unigram property. + const ProbabilityEntry createUpdatedEntryFrom( + const ProbabilityEntry *const originalProbabilityEntry, + const UnigramProperty *const unigramProperty) const; + + bool updatePtNodeFlags(const int ptNodePos, const bool isBlacklisted, const bool isNotAWord, + const bool isTerminal, const bool hasShortcutTargets, const bool hasBigrams, + const bool hasMultipleChars); + + static const int CHILDREN_POSITION_FIELD_SIZE; + + BufferWithExtendableBuffer *const mTrieBuffer; + Ver4DictBuffers *const mBuffers; + const HeaderPolicy *const mHeaderPolicy; + DynamicPtReadingHelper mReadingHelper; + Ver4BigramListPolicy *const mBigramPolicy; + Ver4ShortcutListPolicy *const mShortcutPolicy; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_NODE_WRITER_H */ 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 new file mode 100644 index 000000000..805820b05 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT CHANGE THE LOGIC IN THIS FILE !!!!! + * Do not edit this file other than updating policy's interface. + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h" + +#include <vector> + +#include "suggest/core/dicnode/dic_node.h" +#include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/property/bigram_property.h" +#include "suggest/core/dictionary/property/unigram_property.h" +#include "suggest/core/dictionary/property/word_property.h" +#include "suggest/core/session/prev_words_info.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +// Note that there are corresponding definitions in Java side in BinaryDictionaryTests and +// BinaryDictionaryDecayingTests. +const char *const Ver4PatriciaTriePolicy::UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::BIGRAM_COUNT_QUERY = "BIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; +const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024; +const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS = + Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; + +void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode, + DicNodeVector *const childDicNodes) const { + if (!dicNode->hasChildren()) { + return; + } + DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); + readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPtNodeArrayPos()); + while (!readingHelper.isEnd()) { + const PtNodeParams ptNodeParams = readingHelper.getPtNodeParams(); + if (!ptNodeParams.isValid()) { + break; + } + bool isTerminal = ptNodeParams.isTerminal() && !ptNodeParams.isDeleted(); + if (isTerminal && mHeaderPolicy->isDecayingDict()) { + // A DecayingDict may have a terminal PtNode that has a terminal DicNode whose + // probability is NOT_A_PROBABILITY. In such case, we don't want to treat it as a + // valid terminal DicNode. + isTerminal = ptNodeParams.getProbability() != NOT_A_PROBABILITY; + } + readingHelper.readNextSiblingNode(ptNodeParams); + if (ptNodeParams.representsNonWordInfo()) { + // Skip PtNodes that represent non-word information. + continue; + } + childDicNodes->pushLeavingChild(dicNode, ptNodeParams.getHeadPos(), + ptNodeParams.getChildrenPos(), ptNodeParams.getProbability(), isTerminal, + ptNodeParams.hasChildren(), + ptNodeParams.isBlacklisted() + || ptNodeParams.isNotAWord() /* isBlacklistedOrNotAWord */, + ptNodeParams.getCodePointCount(), ptNodeParams.getCodePoints()); + } + if (readingHelper.isError()) { + mIsCorrupted = true; + AKLOGE("Dictionary reading error in createAndGetAllChildDicNodes()."); + } +} + +int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints, + int *const outUnigramProbability) const { + DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); + readingHelper.initWithPtNodePos(ptNodePos); + const int codePointCount = readingHelper.getCodePointsAndProbabilityAndReturnCodePointCount( + maxCodePointCount, outCodePoints, outUnigramProbability); + if (readingHelper.isError()) { + mIsCorrupted = true; + AKLOGE("Dictionary reading error in getCodePointsAndProbabilityAndReturnCodePointCount()."); + } + return codePointCount; +} + +int Ver4PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord, + const int length, const bool forceLowerCaseSearch) const { + DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); + const int ptNodePos = + readingHelper.getTerminalPtNodePositionOfWord(inWord, length, forceLowerCaseSearch); + if (readingHelper.isError()) { + mIsCorrupted = true; + AKLOGE("Dictionary reading error in createAndGetAllChildDicNodes()."); + } + return ptNodePos; +} + +int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability, + const int bigramProbability) const { + if (mHeaderPolicy->isDecayingDict()) { + // Both probabilities are encoded. Decode them and get probability. + return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability); + } else { + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (bigramProbability == NOT_A_PROBABILITY) { + return ProbabilityUtils::backoff(unigramProbability); + } else { + return bigramProbability; + } + } +} + +int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_PROBABILITY; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { + return NOT_A_PROBABILITY; + } + return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY); +} + +int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_DICT_POS; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted()) { + return NOT_A_DICT_POS; + } + return mBuffers->getShortcutDictContent()->getShortcutListHeadPos( + ptNodeParams.getTerminalId()); +} + +int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_DICT_POS; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted()) { + return NOT_A_DICT_POS; + } + return mBuffers->getBigramDictContent()->getBigramListHeadPos( + ptNodeParams.getTerminalId()); +} + +bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int length, + const UnigramProperty *const unigramProperty) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: addUnigramEntry() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + if (length > MAX_WORD_LENGTH) { + AKLOGE("The word is too long to insert to the dictionary, length: %d", length); + return false; + } + for (const auto &shortcut : unigramProperty->getShortcuts()) { + if (shortcut.getTargetCodePoints()->size() > MAX_WORD_LENGTH) { + AKLOGE("One of shortcut targets is too long to insert to the dictionary, length: %d", + shortcut.getTargetCodePoints()->size()); + return false; + } + } + DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); + bool addedNewUnigram = false; + int codePointsToAdd[MAX_WORD_LENGTH]; + int codePointCountToAdd = length; + memmove(codePointsToAdd, word, sizeof(int) * length); + if (unigramProperty->representsBeginningOfSentence()) { + codePointCountToAdd = CharUtils::attachBeginningOfSentenceMarker(codePointsToAdd, + codePointCountToAdd, MAX_WORD_LENGTH); + } + if (codePointCountToAdd <= 0) { + return false; + } + if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointsToAdd, codePointCountToAdd, + unigramProperty, &addedNewUnigram)) { + if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { + mUnigramCount++; + } + if (unigramProperty->getShortcuts().size() > 0) { + // Add shortcut target. + const int wordPos = getTerminalPtNodePositionOfWord(word, length, + false /* forceLowerCaseSearch */); + if (wordPos == NOT_A_DICT_POS) { + AKLOGE("Cannot find terminal PtNode position to add shortcut target."); + return false; + } + for (const auto &shortcut : unigramProperty->getShortcuts()) { + if (!mUpdatingHelper.addShortcutTarget(wordPos, + shortcut.getTargetCodePoints()->data(), + shortcut.getTargetCodePoints()->size(), shortcut.getProbability())) { + AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %d, " + "probability: %d", wordPos, shortcut.getTargetCodePoints()->size(), + shortcut.getProbability()); + return false; + } + } + } + return true; + } else { + return false; + } +} + +bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + if (!prevWordsInfo->isValid()) { + AKLOGE("prev words info is not valid for adding n-gram entry to the dictionary."); + return false; + } + if (bigramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { + AKLOGE("The word is too long to insert the ngram to the dictionary. " + "length: %d", bigramProperty->getTargetCodePoints()->size()); + return false; + } + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSearch */); + // TODO: Support N-gram. + if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { + if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { + const std::vector<UnigramProperty::ShortcutProperty> shortcuts; + const UnigramProperty beginningOfSentenceUnigramProperty( + true /* representsBeginningOfSentence */, true /* isNotAWord */, + false /* isBlacklisted */, MAX_PROBABILITY /* probability */, + NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts); + if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), + prevWordsInfo->getNthPrevWordCodePointCount(1 /* n */), + &beginningOfSentenceUnigramProperty)) { + AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); + return false; + } + // Refresh Terminal PtNode positions. + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSearch */); + } else { + return false; + } + } + const int word1Pos = getTerminalPtNodePositionOfWord( + bigramProperty->getTargetCodePoints()->data(), + bigramProperty->getTargetCodePoints()->size(), false /* forceLowerCaseSearch */); + if (word1Pos == NOT_A_DICT_POS) { + return false; + } + bool addedNewBigram = false; + if (mUpdatingHelper.addBigramWords(prevWordsPtNodePos[0], word1Pos, bigramProperty, + &addedNewBigram)) { + if (addedNewBigram) { + mBigramCount++; + } + return true; + } else { + return false; + } +} + +bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const int *const word, const int length) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + if (!prevWordsInfo->isValid()) { + AKLOGE("prev words info is not valid for removing n-gram entry form the dictionary."); + return false; + } + if (length > MAX_WORD_LENGTH) { + AKLOGE("word is too long to remove n-gram entry form the dictionary. length: %d", length); + } + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSerch */); + // TODO: Support N-gram. + if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { + return false; + } + const int wordPos = getTerminalPtNodePositionOfWord(word, length, + false /* forceLowerCaseSearch */); + if (wordPos == NOT_A_DICT_POS) { + return false; + } + if (mUpdatingHelper.removeBigramWords(prevWordsPtNodePos[0], wordPos)) { + mBigramCount--; + return true; + } else { + return false; + } +} + +bool Ver4PatriciaTriePolicy::flush(const char *const filePath) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); + return false; + } + if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) { + AKLOGE("Cannot flush the dictionary to file."); + mIsCorrupted = true; + return false; + } + return true; +} + +bool Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); + return false; + } + if (!mWritingHelper.writeToDictFileWithGC(getRootPosition(), filePath)) { + AKLOGE("Cannot flush the dictionary to file with GC."); + mIsCorrupted = true; + return false; + } + return true; +} + +bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary."); + return false; + } + if (mBuffers->isNearSizeLimit()) { + // Additional buffer size is near the limit. + return true; + } else if (mHeaderPolicy->getExtendedRegionSize() + mDictBuffer->getUsedAdditionalBufferSize() + > Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE) { + // Total extended region size of the trie exceeds the limit. + return true; + } else if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS + && mDictBuffer->getUsedAdditionalBufferSize() > 0) { + // Needs to reduce dictionary size. + return true; + } else if (mHeaderPolicy->isDecayingDict()) { + return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mUnigramCount, mBigramCount, + mHeaderPolicy); + } + return false; +} + +void Ver4PatriciaTriePolicy::getProperty(const char *const query, const int queryLength, + char *const outResult, const int maxResultLength) { + const int compareLength = queryLength + 1 /* terminator */; + if (strncmp(query, UNIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", mUnigramCount); + } else if (strncmp(query, BIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", mBigramCount); + } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", + mHeaderPolicy->isDecayingDict() ? + ForgettingCurveUtils::getUnigramCountHardLimit( + mHeaderPolicy->getMaxUnigramCount()) : + static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); + } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", + mHeaderPolicy->isDecayingDict() ? + ForgettingCurveUtils::getBigramCountHardLimit( + mHeaderPolicy->getMaxBigramCount()) : + static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); + } +} + +const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const codePoints, + const int codePointCount) const { + const int ptNodePos = getTerminalPtNodePositionOfWord(codePoints, codePointCount, + false /* forceLowerCaseSearch */); + if (ptNodePos == NOT_A_DICT_POS) { + AKLOGE("getWordProperty is called for invalid word."); + return WordProperty(); + } + const PtNodeParams ptNodeParams = mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); + std::vector<int> codePointVector(ptNodeParams.getCodePoints(), + ptNodeParams.getCodePoints() + ptNodeParams.getCodePointCount()); + const ProbabilityEntry probabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry( + ptNodeParams.getTerminalId()); + const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); + // Fetch bigram information. + std::vector<BigramProperty> bigrams; + const int bigramListPos = getBigramsPositionOfPtNode(ptNodePos); + if (bigramListPos != NOT_A_DICT_POS) { + int bigramWord1CodePoints[MAX_WORD_LENGTH]; + const BigramDictContent *const bigramDictContent = mBuffers->getBigramDictContent(); + const TerminalPositionLookupTable *const terminalPositionLookupTable = + mBuffers->getTerminalPositionLookupTable(); + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const BigramEntry bigramEntry = + bigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + const int word1TerminalId = bigramEntry.getTargetTerminalId(); + const int word1TerminalPtNodePos = + terminalPositionLookupTable->getTerminalPtNodePosition(word1TerminalId); + if (word1TerminalPtNodePos == NOT_A_DICT_POS) { + continue; + } + // Word (unigram) probability + int word1Probability = NOT_A_PROBABILITY; + const int codePointCount = getCodePointsAndProbabilityAndReturnCodePointCount( + word1TerminalPtNodePos, MAX_WORD_LENGTH, bigramWord1CodePoints, + &word1Probability); + const std::vector<int> word1(bigramWord1CodePoints, + bigramWord1CodePoints + codePointCount); + const HistoricalInfo *const historicalInfo = bigramEntry.getHistoricalInfo(); + const int probability = bigramEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability( + bigramEntry.getHistoricalInfo(), mHeaderPolicy) : + bigramEntry.getProbability(); + bigrams.emplace_back(&word1, probability, + historicalInfo->getTimeStamp(), historicalInfo->getLevel(), + historicalInfo->getCount()); + } + } + // Fetch shortcut information. + std::vector<UnigramProperty::ShortcutProperty> shortcuts; + int shortcutPos = getShortcutPositionOfPtNode(ptNodePos); + if (shortcutPos != NOT_A_DICT_POS) { + int shortcutTarget[MAX_WORD_LENGTH]; + const ShortcutDictContent *const shortcutDictContent = + mBuffers->getShortcutDictContent(); + bool hasNext = true; + while (hasNext) { + int shortcutTargetLength = 0; + int shortcutProbability = NOT_A_PROBABILITY; + shortcutDictContent->getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, shortcutTarget, + &shortcutTargetLength, &shortcutProbability, &hasNext, &shortcutPos); + const std::vector<int> target(shortcutTarget, shortcutTarget + shortcutTargetLength); + shortcuts.emplace_back(&target, shortcutProbability); + } + } + const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(), + ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), + historicalInfo->getTimeStamp(), historicalInfo->getLevel(), + historicalInfo->getCount(), &shortcuts); + return WordProperty(&codePointVector, &unigramProperty, &bigrams); +} + +int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints) { + // TODO: Return code point count like other methods. + // Null termination. + outCodePoints[0] = 0; + if (token == 0) { + mTerminalPtNodePositionsForIteratingWords.clear(); + DynamicPtReadingHelper::TraversePolicyToGetAllTerminalPtNodePositions traversePolicy( + &mTerminalPtNodePositionsForIteratingWords); + DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); + readingHelper.traverseAllPtNodesInPostorderDepthFirstManner(&traversePolicy); + } + const int terminalPtNodePositionsVectorSize = + static_cast<int>(mTerminalPtNodePositionsForIteratingWords.size()); + if (token < 0 || token >= terminalPtNodePositionsVectorSize) { + AKLOGE("Given token %d is invalid.", token); + return 0; + } + const int terminalPtNodePos = mTerminalPtNodePositionsForIteratingWords[token]; + int unigramProbability = NOT_A_PROBABILITY; + const int codePointCount = getCodePointsAndProbabilityAndReturnCodePointCount( + terminalPtNodePos, MAX_WORD_LENGTH, outCodePoints, &unigramProbability); + if (codePointCount < MAX_WORD_LENGTH) { + // Null termination. outCodePoints have to be null terminated or contain MAX_WORD_LENGTH + // code points. + outCodePoints[codePointCount] = 0; + } + const int nextToken = token + 1; + if (nextToken >= terminalPtNodePositionsVectorSize) { + // All words have been iterated. + mTerminalPtNodePositionsForIteratingWords.clear(); + return 0; + } + return nextToken; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h new file mode 100644 index 000000000..2e948ac4a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT CHANGE THE LOGIC IN THIS FILE !!!!! + * Do not edit this file other than updating policy's interface. + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_POLICY_H +#define LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_POLICY_H + +#include <vector> + +#include "defines.h" +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class DicNode; +namespace backward { +namespace v402 { +} // namespace v402 +} // namespace backward +class DicNodeVector; +namespace backward { +namespace v402 { + +class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { + public: + Ver4PatriciaTriePolicy(Ver4DictBuffers::Ver4DictBuffersPtr buffers) + : mBuffers(std::move(buffers)), mHeaderPolicy(mBuffers->getHeaderPolicy()), + mDictBuffer(mBuffers->getWritableTrieBuffer()), + mBigramPolicy(mBuffers->getMutableBigramDictContent(), + mBuffers->getTerminalPositionLookupTable(), mHeaderPolicy), + mShortcutPolicy(mBuffers->getMutableShortcutDictContent(), + mBuffers->getTerminalPositionLookupTable()), + mNodeReader(mDictBuffer, mBuffers->getProbabilityDictContent(), mHeaderPolicy), + mPtNodeArrayReader(mDictBuffer), + mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader, + &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy), + mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter), + mWritingHelper(mBuffers.get()), + mUnigramCount(mHeaderPolicy->getUnigramCount()), + mBigramCount(mHeaderPolicy->getBigramCount()), + mTerminalPtNodePositionsForIteratingWords(), mIsCorrupted(false) {}; + + AK_FORCE_INLINE int getRootPosition() const { + return 0; + } + + void createAndGetAllChildDicNodes(const DicNode *const dicNode, + DicNodeVector *const childDicNodes) const; + + int getCodePointsAndProbabilityAndReturnCodePointCount( + const int terminalPtNodePos, const int maxCodePointCount, int *const outCodePoints, + int *const outUnigramProbability) const; + + int getTerminalPtNodePositionOfWord(const int *const inWord, + const int length, const bool forceLowerCaseSearch) const; + + int getProbability(const int unigramProbability, const int bigramProbability) const; + + int getUnigramProbabilityOfPtNode(const int ptNodePos) const; + + int getShortcutPositionOfPtNode(const int ptNodePos) const; + + int getBigramsPositionOfPtNode(const int ptNodePos) const; + + const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { + return mHeaderPolicy; + } + + const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const { + return &mBigramPolicy; + } + + const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const { + return &mShortcutPolicy; + } + + bool addUnigramEntry(const int *const word, const int length, + const UnigramProperty *const unigramProperty); + + bool removeUnigramEntry(const int *const word, const int length) { + // Removing unigram entry is not supported. + return false; + } + + bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty); + + bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word1, + const int length1); + + bool flush(const char *const filePath); + + bool flushWithGC(const char *const filePath); + + bool needsToRunGC(const bool mindsBlockByGC) const; + + void getProperty(const char *const query, const int queryLength, char *const outResult, + const int maxResultLength); + + const WordProperty getWordProperty(const int *const codePoints, + const int codePointCount) const; + + int getNextWordAndNextToken(const int token, int *const outCodePoints); + + bool isCorrupted() const { + return mIsCorrupted; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTriePolicy); + + static const char *const UNIGRAM_COUNT_QUERY; + static const char *const BIGRAM_COUNT_QUERY; + static const char *const MAX_UNIGRAM_COUNT_QUERY; + static const char *const MAX_BIGRAM_COUNT_QUERY; + // When the dictionary size is near the maximum size, we have to refuse dynamic operations to + // prevent the dictionary from overflowing. + static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; + static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS; + + const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers; + const HeaderPolicy *const mHeaderPolicy; + BufferWithExtendableBuffer *const mDictBuffer; + Ver4BigramListPolicy mBigramPolicy; + Ver4ShortcutListPolicy mShortcutPolicy; + Ver4PatriciaTrieNodeReader mNodeReader; + Ver4PtNodeArrayReader mPtNodeArrayReader; + Ver4PatriciaTrieNodeWriter mNodeWriter; + DynamicPtUpdatingHelper mUpdatingHelper; + Ver4PatriciaTrieWritingHelper mWritingHelper; + int mUnigramCount; + int mBigramCount; + std::vector<int> mTerminalPtNodePositionsForIteratingWords; + mutable bool mIsCorrupted; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif // LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.cpp new file mode 100644 index 000000000..80d531198 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h" + +#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +/* static */ int Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition( + const uint8_t *const buffer, int *pos) { + return ByteArrayUtils::readUint32AndAdvancePosition(buffer, pos); +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h new file mode 100644 index 000000000..3579c26d6 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_reading_utils.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_READING_UTILS_H +#define LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_READING_UTILS_H + +#include <cstdint> + +#include "defines.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class BufferWithExtendableBuffer; +namespace backward { +namespace v402 { + +class Ver4PatriciaTrieReadingUtils { + public: + static int getTerminalIdAndAdvancePosition(const uint8_t *const buffer, + int *const pos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTrieReadingUtils); +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_READING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp new file mode 100644 index 000000000..99eed0f67 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h" + +#include <cstring> +#include <queue> + +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { +namespace backward { +namespace v402 { + +bool Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const dictDirPath, + const int unigramCount, const int bigramCount) const { + const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); + BufferWithExtendableBuffer headerBuffer( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + + mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize(); + if (!headerPolicy->fillInAndWriteHeaderToBuffer(false /* updatesLastDecayedTime */, + unigramCount, bigramCount, extendedRegionSize, &headerBuffer)) { + AKLOGE("Cannot write header structure to buffer. " + "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, " + "extendedRegionSize: %d", false, unigramCount, bigramCount, + extendedRegionSize); + return false; + } + return mBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); +} + +bool Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeArrayPos, + const char *const dictDirPath) { + const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); + Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( + Ver4DictBuffers::createVer4DictBuffers(headerPolicy, + Ver4DictConstants::MAX_DICTIONARY_SIZE)); + int unigramCount = 0; + int bigramCount = 0; + if (!runGC(rootPtNodeArrayPos, headerPolicy, dictBuffers.get(), &unigramCount, &bigramCount)) { + return false; + } + BufferWithExtendableBuffer headerBuffer( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + if (!headerPolicy->fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, + unigramCount, bigramCount, 0 /* extendedRegionSize */, &headerBuffer)) { + return false; + } + return dictBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); +} + +bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, + const HeaderPolicy *const headerPolicy, Ver4DictBuffers *const buffersToWrite, + int *const outUnigramCount, int *const outBigramCount) { + Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(), + mBuffers->getProbabilityDictContent(), headerPolicy); + Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer()); + Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(), + mBuffers->getTerminalPositionLookupTable(), headerPolicy); + Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(), + mBuffers->getTerminalPositionLookupTable()); + Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(), + mBuffers, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, + &shortcutPolicy); + + DynamicPtReadingHelper readingHelper(&ptNodeReader, &ptNodeArrayReader); + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners + ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( + &ptNodeWriter); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) { + return false; + } + const int unigramCount = traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + .getValidUnigramCount(); + const int maxUnigramCount = headerPolicy->getMaxUnigramCount(); + if (headerPolicy->isDecayingDict() && unigramCount > maxUnigramCount) { + if (!truncateUnigrams(&ptNodeReader, &ptNodeWriter, maxUnigramCount)) { + AKLOGE("Cannot remove unigrams. current: %d, max: %d", unigramCount, + maxUnigramCount); + return false; + } + } + + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners::TraversePolicyToUpdateBigramProbability + traversePolicyToUpdateBigramProbability(&ptNodeWriter); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateBigramProbability)) { + return false; + } + const int bigramCount = traversePolicyToUpdateBigramProbability.getValidBigramEntryCount(); + const int maxBigramCount = headerPolicy->getMaxBigramCount(); + if (headerPolicy->isDecayingDict() && bigramCount > maxBigramCount) { + if (!truncateBigrams(maxBigramCount)) { + AKLOGE("Cannot remove bigrams. current: %d, max: %d", bigramCount, maxBigramCount); + return false; + } + } + + // Mapping from positions in mBuffer to positions in bufferToWrite. + PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap; + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(), + buffersToWrite, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy, + &shortcutPolicy); + DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers, + buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap); + if (!readingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToPlaceAndWriteValidPtNodesToBuffer)) { + return false; + } + + // Create policy instances for the GCed dictionary. + Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(), + buffersToWrite->getProbabilityDictContent(), headerPolicy); + Ver4PtNodeArrayReader newPtNodeArrayreader(buffersToWrite->getTrieBuffer()); + Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(), + buffersToWrite->getTerminalPositionLookupTable(), headerPolicy); + Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(), + buffersToWrite->getTerminalPositionLookupTable()); + Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(), + buffersToWrite, headerPolicy, &newPtNodeReader, &newPtNodeArrayreader, &newBigramPolicy, + &newShortcutPolicy); + // Re-assign terminal IDs for valid terminal PtNodes. + TerminalPositionLookupTable::TerminalIdMap terminalIdMap; + if(!buffersToWrite->getMutableTerminalPositionLookupTable()->runGCTerminalIds( + &terminalIdMap)) { + return false; + } + // Run GC for probability dict content. + if (!buffersToWrite->getMutableProbabilityDictContent()->runGC(&terminalIdMap, + mBuffers->getProbabilityDictContent())) { + return false; + } + // Run GC for bigram dict content. + if(!buffersToWrite->getMutableBigramDictContent()->runGC(&terminalIdMap, + mBuffers->getBigramDictContent(), outBigramCount)) { + return false; + } + // Run GC for shortcut dict content. + if(!buffersToWrite->getMutableShortcutDictContent()->runGC(&terminalIdMap, + mBuffers->getShortcutDictContent())) { + return false; + } + DynamicPtReadingHelper newDictReadingHelper(&newPtNodeReader, &newPtNodeArrayreader); + newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners::TraversePolicyToUpdateAllPositionFields + traversePolicyToUpdateAllPositionFields(&newPtNodeWriter, &dictPositionRelocationMap); + if (!newDictReadingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToUpdateAllPositionFields)) { + return false; + } + newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds(&newPtNodeWriter, &terminalIdMap); + if (!newDictReadingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds)) { + return false; + } + *outUnigramCount = traversePolicyToUpdateAllPositionFields.getUnigramCount(); + return true; +} + +bool Ver4PatriciaTrieWritingHelper::truncateUnigrams( + const Ver4PatriciaTrieNodeReader *const ptNodeReader, + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, const int maxUnigramCount) { + const TerminalPositionLookupTable *const terminalPosLookupTable = + mBuffers->getTerminalPositionLookupTable(); + const int nextTerminalId = terminalPosLookupTable->getNextTerminalId(); + std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator> + priorityQueue; + for (int i = 0; i < nextTerminalId; ++i) { + const int terminalPos = terminalPosLookupTable->getTerminalPtNodePosition(i); + if (terminalPos == NOT_A_DICT_POS) { + continue; + } + const ProbabilityEntry probabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry(i); + const int probability = probabilityEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability( + probabilityEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) : + probabilityEntry.getProbability(); + priorityQueue.push(DictProbability(terminalPos, probability, + probabilityEntry.getHistoricalInfo()->getTimeStamp())); + } + + // Delete unigrams. + while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) { + const int ptNodePos = priorityQueue.top().getDictPos(); + priorityQueue.pop(); + const PtNodeParams ptNodeParams = + ptNodeReader->fetchNodeInfoInBufferFromPtNodePos(ptNodePos); + if (ptNodeParams.representsNonWordInfo()) { + continue; + } + if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) { + AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos); + return false; + } + } + return true; +} + +bool Ver4PatriciaTrieWritingHelper::truncateBigrams(const int maxBigramCount) { + const TerminalPositionLookupTable *const terminalPosLookupTable = + mBuffers->getTerminalPositionLookupTable(); + const int nextTerminalId = terminalPosLookupTable->getNextTerminalId(); + std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator> + priorityQueue; + BigramDictContent *const bigramDictContent = mBuffers->getMutableBigramDictContent(); + for (int i = 0; i < nextTerminalId; ++i) { + const int bigramListPos = bigramDictContent->getBigramListHeadPos(i); + if (bigramListPos == NOT_A_DICT_POS) { + continue; + } + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + bigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!bigramEntry.isValid()) { + continue; + } + const int probability = bigramEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability( + bigramEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) : + bigramEntry.getProbability(); + priorityQueue.push(DictProbability(entryPos, probability, + bigramEntry.getHistoricalInfo()->getTimeStamp())); + } + } + + // Delete bigrams. + while (static_cast<int>(priorityQueue.size()) > maxBigramCount) { + const int entryPos = priorityQueue.top().getDictPos(); + const BigramEntry bigramEntry = bigramDictContent->getBigramEntry(entryPos); + const BigramEntry invalidatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!bigramDictContent->writeBigramEntry(&invalidatedBigramEntry, entryPos)) { + AKLOGE("Cannot write bigram entry to remove. pos: %d", entryPos); + return false; + } + priorityQueue.pop(); + } + return true; +} + +bool Ver4PatriciaTrieWritingHelper::TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + if (!ptNodeParams->isTerminal()) { + return true; + } + TerminalPositionLookupTable::TerminalIdMap::const_iterator it = + mTerminalIdMap->find(ptNodeParams->getTerminalId()); + if (it == mTerminalIdMap->end()) { + AKLOGE("terminal Id %d is not in the terminal position map. map size: %zd", + ptNodeParams->getTerminalId(), mTerminalIdMap->size()); + return false; + } + if (!mPtNodeWriter->updateTerminalId(ptNodeParams, it->second)) { + AKLOGE("Cannot update terminal id. %d -> %d", it->first, it->second); + } + return mPtNodeWriter->updatePtNodeHasBigramsAndShortcutTargetsFlags(ptNodeParams); +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h new file mode 100644 index 000000000..9034ee656 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_writing_helper.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_WRITING_HELPER_H +#define LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_WRITING_HELPER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/content/terminal_position_lookup_table.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class HeaderPolicy; +namespace backward { +namespace v402 { +class Ver4DictBuffers; +class Ver4PatriciaTrieNodeReader; +class Ver4PatriciaTrieNodeWriter; + +class Ver4PatriciaTrieWritingHelper { + public: + Ver4PatriciaTrieWritingHelper(Ver4DictBuffers *const buffers) + : mBuffers(buffers) {} + + bool writeToDictFile(const char *const dictDirPath, const int unigramCount, + const int bigramCount) const; + + // This method cannot be const because the original dictionary buffer will be updated to detect + // useless PtNodes during GC. + bool writeToDictFileWithGC(const int rootPtNodeArrayPos, const char *const dictDirPath); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTrieWritingHelper); + + class TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + : public DynamicPtReadingHelper::TraversingEventListener { + public: + TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds( + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap) + : mPtNodeWriter(ptNodeWriter), mTerminalIdMap(terminalIdMap) {} + + bool onAscend() { return true; } + + bool onDescend(const int ptNodeArrayPos) { return true; } + + bool onReadingPtNodeArrayTail() { return true; } + + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds); + + Ver4PatriciaTrieNodeWriter *const mPtNodeWriter; + const TerminalPositionLookupTable::TerminalIdMap *const mTerminalIdMap; + }; + + // For truncateUnigrams() and truncateBigrams(). + class DictProbability { + public: + DictProbability(const int dictPos, const int probability, const int timestamp) + : mDictPos(dictPos), mProbability(probability), mTimestamp(timestamp) {} + + int getDictPos() const { + return mDictPos; + } + + int getProbability() const { + return mProbability; + } + + int getTimestamp() const { + return mTimestamp; + } + + private: + DISALLOW_DEFAULT_CONSTRUCTOR(DictProbability); + + int mDictPos; + int mProbability; + int mTimestamp; + }; + + // For truncateUnigrams() and truncateBigrams(). + class DictProbabilityComparator { + public: + bool operator()(const DictProbability &left, const DictProbability &right) { + if (left.getProbability() != right.getProbability()) { + return left.getProbability() > right.getProbability(); + } + if (left.getTimestamp() != right.getTimestamp()) { + return left.getTimestamp() < right.getTimestamp(); + } + return left.getDictPos() > right.getDictPos(); + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(DictProbabilityComparator); + }; + + bool runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, + Ver4DictBuffers *const buffersToWrite, int *const outUnigramCount, + int *const outBigramCount); + + bool truncateUnigrams(const Ver4PatriciaTrieNodeReader *const ptNodeReader, + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, const int maxUnigramCount); + + bool truncateBigrams(const int maxBigramCount); + + Ver4DictBuffers *const mBuffers; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime + +#endif /* LATINIME_BACKWARD_V402_VER4_PATRICIA_TRIE_WRITING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.cpp new file mode 100644 index 000000000..537a6d420 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.cpp @@ -0,0 +1,90 @@ +/* + * 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. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.cpp + */ + +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { +namespace backward { +namespace v402 { + +bool Ver4PtNodeArrayReader::readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos, + int *const outPtNodeCount, int *const outFirstPtNodePos) const { + if (ptNodeArrayPos < 0 || ptNodeArrayPos >= mBuffer->getTailPosition()) { + // Reading invalid position because of a bug or a broken dictionary. + AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %d", + ptNodeArrayPos, mBuffer->getTailPosition()); + ASSERT(false); + return false; + } + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodeArrayPos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + int readingPos = ptNodeArrayPos; + if (usesAdditionalBuffer) { + readingPos -= mBuffer->getOriginalBufferSize(); + } + const int ptNodeCountInArray = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( + dictBuf, &readingPos); + if (usesAdditionalBuffer) { + readingPos += mBuffer->getOriginalBufferSize(); + } + if (ptNodeCountInArray < 0) { + AKLOGE("Invalid PtNode count in an array: %d.", ptNodeCountInArray); + return false; + } + *outPtNodeCount = ptNodeCountInArray; + *outFirstPtNodePos = readingPos; + return true; +} + +bool Ver4PtNodeArrayReader::readForwardLinkAndReturnIfValid(const int forwordLinkPos, + int *const outNextPtNodeArrayPos) const { + if (forwordLinkPos < 0 || forwordLinkPos >= mBuffer->getTailPosition()) { + // Reading invalid position because of bug or broken dictionary. + AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %d", + forwordLinkPos, mBuffer->getTailPosition()); + ASSERT(false); + return false; + } + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(forwordLinkPos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + int readingPos = forwordLinkPos; + if (usesAdditionalBuffer) { + readingPos -= mBuffer->getOriginalBufferSize(); + } + const int nextPtNodeArrayOffset = + DynamicPtReadingUtils::getForwardLinkPosition(dictBuf, readingPos); + if (DynamicPtReadingUtils::isValidForwardLinkPosition(nextPtNodeArrayOffset)) { + *outNextPtNodeArrayPos = forwordLinkPos + nextPtNodeArrayOffset; + } else { + *outNextPtNodeArrayPos = NOT_A_DICT_POS; + } + return true; +} + +} // namespace v402 +} // namespace backward +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h new file mode 100644 index 000000000..4f8056801 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_pt_node_array_reader.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +/* + * !!!!! DO NOT EDIT THIS FILE !!!!! + * + * This file was generated from + * suggest/policyimpl/dictionary/structure/v4/ver4_pt_node_array_reader.h + */ + +#ifndef LATINIME_BACKWARD_V402_VER4_PT_NODE_ARRAY_READER_H +#define LATINIME_BACKWARD_V402_VER4_PT_NODE_ARRAY_READER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_array_reader.h" + +namespace latinime { +namespace backward { +namespace v402 { + +} // namespace v402 +} // namespace backward +class BufferWithExtendableBuffer; +namespace backward { +namespace v402 { + +class Ver4PtNodeArrayReader : public PtNodeArrayReader { + public: + Ver4PtNodeArrayReader(const BufferWithExtendableBuffer *const buffer) : mBuffer(buffer) {}; + + virtual bool readPtNodeArrayInfoAndReturnIfValid(const int ptNodeArrayPos, + int *const outPtNodeCount, int *const outFirstPtNodePos) const; + virtual bool readForwardLinkAndReturnIfValid(const int forwordLinkPos, + int *const outNextPtNodeArrayPos) const; + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4PtNodeArrayReader); + + const BufferWithExtendableBuffer *const mBuffer; +}; +} // namespace v402 +} // namespace backward +} // namespace latinime +#endif /* LATINIME_BACKWARD_V402_VER4_PT_NODE_ARRAY_READER_H */ 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 be7e43b98..e4b5fa267 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 @@ -19,6 +19,9 @@ #include <climits> #include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" @@ -42,7 +45,7 @@ namespace latinime { if (isUpdatable) { AKLOGE("One file dictionaries don't support updating. path: %s", path); ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } return newPolicyForFileDict(path, bufOffset, size); } @@ -52,26 +55,45 @@ namespace latinime { DictionaryStructureWithBufferPolicyFactory:: newPolicyForOnMemoryDict( const int formatVersion, const std::vector<int> &locale, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { - switch (formatVersion) { + FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion); + switch (dictFormatVersion) { case FormatUtils::VERSION_4: { - HeaderPolicy headerPolicy(FormatUtils::VERSION_4, locale, attributeMap); - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = - Ver4DictBuffers::createVer4DictBuffers(&headerPolicy, - Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); - if (!DynamicPtWritingUtils::writeEmptyDictionary( - dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { - AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new Ver4PatriciaTriePolicy(std::move(dictBuffers))); + return newPolicyForOnMemoryV4Dict<backward::v402::Ver4DictConstants, + backward::v402::Ver4DictBuffers, + backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr, + backward::v402::Ver4PatriciaTriePolicy>( + dictFormatVersion, locale, attributeMap); + } + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: + case FormatUtils::VERSION_4_DEV: { + return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( + dictFormatVersion, locale, attributeMap); } default: AKLOGE("DICT: dictionary format %d is not supported for on memory dictionary", formatVersion); break; } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; +} + +template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryV4Dict( + const FormatUtils::FORMAT_VERSION formatVersion, + const std::vector<int> &locale, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { + HeaderPolicy headerPolicy(formatVersion, locale, attributeMap); + DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy, + DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); + if (!DynamicPtWritingUtils::writeEmptyDictionary( + dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { + AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); + return nullptr; + } + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new StructurePolicy(std::move(dictBuffers))); } /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr @@ -82,42 +104,62 @@ namespace latinime { getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath); // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of // MmappedBufferPtr if the instance has the responsibility. - MmappedBuffer::MmappedBufferPtr mmappedBuffer( - MmappedBuffer::openBuffer(headerFilePath, isUpdatable)); + MmappedBuffer::MmappedBufferPtr mmappedBuffer = + MmappedBuffer::openBuffer(headerFilePath, isUpdatable); if (!mmappedBuffer) { - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } - switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), - mmappedBuffer->getBufferSize())) { + const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion( + mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize()); + switch (formatVersion) { case FormatUtils::VERSION_2: AKLOGE("Given path is a directory but the format is version 2. path: %s", path); break; case FormatUtils::VERSION_4: { - const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */; - char dictPath[dictDirPathBufSize]; - if (!FileUtils::getFilePathWithoutSuffix(headerFilePath, - Ver4DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) { - AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path); - ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( - Ver4DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer))); - if (!dictBuffers || !dictBuffers->isValid()) { - AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", - path); - ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new Ver4PatriciaTriePolicy(std::move(dictBuffers))); + return newPolicyForV4Dict<backward::v402::Ver4DictConstants, + backward::v402::Ver4DictBuffers, + backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr, + backward::v402::Ver4PatriciaTriePolicy>( + headerFilePath, formatVersion, std::move(mmappedBuffer)); + } + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: + case FormatUtils::VERSION_4_DEV: { + return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( + headerFilePath, formatVersion, std::move(mmappedBuffer)); } default: AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path); break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; +} + +template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyForV4Dict( + const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion, + MmappedBuffer::MmappedBufferPtr &&mmappedBuffer) { + const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */; + char dictPath[dictDirPathBufSize]; + if (!FileUtils::getFilePathWithoutSuffix(headerFilePath, + DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) { + AKLOGE("Dictionary file name is not valid as a ver4 dictionary. header path: %s", + headerFilePath); + ASSERT(false); + return nullptr; + } + DictBuffersPtr dictBuffers = + DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer), formatVersion); + if (!dictBuffers || !dictBuffers->isValid()) { + AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", + dictPath); + ASSERT(false); + return nullptr; + } + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new StructurePolicy(std::move(dictBuffers))); } /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr @@ -128,14 +170,16 @@ namespace latinime { MmappedBuffer::MmappedBufferPtr mmappedBuffer( MmappedBuffer::openBuffer(path, bufOffset, size, false /* isUpdatable */)); if (!mmappedBuffer) { - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize())) { case FormatUtils::VERSION_2: return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( new PatriciaTriePolicy(std::move(mmappedBuffer))); + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: + case FormatUtils::VERSION_4_DEV: AKLOGE("Given path is a file but the format is version 4. path: %s", path); break; default: @@ -143,7 +187,7 @@ namespace latinime { break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } /* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h index f71447e23..768454d8d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h @@ -22,6 +22,8 @@ #include "defines.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" namespace latinime { @@ -32,16 +34,26 @@ class DictionaryStructureWithBufferPolicyFactory { const int size, const bool isUpdatable); static DictionaryStructureWithBufferPolicy::StructurePolicyPtr - newPolicyForOnMemoryDict(const int formatVersion, - const std::vector<int> &locale, + newPolicyForOnMemoryDict(const int formatVersion, const std::vector<int> &locale, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory); + template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr + newPolicyForOnMemoryV4Dict(const FormatUtils::FORMAT_VERSION formatVersion, + const std::vector<int> &locale, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap); + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForDirectoryDict(const char *const path, const bool isUpdatable); + template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForV4Dict( + const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion, + MmappedBuffer::MmappedBufferPtr &&mmappedBuffer); + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForFileDict(const char *const path, const int bufOffset, const int size); diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp index 7d0d09631..08b4e0b5e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h index 15f924a6a..15f924a6a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp index 8f42df6d2..1f00fc6ab 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp @@ -29,10 +29,10 @@ bool DynamicPtGcEventListeners // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless // children. bool isUselessPtNode = !ptNodeParams->isTerminal(); - if (ptNodeParams->isTerminal()) { + if (ptNodeParams->isTerminal() && !ptNodeParams->representsNonWordInfo()) { bool needsToKeepPtNode = true; - if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(ptNodeParams, - &needsToKeepPtNode)) { + if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + ptNodeParams, &needsToKeepPtNode)) { AKLOGE("Cannot update PtNode probability or get needs to keep PtNode after GC."); return false; } @@ -56,7 +56,7 @@ bool DynamicPtGcEventListeners } } else { mValueStack.back() += 1; - if (ptNodeParams->isTerminal()) { + if (ptNodeParams->isTerminal() && !ptNodeParams->representsNonWordInfo()) { mValidUnigramCount += 1; } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp index e02dd5550..9e575858a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp @@ -16,6 +16,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h" +#include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" @@ -29,9 +30,8 @@ const int DynamicPtUpdatingHelper::CHILDREN_POSITION_FIELD_SIZE = 3; bool DynamicPtUpdatingHelper::addUnigramWord( DynamicPtReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, const int probability, - const bool isNotAWord, const bool isBlacklisted, const int timestamp, - bool *const outAddedNewUnigram) { + const int *const wordCodePoints, const int codePointCount, + const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram) { int parentPos = NOT_A_DICT_POS; while (!readingHelper->isEnd()) { const PtNodeParams ptNodeParams(readingHelper->getPtNodeParams()); @@ -53,20 +53,18 @@ bool DynamicPtUpdatingHelper::addUnigramWord( if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(ptNodeParams, j, wordCodePoints[matchedCodePointCount + j])) { *outAddedNewUnigram = true; - return reallocatePtNodeAndAddNewPtNodes(&ptNodeParams, j, isNotAWord, isBlacklisted, - probability, timestamp, wordCodePoints + matchedCodePointCount, + return reallocatePtNodeAndAddNewPtNodes(&ptNodeParams, j, unigramProperty, + wordCodePoints + matchedCodePointCount, codePointCount - matchedCodePointCount); } } // All characters are matched. if (codePointCount == readingHelper->getTotalCodePointCount(ptNodeParams)) { - return setPtNodeProbability(&ptNodeParams, isNotAWord, isBlacklisted, probability, - timestamp, outAddedNewUnigram); + return setPtNodeProbability(&ptNodeParams, unigramProperty, outAddedNewUnigram); } if (!ptNodeParams.hasChildren()) { *outAddedNewUnigram = true; - return createChildrenPtNodeArrayAndAChildPtNode(&ptNodeParams, - isNotAWord, isBlacklisted, probability, timestamp, + return createChildrenPtNodeArrayAndAChildPtNode(&ptNodeParams, unigramProperty, wordCodePoints + readingHelper->getTotalCodePointCount(ptNodeParams), codePointCount - readingHelper->getTotalCodePointCount(ptNodeParams)); } @@ -83,17 +81,17 @@ bool DynamicPtUpdatingHelper::addUnigramWord( return createAndInsertNodeIntoPtNodeArray(parentPos, wordCodePoints + readingHelper->getPrevTotalCodePointCount(), codePointCount - readingHelper->getPrevTotalCodePointCount(), - isNotAWord, isBlacklisted, probability, timestamp, &pos); + unigramProperty, &pos); } bool DynamicPtUpdatingHelper::addBigramWords(const int word0Pos, const int word1Pos, - const int probability, const int timestamp, bool *const outAddedNewBigram) { + const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { const PtNodeParams sourcePtNodeParams( mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word0Pos)); const PtNodeParams targetPtNodeParams( mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word1Pos)); - return mPtNodeWriter->addNewBigramEntry(&sourcePtNodeParams, &targetPtNodeParams, probability, - timestamp, outAddedNewBigram); + return mPtNodeWriter->addNewBigramEntry(&sourcePtNodeParams, &targetPtNodeParams, + bigramProperty, outAddedNewBigram); } // Remove a bigram relation from word0Pos to word1Pos. @@ -115,36 +113,34 @@ bool DynamicPtUpdatingHelper::addShortcutTarget(const int wordPos, bool DynamicPtUpdatingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, const int nodeCodePointCount, - const bool isNotAWord, const bool isBlacklisted, const int probability, - const int timestamp, int *const forwardLinkFieldPos) { + const UnigramProperty *const unigramProperty, int *const forwardLinkFieldPos) { const int newPtNodeArrayPos = mBuffer->getTailPosition(); if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, newPtNodeArrayPos, forwardLinkFieldPos)) { return false; } return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount, - isNotAWord, isBlacklisted, probability, timestamp); + unigramProperty); } -bool DynamicPtUpdatingHelper::setPtNodeProbability( - const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, - const bool isBlacklisted, const int probability, const int timestamp, - bool *const outAddedNewUnigram) { +bool DynamicPtUpdatingHelper::setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, + const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram) { if (originalPtNodeParams->isTerminal()) { // Overwrites the probability. *outAddedNewUnigram = false; - return mPtNodeWriter->updatePtNodeProbability(originalPtNodeParams, probability, timestamp); + return mPtNodeWriter->updatePtNodeUnigramProperty(originalPtNodeParams, unigramProperty); } else { // Make the node terminal and write the probability. *outAddedNewUnigram = true; const int movedPos = mBuffer->getTailPosition(); int writingPos = movedPos; const PtNodeParams ptNodeParamsToWrite(getUpdatedPtNodeParams(originalPtNodeParams, - isNotAWord, isBlacklisted, true /* isTerminal */, - originalPtNodeParams->getParentPos(), originalPtNodeParams->getCodePointCount(), - originalPtNodeParams->getCodePoints(), probability)); + unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + true /* isTerminal */, originalPtNodeParams->getParentPos(), + originalPtNodeParams->getCodePointCount(), originalPtNodeParams->getCodePoints(), + unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, - timestamp, &writingPos)) { + unigramProperty, &writingPos)) { return false; } if (!mPtNodeWriter->markPtNodeAsMoved(originalPtNodeParams, movedPos, movedPos)) { @@ -155,31 +151,30 @@ bool DynamicPtUpdatingHelper::setPtNodeProbability( } bool DynamicPtUpdatingHelper::createChildrenPtNodeArrayAndAChildPtNode( - const PtNodeParams *const parentPtNodeParams, const bool isNotAWord, - const bool isBlacklisted, const int probability, const int timestamp, + const PtNodeParams *const parentPtNodeParams, const UnigramProperty *const unigramProperty, const int *const codePoints, const int codePointCount) { const int newPtNodeArrayPos = mBuffer->getTailPosition(); if (!mPtNodeWriter->updateChildrenPosition(parentPtNodeParams, newPtNodeArrayPos)) { return false; } return createNewPtNodeArrayWithAChildPtNode(parentPtNodeParams->getHeadPos(), codePoints, - codePointCount, isNotAWord, isBlacklisted, probability, timestamp); + codePointCount, unigramProperty); } bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount, - const bool isNotAWord, const bool isBlacklisted, const int probability, - const int timestamp) { + const UnigramProperty *const unigramProperty) { int writingPos = mBuffer->getTailPosition(); if (!DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, 1 /* arraySize */, &writingPos)) { return false; } const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( - isNotAWord, isBlacklisted, true /* isTerminal */, - parentPtNodePos, nodeCodePointCount, nodeCodePoints, probability)); - if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, timestamp, - &writingPos)) { + unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), true /* isTerminal */, + parentPtNodePos, nodeCodePointCount, nodeCodePoints, + unigramProperty->getProbability())); + if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, + unigramProperty, &writingPos)) { return false; } if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, @@ -192,13 +187,13 @@ bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( // Returns whether the dictionary updating was succeeded or not. bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, - const bool isNotAWord, const bool isBlacklisted, const int probabilityOfNewPtNode, - const int timestamp, const int *const newNodeCodePoints, const int newNodeCodePointCount) { + const UnigramProperty *const unigramProperty, const int *const newNodeCodePoints, + const int newNodeCodePointCount) { // When addsExtraChild is true, split the reallocating PtNode and add new child. // Reallocating PtNode: abcde, newNode: abcxy. // abc (1st, not terminal) __ de (2nd) // \_ xy (extra child, terminal) - // Otherwise, this method makes 1st part terminal and write probabilityOfNewPtNode. + // Otherwise, this method makes 1st part terminal and write information in unigramProperty. // Reallocating PtNode: abcde, newNode: abc. // abc (1st, terminal) __ de (2nd) const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount; @@ -216,11 +211,12 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } } else { const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( - isNotAWord, isBlacklisted, true /* isTerminal */, - reallocatingPtNodeParams->getParentPos(), overlappingCodePointCount, - reallocatingPtNodeParams->getCodePoints(), probabilityOfNewPtNode)); + unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + true /* isTerminal */, reallocatingPtNodeParams->getParentPos(), + overlappingCodePointCount, reallocatingPtNodeParams->getCodePoints(), + unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, - timestamp, &writingPos)) { + unigramProperty, &writingPos)) { return false; } } @@ -244,11 +240,12 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } if (addsExtraChild) { const PtNodeParams extraChildPtNodeParams(getPtNodeParamsForNewPtNode( - isNotAWord, isBlacklisted, true /* isTerminal */, - firstPartOfReallocatedPtNodePos, newNodeCodePointCount - overlappingCodePointCount, - newNodeCodePoints + overlappingCodePointCount, probabilityOfNewPtNode)); + unigramProperty->isNotAWord(), unigramProperty->isBlacklisted(), + true /* isTerminal */, firstPartOfReallocatedPtNodePos, + newNodeCodePointCount - overlappingCodePointCount, + newNodeCodePoints + overlappingCodePointCount, unigramProperty->getProbability())); if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&extraChildPtNodeParams, - timestamp, &writingPos)) { + unigramProperty, &writingPos)) { return false; } } @@ -269,8 +266,8 @@ bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( } const PtNodeParams DynamicPtUpdatingHelper::getUpdatedPtNodeParams( - const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, - const bool isBlacklisted, const bool isTerminal, const int parentPos, + const PtNodeParams *const originalPtNodeParams, + const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, const int parentPos, const int codePointCount, const int *const codePoints, const int probability) const { const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( isBlacklisted, isNotAWord, isTerminal, originalPtNodeParams->hasShortcutTargets(), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h index 9b2815263..f10d15a9b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h @@ -22,10 +22,12 @@ namespace latinime { +class BigramProperty; class BufferWithExtendableBuffer; class DynamicPtReadingHelper; class PtNodeReader; class PtNodeWriter; +class UnigramProperty; class DynamicPtUpdatingHelper { public: @@ -37,13 +39,12 @@ class DynamicPtUpdatingHelper { // Add a word to the dictionary. If the word already exists, update the probability. bool addUnigramWord(DynamicPtReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, const int probability, - const bool isNotAWord, const bool isBlacklisted, const int timestamp, - bool *const outAddedNewUnigram); + const int *const wordCodePoints, const int codePointCount, + const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram); // Add a bigram relation from word0Pos to word1Pos. - bool addBigramWords(const int word0Pos, const int word1Pos, const int probability, - const int timestamp, bool *const outAddedNewBigram); + bool addBigramWords(const int word0Pos, const int word1Pos, + const BigramProperty *const bigramProperty, bool *const outAddedNewBigram); // Remove a bigram relation from word0Pos to word1Pos. bool removeBigramWords(const int word0Pos, const int word1Pos); @@ -62,25 +63,22 @@ class DynamicPtUpdatingHelper { PtNodeWriter *const mPtNodeWriter; bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, - const int nodeCodePointCount, const bool isNotAWord, const bool isBlacklisted, - const int probability, const int timestamp, int *const forwardLinkFieldPos); + const int nodeCodePointCount, const UnigramProperty *const unigramProperty, + int *const forwardLinkFieldPos); - bool setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, - const bool isBlacklisted, const int probability, const int timestamp, - bool *const outAddedNewUnigram); + bool setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, + const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram); bool createChildrenPtNodeArrayAndAChildPtNode(const PtNodeParams *const parentPtNodeParams, - const bool isNotAWord, const bool isBlacklisted, const int probability, - const int timestamp, const int *const codePoints, const int codePointCount); + const UnigramProperty *const unigramProperty, const int *const codePoints, + const int codePointCount); bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints, - const int nodeCodePointCount, const bool isNotAWord, const bool isBlacklisted, - const int probability, const int timestamp); + const int nodeCodePointCount, const UnigramProperty *const unigramProperty); bool reallocatePtNodeAndAddNewPtNodes( const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, - const bool isNotAWord, const bool isBlacklisted, const int probabilityOfNewPtNode, - const int timestamp, const int *const newNodeCodePoints, + const UnigramProperty *const unigramProperty, const int *const newNodeCodePoints, const int newNodeCodePointCount); const PtNodeParams getUpdatedPtNodeParams(const PtNodeParams *const originalPtNodeParams, 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 91192fc57..b2e60a837 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 @@ -23,6 +23,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "utils/char_utils.h" namespace latinime { @@ -158,6 +159,16 @@ class PtNodeParams { return PatriciaTrieReadingUtils::hasShortcutTargets(mFlags); } + AK_FORCE_INLINE bool representsNonWordInfo() const { + return getCodePointCount() > 0 && !CharUtils::isInUnicodeSpace(getCodePoints()[0]) + && isNotAWord(); + } + + AK_FORCE_INLINE int representsBeginningOfSentence() const { + return getCodePointCount() > 0 && getCodePoints()[0] == CODE_POINT_BEGINNING_OF_SENTENCE + && isNotAWord(); + } + // Parent node position AK_FORCE_INLINE int getParentPos() const { return mParentPos; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h index e843f074a..a8029f73f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h @@ -24,6 +24,9 @@ namespace latinime { +class BigramProperty; +class UnigramProperty; + // Interface class used to write PtNode information. class PtNodeWriter { public: @@ -51,8 +54,8 @@ class PtNodeWriter { virtual bool markPtNodeAsWillBecomeNonTerminal( const PtNodeParams *const toBeUpdatedPtNodeParams) = 0; - virtual bool updatePtNodeProbability(const PtNodeParams *const toBeUpdatedPtNodeParams, - const int probability, const int timestamp) = 0; + virtual bool updatePtNodeUnigramProperty(const PtNodeParams *const toBeUpdatedPtNodeParams, + const UnigramProperty *const unigramProperty) = 0; virtual bool updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( const PtNodeParams *const toBeUpdatedPtNodeParams, @@ -65,10 +68,10 @@ class PtNodeWriter { int *const ptNodeWritingPos) = 0; virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, - const int timestamp, int *const ptNodeWritingPos) = 0; + const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos) = 0; virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, + const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) = 0; virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp index 847dcdee5..91c76941c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" @@ -44,7 +44,7 @@ const int ShortcutListReadingUtils::WHITELIST_SHORTCUT_PROBABILITY = 15; } /* static */ int ShortcutListReadingUtils::readShortcutTarget( - const uint8_t *const dictRoot, const int maxLength, int *const outWord, int *const pos) { + const uint8_t *const dictRoot, const int maxLength, int *const outWord, int *const pos) { return ByteArrayUtils::readStringAndAdvancePosition(dictRoot, maxLength, outWord, pos); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h index d065bf7fd..d065bf7fd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h index a898e2afc..00bb502dc 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h @@ -21,7 +21,7 @@ #include "defines.h" #include "suggest/core/policy/dictionary_bigrams_structure_policy.h" -#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h" namespace latinime { 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 b3af1f47a..a6a470c4e 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 @@ -24,6 +24,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" +#include "utils/char_utils.h" namespace latinime { @@ -318,12 +319,15 @@ int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNod PatriciaTrieReadingUtils::readPtNodeInfo(mDictRoot, ptNodePos, getShortcutsStructurePolicy(), getBigramsStructurePolicy(), &flags, &mergedNodeCodePointCount, mergedNodeCodePoints, &probability, &childrenPos, &shortcutPos, &bigramPos, &siblingPos); - childDicNodes->pushLeavingChild(dicNode, ptNodePos, childrenPos, probability, - PatriciaTrieReadingUtils::isTerminal(flags), - PatriciaTrieReadingUtils::hasChildrenInFlags(flags), - PatriciaTrieReadingUtils::isBlacklisted(flags) - || PatriciaTrieReadingUtils::isNotAWord(flags), - mergedNodeCodePointCount, mergedNodeCodePoints); + // Skip PtNodes don't start with Unicode code point because they represent non-word information. + if (CharUtils::isInUnicodeSpace(mergedNodeCodePoints[0])) { + childDicNodes->pushLeavingChild(dicNode, ptNodePos, childrenPos, probability, + PatriciaTrieReadingUtils::isTerminal(flags), + PatriciaTrieReadingUtils::hasChildrenInFlags(flags), + PatriciaTrieReadingUtils::isBlacklisted(flags) + || PatriciaTrieReadingUtils::isNotAWord(flags), + mergedNodeCodePointCount, mergedNodeCodePoints); + } return siblingPos; } @@ -379,8 +383,8 @@ const WordProperty PatriciaTriePolicy::getWordProperty(const int *const codePoin shortcuts.emplace_back(&shortcutTarget, shortcutProbability); } } - const UnigramProperty unigramProperty(ptNodeParams.isNotAWord(), - ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), + const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(), + ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts); return WordProperty(&codePointVector, &unigramProperty, &bigrams); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index 85f46603e..dce94363a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -22,9 +22,9 @@ #include "defines.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "suggest/policyimpl/dictionary/bigram/bigram_list_policy.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/v2/ver2_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" @@ -81,35 +81,43 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return &mShortcutListPolicy; } - bool addUnigramWord(const int *const word, const int length, + bool addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty) { // This method should not be called for non-updatable dictionary. - AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); + AKLOGI("Warning: addUnigramEntry() is called for non-updatable dictionary."); return false; } - bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability, const int timestamp) { + bool removeUnigramEntry(const int *const word, const int length) { // This method should not be called for non-updatable dictionary. - AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); + AKLOGI("Warning: removeUnigramEntry() is called for non-updatable dictionary."); return false; } - bool removeBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1) { + bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty) { // This method should not be called for non-updatable dictionary. - AKLOGI("Warning: removeBigramWords() is called for non-updatable dictionary."); + AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; } - void flush(const char *const filePath) { + bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word, + const int length) { + // This method should not be called for non-updatable dictionary. + AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); + return false; + } + + bool flush(const char *const filePath) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: flush() is called for non-updatable dictionary."); + return false; } - void flushWithGC(const char *const filePath) { + bool flushWithGC(const char *const filePath) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); + return false; } bool needsToRunGC(const bool mindsBlockByGC) const { diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h index 6d2b4778c..8e16ccc05 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/shortcut/shortcut_list_policy.h @@ -21,7 +21,7 @@ #include "defines.h" #include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h" namespace latinime { diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp index 5df2096a4..7a52fd180 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h" -#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/core/dictionary/property/bigram_property.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" @@ -49,13 +50,18 @@ void Ver4BigramListPolicy::getNextBigram(int *const outBigramPos, int *const out } bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTargetTerminalId, - const int newProbability, const int timestamp, bool *const outAddedNewEntry) { + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) { + // 1. The word has no bigrams yet. + // 2. The word has bigrams, and there is the target in the list. + // 3. The word has bigrams, and there is an invalid entry that can be reclaimed. + // 4. The word has bigrams. We have to append new bigram entry to the list. + // 5. Same as 4, but the list is the last entry of the content file. if (outAddedNewEntry) { *outAddedNewEntry = false; } const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); if (bigramListPos == NOT_A_DICT_POS) { - // Updating PtNode doesn't have a bigram list. + // Case 1. PtNode that doesn't have a bigram list. // Create new bigram list. if (!mBigramDictContent->createNewBigramList(terminalId)) { return false; @@ -63,7 +69,7 @@ bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTarget const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY, newTargetTerminalId); const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(&newBigramEntry, - newProbability, timestamp); + bigramProperty); // Write an entry. const int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId); if (!mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, writingPos)) { @@ -75,42 +81,55 @@ bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTarget return true; } - const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos); - if (entryPosToUpdate != NOT_A_DICT_POS) { - // Overwrite existing entry. - const BigramEntry originalBigramEntry = - mBigramDictContent->getBigramEntry(entryPosToUpdate); - if (!originalBigramEntry.isValid()) { - // Reuse invalid entry. - if (outAddedNewEntry) { - *outAddedNewEntry = true; + int tailEntryPos = NOT_A_DICT_POS; + const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos, + &tailEntryPos); + if (tailEntryPos != NOT_A_DICT_POS || entryPosToUpdate == NOT_A_DICT_POS) { + // Case 4, 5. + // Add new entry to the bigram list. + if (tailEntryPos == NOT_A_DICT_POS) { + // Case 4. Create new bigram list. + if (!mBigramDictContent->createNewBigramList(terminalId)) { + return false; + } + const int destPos = mBigramDictContent->getBigramListHeadPos(terminalId); + // Copy existing bigram list. + if (!mBigramDictContent->copyBigramList(bigramListPos, destPos, &tailEntryPos)) { + return false; } } - const BigramEntry updatedBigramEntry = - originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId); + // Write new entry at the tail position of the bigram content. + const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY, + newTargetTerminalId); const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( - &updatedBigramEntry, newProbability, timestamp); - return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate); + &newBigramEntry, bigramProperty); + if (!mBigramDictContent->writeBigramEntryAtTail(&bigramEntryToWrite)) { + return false; + } + // Update has next flag of the tail entry. + if (!updateHasNextFlag(true /* hasNext */, tailEntryPos)) { + return false; + } + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + return true; } - // Add new entry to the bigram list. - // Create new bigram list. - if (!mBigramDictContent->createNewBigramList(terminalId)) { - return false; + // Case 2. Overwrite the existing entry. Case 3. Reclaim and reuse the existing invalid entry. + const BigramEntry originalBigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate); + if (!originalBigramEntry.isValid()) { + // Case 3. Reuse the existing invalid entry. outAddedNewEntry is false when an existing + // entry is updated. + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } } - // Write new entry at a head position of the bigram list. - int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId); - const BigramEntry newBigramEntry(true /* hasNext */, NOT_A_PROBABILITY, newTargetTerminalId); + const BigramEntry updatedBigramEntry = + originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId); const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( - &newBigramEntry, newProbability, timestamp); - if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(&bigramEntryToWrite, &writingPos)) { - return false; - } - if (outAddedNewEntry) { - *outAddedNewEntry = true; - } - // Append existing entries by copying. - return mBigramDictContent->copyBigramList(bigramListPos, writingPos); + &updatedBigramEntry, bigramProperty); + return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate); } bool Ver4BigramListPolicy::removeEntry(const int terminalId, const int targetTerminalId) { @@ -119,7 +138,8 @@ bool Ver4BigramListPolicy::removeEntry(const int terminalId, const int targetTer // Bigram list doesn't exist. return false; } - const int entryPosToUpdate = getEntryPosToUpdate(targetTerminalId, bigramListPos); + const int entryPosToUpdate = getEntryPosToUpdate(targetTerminalId, bigramListPos, + nullptr /* outTailEntryPos */); if (entryPosToUpdate == NOT_A_DICT_POS) { // Bigram entry doesn't exist. return false; @@ -204,7 +224,10 @@ int Ver4BigramListPolicy::getBigramEntryConut(const int terminalId) { } int Ver4BigramListPolicy::getEntryPosToUpdate(const int targetTerminalIdToFind, - const int bigramListPos) const { + const int bigramListPos, int *const outTailEntryPos) const { + if (outTailEntryPos) { + *outTailEntryPos = NOT_A_DICT_POS; + } bool hasNext = true; int invalidEntryPos = NOT_A_DICT_POS; int readingPos = bigramListPos; @@ -220,23 +243,36 @@ int Ver4BigramListPolicy::getEntryPosToUpdate(const int targetTerminalIdToFind, // Invalid entry that can be reused is found. invalidEntryPos = entryPos; } + if (!hasNext && mBigramDictContent->isContentTailPos(readingPos)) { + if (outTailEntryPos) { + *outTailEntryPos = entryPos; + } + } } return invalidEntryPos; } const BigramEntry Ver4BigramListPolicy::createUpdatedBigramEntryFrom( - const BigramEntry *const originalBigramEntry, const int newProbability, - const int timestamp) const { + const BigramEntry *const originalBigramEntry, + const BigramProperty *const bigramProperty) const { // TODO: Consolidate historical info and probability. if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + const HistoricalInfo historicalInfoForUpdate(bigramProperty->getTimestamp(), + bigramProperty->getLevel(), bigramProperty->getCount()); const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo( - originalBigramEntry->getHistoricalInfo(), newProbability, timestamp, - mHeaderPolicy); + originalBigramEntry->getHistoricalInfo(), bigramProperty->getProbability(), + &historicalInfoForUpdate, mHeaderPolicy); return originalBigramEntry->updateHistoricalInfoAndGetEntry(&updatedHistoricalInfo); } else { - return originalBigramEntry->updateProbabilityAndGetEntry(newProbability); + return originalBigramEntry->updateProbabilityAndGetEntry(bigramProperty->getProbability()); } } +bool Ver4BigramListPolicy::updateHasNextFlag(const bool hasNext, const int bigramEntryPos) { + const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(bigramEntryPos); + const BigramEntry updatedBigramEntry = bigramEntry.updateHasNextAndGetEntry(hasNext); + return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, bigramEntryPos); +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h index 5b6c5a173..1613941c4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h @@ -24,6 +24,7 @@ namespace latinime { class BigramDictContent; +class BigramProperty; class HeaderPolicy; class TerminalPositionLookupTable; @@ -43,8 +44,8 @@ class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { // Do nothing because we don't need to skip bigram lists in ver4 dictionaries. } - bool addNewEntry(const int terminalId, const int newTargetTerminalId, const int newProbability, - const int timestamp, bool *const outAddedNewEntry); + bool addNewEntry(const int terminalId, const int newTargetTerminalId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); bool removeEntry(const int terminalId, const int targetTerminalId); @@ -56,10 +57,13 @@ class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { private: DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4BigramListPolicy); - int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos) const; + int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos, + int *const outTailEntryPos) const; const BigramEntry createUpdatedBigramEntryFrom(const BigramEntry *const originalBigramEntry, - const int newProbability, const int timestamp) const; + const BigramProperty *const bigramProperty) const; + + bool updateHasNextFlag(const bool hasNext, const int bigramEntryPos); BigramDictContent *const mBigramDictContent; const TerminalPositionLookupTable *const mTerminalPositionLookupTable; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp index 279f5b33a..e1ceaee49 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp @@ -23,9 +23,11 @@ namespace latinime { const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( int *const bigramEntryPos) const { const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer(); - if (*bigramEntryPos < 0 || *bigramEntryPos >= bigramListBuffer->getTailPosition()) { - AKLOGE("Invalid bigram entry position. bigramEntryPos: %d, bufSize: %d", - *bigramEntryPos, bigramListBuffer->getTailPosition()); + const int bigramEntryTailPos = (*bigramEntryPos) + getBigramEntrySize(); + if (*bigramEntryPos < 0 || bigramEntryTailPos > bigramListBuffer->getTailPosition()) { + AKLOGE("Invalid bigram entry position. bigramEntryPos: %d, bigramEntryTailPos: %d, " + "bufSize: %d", *bigramEntryPos, bigramEntryTailPos, + bigramListBuffer->getTailPosition()); ASSERT(false); return BigramEntry(false /* hasNext */, NOT_A_PROBABILITY, Ver4DictConstants::NOT_A_TERMINAL_ID); @@ -38,8 +40,6 @@ const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( int level = 0; int count = 0; if (mHasHistoricalInfo) { - probability = bigramListBuffer->readUintAndAdvancePosition( - Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos); timestamp = bigramListBuffer->readUintAndAdvancePosition( Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos); level = bigramListBuffer->readUintAndAdvancePosition( @@ -47,7 +47,8 @@ const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( count = bigramListBuffer->readUintAndAdvancePosition( Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos); } else { - probability = bigramFlags & Ver4DictConstants::BIGRAM_PROBABILITY_MASK; + probability = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos); } const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition( Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos); @@ -65,21 +66,13 @@ const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( bool BigramDictContent::writeBigramEntryAndAdvancePosition( const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) { BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer(); - const int bigramFlags = createAndGetBigramFlags( - mHasHistoricalInfo ? 0 : bigramEntryToWrite->getProbability(), - bigramEntryToWrite->hasNext()); + const int bigramFlags = createAndGetBigramFlags(bigramEntryToWrite->hasNext()); if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags, Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) { AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags); return false; } if (mHasHistoricalInfo) { - if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(), - Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) { - AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos, - bigramEntryToWrite->getProbability()); - return false; - } const HistoricalInfo *const historicalInfo = bigramEntryToWrite->getHistoricalInfo(); if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) { @@ -99,6 +92,13 @@ bool BigramDictContent::writeBigramEntryAndAdvancePosition( historicalInfo->getCount()); return false; } + } else { + if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(), + Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos, + bigramEntryToWrite->getProbability()); + return false; + } } const int targetTerminalIdToWrite = (bigramEntryToWrite->getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) ? @@ -113,13 +113,17 @@ bool BigramDictContent::writeBigramEntryAndAdvancePosition( return true; } -bool BigramDictContent::copyBigramList(const int bigramListPos, const int toPos) { +bool BigramDictContent::copyBigramList(const int bigramListPos, const int toPos, + int *const outTailEntryPos) { int readingPos = bigramListPos; int writingPos = toPos; bool hasNext = true; while (hasNext) { const BigramEntry bigramEntry = getBigramEntryAndAdvancePosition(&readingPos); hasNext = bigramEntry.hasNext(); + if (!hasNext) { + *outTailEntryPos = writingPos; + } if (!writeBigramEntryAndAdvancePosition(&bigramEntry, &writingPos)) { AKLOGE("Cannot write bigram entry to copy. pos: %d", writingPos); return false; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h index ba2a05209..52447a336 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h @@ -58,6 +58,11 @@ class BigramDictContent : public SparseTableDictContent { return addressLookupTable->get(terminalId); } + bool writeBigramEntryAtTail(const BigramEntry *const bigramEntryToWrite) { + int writingPos = getContentBuffer()->getTailPosition(); + return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos); + } + bool writeBigramEntry(const BigramEntry *const bigramEntryToWrite, const int entryWritingPos) { int writingPos = entryWritingPos; return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos); @@ -71,7 +76,7 @@ class BigramDictContent : public SparseTableDictContent { return getUpdatableAddressLookupTable()->set(terminalId, bigramListPos); } - bool copyBigramList(const int bigramListPos, const int toPos); + bool copyBigramList(const int bigramListPos, const int toPos, int *const outTailEntryPos); bool flushToFile(const char *const dictPath) const { return flush(dictPath, Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION, @@ -83,12 +88,29 @@ class BigramDictContent : public SparseTableDictContent { const BigramDictContent *const originalBigramDictContent, int *const outBigramEntryCount); + bool isContentTailPos(const int pos) const { + return pos == getContentBuffer()->getTailPosition(); + } + private: DISALLOW_COPY_AND_ASSIGN(BigramDictContent); - int createAndGetBigramFlags(const int probability, const bool hasNext) const { - return (probability & Ver4DictConstants::BIGRAM_PROBABILITY_MASK) - | (hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0); + int createAndGetBigramFlags(const bool hasNext) const { + return hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0; + } + + int getBigramEntrySize() const { + if (mHasHistoricalInfo) { + return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE + + Ver4DictConstants::TIME_STAMP_FIELD_SIZE + + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE + + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + } else { + return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE + + Ver4DictConstants::PROBABILITY_SIZE + + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + } } bool runGCBigramList(const int bigramListPos, diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h index fe984615c..790273541 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h @@ -19,7 +19,7 @@ #include "defines.h" #include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/shortcut/shortcut_list_reading_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp index 95f654498..5aa6b9a92 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp @@ -27,7 +27,8 @@ namespace latinime { /* static */ Ver4DictBuffers::Ver4DictBuffersPtr Ver4DictBuffers::openVer4DictBuffers( - const char *const dictPath, MmappedBuffer::MmappedBufferPtr headerBuffer) { + const char *const dictPath, MmappedBuffer::MmappedBufferPtr headerBuffer, + const FormatUtils::FORMAT_VERSION formatVersion) { if (!headerBuffer) { ASSERT(false); AKLOGE("The header buffer must be valid to open ver4 dict buffers."); @@ -35,7 +36,8 @@ namespace latinime { } // TODO: take only dictDirPath, and open both header and trie files in the constructor below const bool isUpdatable = headerBuffer->isUpdatable(); - return Ver4DictBuffersPtr(new Ver4DictBuffers(dictPath, std::move(headerBuffer), isUpdatable)); + return Ver4DictBuffersPtr(new Ver4DictBuffers(dictPath, std::move(headerBuffer), isUpdatable, + formatVersion)); } bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, @@ -54,6 +56,7 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, return false; } } + umask(S_IWGRP | S_IWOTH); if (mkdir(tmpDirPath, S_IRWXU) == -1) { AKLOGE("Cannot create directory: %s. errno: %d.", tmpDirPath, errno); return false; @@ -113,11 +116,12 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, } Ver4DictBuffers::Ver4DictBuffers(const char *const dictPath, - MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable) + MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable, + const FormatUtils::FORMAT_VERSION formatVersion) : mHeaderBuffer(std::move(headerBuffer)), mDictBuffer(MmappedBuffer::openBuffer(dictPath, Ver4DictConstants::TRIE_FILE_EXTENSION, isUpdatable)), - mHeaderPolicy(mHeaderBuffer->getBuffer(), FormatUtils::VERSION_4), + mHeaderPolicy(mHeaderBuffer->getBuffer(), formatVersion), mExpandableHeaderBuffer(mHeaderBuffer ? mHeaderBuffer->getBuffer() : nullptr, mHeaderPolicy.getSize(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h index fc41432f4..df177c14a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h @@ -36,7 +36,8 @@ class Ver4DictBuffers { typedef std::unique_ptr<Ver4DictBuffers> Ver4DictBuffersPtr; static Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath, - MmappedBuffer::MmappedBufferPtr headerBuffer); + MmappedBuffer::MmappedBufferPtr headerBuffer, + const FormatUtils::FORMAT_VERSION formatVersion); static AK_FORCE_INLINE Ver4DictBuffersPtr createVer4DictBuffers( const HeaderPolicy *const headerPolicy, const int maxTrieSize) { @@ -120,7 +121,8 @@ class Ver4DictBuffers { DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers); Ver4DictBuffers(const char *const dictDirPath, - const MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable); + const MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable, + const FormatUtils::FORMAT_VERSION formatVersion); Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const int maxTrieSize); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp index 67420a252..0a435e91c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp @@ -95,4 +95,4 @@ const PtNodeParams Ver4PatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProce } } -} +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index 38ff42fee..f89d3d7a0 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -16,13 +16,14 @@ #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h" -#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" +#include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" @@ -75,7 +76,7 @@ bool Ver4PatriciaTrieNodeWriter::markPtNodeAsMoved( PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); const PatriciaTrieReadingUtils::NodeFlags updatedFlags = DynamicPtReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */, - false /* isDeleted */, false /* willBecomeNonTerminal */); + false /* isDeleted */, false /* willBecomeNonTerminal */); int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); // Update flags. if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, @@ -133,9 +134,11 @@ bool Ver4PatriciaTrieNodeWriter::markPtNodeAsWillBecomeNonTerminal( &writingPos); } -bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbability( - const PtNodeParams *const toBeUpdatedPtNodeParams, const int newProbability, - const int timestamp) { +bool Ver4PatriciaTrieNodeWriter::updatePtNodeUnigramProperty( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const UnigramProperty *const unigramProperty) { + // Update probability and historical information. + // TODO: Update other information in the unigram property. if (!toBeUpdatedPtNodeParams->isTerminal()) { return false; } @@ -143,7 +146,7 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbability( mBuffers->getProbabilityDictContent()->getProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId()); const ProbabilityEntry probabilityEntry = createUpdatedEntryFrom(&originalProbabilityEntry, - newProbability, timestamp); + unigramProperty); return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry); } @@ -204,7 +207,8 @@ bool Ver4PatriciaTrieNodeWriter::writePtNodeAndAdvancePosition( bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( - const PtNodeParams *const ptNodeParams, const int timestamp, int *const ptNodeWritingPos) { + const PtNodeParams *const ptNodeParams, const UnigramProperty *const unigramProperty, + int *const ptNodeWritingPos) { int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; if (!writePtNodeAndGetTerminalIdAndAdvancePosition(ptNodeParams, &terminalId, ptNodeWritingPos)) { @@ -213,17 +217,16 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( // Write probability. ProbabilityEntry newProbabilityEntry; const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( - &newProbabilityEntry, ptNodeParams->getProbability(), timestamp); + &newProbabilityEntry, unigramProperty); return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry(terminalId, &probabilityEntryToWrite); } bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry( - const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, - bool *const outAddedNewBigram) { + const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam, + const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { if (!mBigramPolicy->addNewEntry(sourcePtNodeParams->getTerminalId(), - targetPtNodeParam->getTerminalId(), probability, timestamp, outAddedNewBigram)) { + targetPtNodeParam->getTerminalId(), bigramProperty, outAddedNewBigram)) { AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); return false; @@ -379,18 +382,21 @@ bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( } const ProbabilityEntry Ver4PatriciaTrieNodeWriter::createUpdatedEntryFrom( - const ProbabilityEntry *const originalProbabilityEntry, const int newProbability, - const int timestamp) const { + const ProbabilityEntry *const originalProbabilityEntry, + const UnigramProperty *const unigramProperty) const { // TODO: Consolidate historical info and probability. if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + const HistoricalInfo historicalInfoForUpdate(unigramProperty->getTimestamp(), + unigramProperty->getLevel(), unigramProperty->getCount()); const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo( - originalProbabilityEntry->getHistoricalInfo(), newProbability, timestamp, - mHeaderPolicy); + originalProbabilityEntry->getHistoricalInfo(), + unigramProperty->getProbability(), &historicalInfoForUpdate, mHeaderPolicy); return originalProbabilityEntry->createEntryWithUpdatedHistoricalInfo( &updatedHistoricalInfo); } else { - return originalProbabilityEntry->createEntryWithUpdatedProbability(newProbability); + return originalProbabilityEntry->createEntryWithUpdatedProbability( + unigramProperty->getProbability()); } } @@ -409,4 +415,4 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, return true; } -} +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h index b2b0504a1..e90bc44c0 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h @@ -57,8 +57,8 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { virtual bool markPtNodeAsWillBecomeNonTerminal( const PtNodeParams *const toBeUpdatedPtNodeParams); - virtual bool updatePtNodeProbability(const PtNodeParams *const toBeUpdatedPtNodeParams, - const int newProbability, const int timestamp); + virtual bool updatePtNodeUnigramProperty(const PtNodeParams *const toBeUpdatedPtNodeParams, + const UnigramProperty *const unigramProperty); virtual bool updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( const PtNodeParams *const toBeUpdatedPtNodeParams, bool *const outNeedsToKeepPtNode); @@ -73,10 +73,10 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { int *const ptNodeWritingPos); virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, - const int timestamp, int *const ptNodeWritingPos); + const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos); virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, + const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, bool *const outAddedNewBigram); virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, @@ -102,11 +102,12 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { const PtNodeParams *const ptNodeParams, int *const outTerminalId, int *const ptNodeWritingPos); - // Create updated probability entry using given probability and timestamp. In addition to the + // Create updated probability entry using given unigram property. In addition to the // probability, this method updates historical information if needed. + // TODO: Update flags belonging to the unigram property. const ProbabilityEntry createUpdatedEntryFrom( - const ProbabilityEntry *const originalProbabilityEntry, const int newProbability, - const int timestamp) const; + const ProbabilityEntry *const originalProbabilityEntry, + const UnigramProperty *const unigramProperty) const; bool updatePtNodeFlags(const int ptNodePos, const bool isBlacklisted, const bool isNotAWord, const bool isTerminal, const bool hasShortcutTargets, const bool hasBigrams, 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 bbfd22e59..aec3b8ea3 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 @@ -23,6 +23,7 @@ #include "suggest/core/dictionary/property/bigram_property.h" #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" +#include "suggest/core/session/prev_words_info.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" @@ -59,13 +60,17 @@ void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const d // valid terminal DicNode. isTerminal = ptNodeParams.getProbability() != NOT_A_PROBABILITY; } + readingHelper.readNextSiblingNode(ptNodeParams); + if (ptNodeParams.representsNonWordInfo()) { + // Skip PtNodes that represent non-word information. + continue; + } childDicNodes->pushLeavingChild(dicNode, ptNodeParams.getHeadPos(), ptNodeParams.getChildrenPos(), ptNodeParams.getProbability(), isTerminal, ptNodeParams.hasChildren(), ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord() /* isBlacklistedOrNotAWord */, ptNodeParams.getCodePointCount(), ptNodeParams.getCodePoints()); - readingHelper.readNextSiblingNode(ptNodeParams); } if (readingHelper.isError()) { mIsCorrupted = true; @@ -111,9 +116,7 @@ int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability, } else if (bigramProbability == NOT_A_PROBABILITY) { return ProbabilityUtils::backoff(unigramProbability); } else { - // bigramProbability is a bigram probability delta. - return ProbabilityUtils::computeProbabilityForBigram(unigramProbability, - bigramProbability); + return bigramProbability; } } } @@ -153,10 +156,10 @@ int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) cons ptNodeParams.getTerminalId()); } -bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int length, +bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty) { if (!mBuffers->isUpdatable()) { - AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); + AKLOGI("Warning: addUnigramEntry() is called for non-updatable dictionary."); return false; } if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { @@ -178,11 +181,19 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len DynamicPtReadingHelper readingHelper(&mNodeReader, &mPtNodeArrayReader); readingHelper.initWithPtNodeArrayPos(getRootPosition()); bool addedNewUnigram = false; - if (mUpdatingHelper.addUnigramWord(&readingHelper, word, length, - unigramProperty->getProbability(), unigramProperty->isNotAWord(), - unigramProperty->isBlacklisted(), unigramProperty->getTimestamp(), - &addedNewUnigram)) { - if (addedNewUnigram) { + int codePointsToAdd[MAX_WORD_LENGTH]; + int codePointCountToAdd = length; + memmove(codePointsToAdd, word, sizeof(int) * length); + if (unigramProperty->representsBeginningOfSentence()) { + codePointCountToAdd = CharUtils::attachBeginningOfSentenceMarker(codePointsToAdd, + codePointCountToAdd, MAX_WORD_LENGTH); + } + if (codePointCountToAdd <= 0) { + return false; + } + if (mUpdatingHelper.addUnigramWord(&readingHelper, codePointsToAdd, codePointCountToAdd, + unigramProperty, &addedNewUnigram)) { + if (addedNewUnigram && !unigramProperty->representsBeginningOfSentence()) { mUnigramCount++; } if (unigramProperty->getShortcuts().size() > 0) { @@ -210,11 +221,15 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len } } -bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1, const int probability, - const int timestamp) { +bool Ver4PatriciaTriePolicy::removeUnigramEntry(const int *const word, const int length) { + // TODO: Implement. + return false; +} + +bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty) { if (!mBuffers->isUpdatable()) { - AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); + AKLOGI("Warning: addNgramEntry() is called for non-updatable dictionary."); return false; } if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { @@ -222,23 +237,47 @@ bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int le mDictBuffer->getTailPosition()); return false; } - if (length0 > MAX_WORD_LENGTH || length1 > MAX_WORD_LENGTH) { - AKLOGE("Either src word or target word is too long to insert the bigram to the dictionary. " - "length0: %d, length1: %d", length0, length1); + if (!prevWordsInfo->isValid()) { + AKLOGE("prev words info is not valid for adding n-gram entry to the dictionary."); return false; } - const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0, - false /* forceLowerCaseSearch */); - if (word0Pos == NOT_A_DICT_POS) { + if (bigramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { + AKLOGE("The word is too long to insert the ngram to the dictionary. " + "length: %d", bigramProperty->getTargetCodePoints()->size()); return false; } - const int word1Pos = getTerminalPtNodePositionOfWord(word1, length1, - false /* forceLowerCaseSearch */); + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSearch */); + // TODO: Support N-gram. + if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { + if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { + const std::vector<UnigramProperty::ShortcutProperty> shortcuts; + const UnigramProperty beginningOfSentenceUnigramProperty( + true /* representsBeginningOfSentence */, true /* isNotAWord */, + false /* isBlacklisted */, MAX_PROBABILITY /* probability */, + NOT_A_TIMESTAMP /* timestamp */, 0 /* level */, 0 /* count */, &shortcuts); + if (!addUnigramEntry(prevWordsInfo->getNthPrevWordCodePoints(1 /* n */), + prevWordsInfo->getNthPrevWordCodePointCount(1 /* n */), + &beginningOfSentenceUnigramProperty)) { + AKLOGE("Cannot add unigram entry for the beginning-of-sentence."); + return false; + } + // Refresh Terminal PtNode positions. + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSearch */); + } else { + return false; + } + } + const int word1Pos = getTerminalPtNodePositionOfWord( + bigramProperty->getTargetCodePoints()->data(), + bigramProperty->getTargetCodePoints()->size(), false /* forceLowerCaseSearch */); if (word1Pos == NOT_A_DICT_POS) { return false; } bool addedNewBigram = false; - if (mUpdatingHelper.addBigramWords(word0Pos, word1Pos, probability, timestamp, + if (mUpdatingHelper.addBigramWords(prevWordsPtNodePos[0], word1Pos, bigramProperty, &addedNewBigram)) { if (addedNewBigram) { mBigramCount++; @@ -249,10 +288,10 @@ bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int le } } -bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1) { +bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const int *const word, const int length) { if (!mBuffers->isUpdatable()) { - AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); + AKLOGI("Warning: removeNgramEntry() is called for non-updatable dictionary."); return false; } if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { @@ -260,22 +299,26 @@ bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int mDictBuffer->getTailPosition()); return false; } - if (length0 > MAX_WORD_LENGTH || length1 > MAX_WORD_LENGTH) { - AKLOGE("Either src word or target word is too long to remove the bigram to from the " - "dictionary. length0: %d, length1: %d", length0, length1); + if (!prevWordsInfo->isValid()) { + AKLOGE("prev words info is not valid for removing n-gram entry form the dictionary."); return false; } - const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0, - false /* forceLowerCaseSearch */); - if (word0Pos == NOT_A_DICT_POS) { + if (length > MAX_WORD_LENGTH) { + AKLOGE("word is too long to remove n-gram entry form the dictionary. length: %d", length); + } + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, + false /* tryLowerCaseSerch */); + // TODO: Support N-gram. + if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { return false; } - const int word1Pos = getTerminalPtNodePositionOfWord(word1, length1, + const int wordPos = getTerminalPtNodePositionOfWord(word, length, false /* forceLowerCaseSearch */); - if (word1Pos == NOT_A_DICT_POS) { + if (wordPos == NOT_A_DICT_POS) { return false; } - if (mUpdatingHelper.removeBigramWords(word0Pos, word1Pos)) { + if (mUpdatingHelper.removeBigramWords(prevWordsPtNodePos[0], wordPos)) { mBigramCount--; return true; } else { @@ -283,26 +326,30 @@ bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int } } -void Ver4PatriciaTriePolicy::flush(const char *const filePath) { +bool Ver4PatriciaTriePolicy::flush(const char *const filePath) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); - return; + return false; } if (!mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount)) { AKLOGE("Cannot flush the dictionary to file."); mIsCorrupted = true; + return false; } + return true; } -void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { +bool Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { if (!mBuffers->isUpdatable()) { AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); - return; + return false; } if (!mWritingHelper.writeToDictFileWithGC(getRootPosition(), filePath)) { AKLOGE("Cannot flush the dictionary to file with GC."); mIsCorrupted = true; + return false; } + return true; } bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { @@ -396,7 +443,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code const int probability = bigramEntry.hasHistoricalInfo() ? ForgettingCurveUtils::decodeProbability( bigramEntry.getHistoricalInfo(), mHeaderPolicy) : - getProbability(word1Probability, bigramEntry.getProbability()); + bigramEntry.getProbability(); bigrams.emplace_back(&word1, probability, historicalInfo->getTimeStamp(), historicalInfo->getLevel(), historicalInfo->getCount()); @@ -419,14 +466,17 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code shortcuts.emplace_back(&target, shortcutProbability); } } - const UnigramProperty unigramProperty(ptNodeParams.isNotAWord(), - ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), + const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(), + ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.getProbability(), historicalInfo->getTimeStamp(), historicalInfo->getLevel(), historicalInfo->getCount(), &shortcuts); return WordProperty(&codePointVector, &unigramProperty, &bigrams); } int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints) { + // TODO: Return code point count like other methods. + // Null termination. + outCodePoints[0] = 0; if (token == 0) { mTerminalPtNodePositionsForIteratingWords.clear(); DynamicPtReadingHelper::TraversePolicyToGetAllTerminalPtNodePositions traversePolicy( @@ -443,8 +493,13 @@ int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const } const int terminalPtNodePos = mTerminalPtNodePositionsForIteratingWords[token]; int unigramProbability = NOT_A_PROBABILITY; - getCodePointsAndProbabilityAndReturnCodePointCount(terminalPtNodePos, MAX_WORD_LENGTH, - outCodePoints, &unigramProbability); + const int codePointCount = getCodePointsAndProbabilityAndReturnCodePointCount( + terminalPtNodePos, MAX_WORD_LENGTH, outCodePoints, &unigramProbability); + if (codePointCount < MAX_WORD_LENGTH) { + // Null termination. outCodePoints have to be null terminated or contain MAX_WORD_LENGTH + // code points. + outCodePoints[codePointCount] = 0; + } const int nextToken = token + 1; if (nextToken >= terminalPtNodePositionsVectorSize) { // All words have been iterated. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h index 8f981def5..0a20965f3 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -21,10 +21,10 @@ #include "defines.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h" +#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h" @@ -90,18 +90,20 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return &mShortcutPolicy; } - bool addUnigramWord(const int *const word, const int length, + bool addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty); - bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability, const int timestamp); + bool removeUnigramEntry(const int *const word, const int length); - bool removeBigramWords(const int *const word0, const int length0, const int *const word1, + bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, + const BigramProperty *const bigramProperty); + + bool removeNgramEntry(const PrevWordsInfo *const prevWordsInfo, const int *const word1, const int length1); - void flush(const char *const filePath); + bool flush(const char *const filePath); - void flushWithGC(const char *const filePath); + bool flushWithGC(const char *const filePath); bool needsToRunGC(const bool mindsBlockByGC) const; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp index 12298d967..e868ddf6f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -19,9 +19,9 @@ #include <cstring> #include <queue> -#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" @@ -213,13 +213,16 @@ bool Ver4PatriciaTrieWritingHelper::truncateUnigrams( // Delete unigrams. while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) { const int ptNodePos = priorityQueue.top().getDictPos(); + priorityQueue.pop(); const PtNodeParams ptNodeParams = ptNodeReader->fetchNodeInfoInBufferFromPtNodePos(ptNodePos); + if (ptNodeParams.representsNonWordInfo()) { + continue; + } if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) { AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos); return false; } - priorityQueue.pop(); } return true; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h index 23cbe3aa3..a2e88a46c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h @@ -37,13 +37,13 @@ class BufferWithExtendableBuffer { BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize, const int maxAdditionalBufferSize) : mOriginalBuffer(originalBuffer), mOriginalBufferSize(originalBufferSize), - mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0), + mAdditionalBuffer(0), mUsedAdditionalBufferSize(0), mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} // Without original buffer. BufferWithExtendableBuffer(const int maxAdditionalBufferSize) : mOriginalBuffer(0), mOriginalBufferSize(0), - mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0), + mAdditionalBuffer(0), mUsedAdditionalBufferSize(0), mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} AK_FORCE_INLINE int getTailPosition() const { 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 87fa5994c..4da339b0a 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 @@ -17,8 +17,13 @@ #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include <cstdio> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> #include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" @@ -34,9 +39,18 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = const int dictVersion, const std::vector<int> localeAsCodePointVector, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { TimeKeeper::setCurrentTime(); - switch (dictVersion) { + const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::getFormatVersion(dictVersion); + switch (formatVersion) { case FormatUtils::VERSION_4: - return createEmptyV4DictFile(filePath, localeAsCodePointVector, attributeMap); + 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: + return createEmptyV4DictFile<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr>( + filePath, localeAsCodePointVector, attributeMap, formatVersion); default: AKLOGE("Cannot create dictionary %s because format version %d is not supported.", filePath, dictVersion); @@ -44,13 +58,14 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = } } +template<class DictConstants, class DictBuffers, class DictBuffersPtr> /* static */ bool DictFileWritingUtils::createEmptyV4DictFile(const char *const dirPath, const std::vector<int> localeAsCodePointVector, - const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { - HeaderPolicy headerPolicy(FormatUtils::VERSION_4, localeAsCodePointVector, attributeMap); - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( - Ver4DictBuffers::createVer4DictBuffers(&headerPolicy, - Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE)); + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, + const FormatUtils::FORMAT_VERSION formatVersion) { + HeaderPolicy headerPolicy(formatVersion, localeAsCodePointVector, attributeMap); + DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy, + DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, 0 /* unigramCount */, 0 /* bigramCount */, 0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer()); @@ -96,9 +111,15 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = /* static */ bool DictFileWritingUtils::flushBufferToFile(const char *const filePath, const BufferWithExtendableBuffer *const buffer) { - FILE *const file = fopen(filePath, "wb"); + const int fd = open(filePath, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd == -1) { + AKLOGE("File %s cannot be opened. errno: %d", filePath, errno); + ASSERT(false); + return false; + } + FILE *const file = fdopen(fd, "wb"); if (!file) { - AKLOGE("File %s cannot be opened.", filePath); + AKLOGE("fdopen failed for the file %s. errno: %d", filePath, errno); ASSERT(false); return false; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h index 54ec651f7..5df5856d2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h @@ -21,6 +21,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" namespace latinime { @@ -44,9 +45,16 @@ class DictFileWritingUtils { private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictFileWritingUtils); + static bool createEmptyV401DictFile(const char *const filePath, + const std::vector<int> localeAsCodePointVector, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, + const FormatUtils::FORMAT_VERSION formatVersion); + + template<class DictConstants, class DictBuffers, class DictBuffersPtr> static bool createEmptyV4DictFile(const char *const filePath, const std::vector<int> localeAsCodePointVector, - const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap); + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, + const FormatUtils::FORMAT_VERSION formatVersion); static bool flushBufferToFile(const char *const filePath, const BufferWithExtendableBuffer *const buffer); diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp index c7d3df984..fed0ae77e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp @@ -30,7 +30,7 @@ const int ForgettingCurveUtils::MULTIPLIER_TWO_IN_PROBABILITY_SCALE = 8; const int ForgettingCurveUtils::DECAY_INTERVAL_SECONDS = 2 * 60 * 60; const int ForgettingCurveUtils::MAX_LEVEL = 3; -const int ForgettingCurveUtils::MIN_VALID_LEVEL = 1; +const int ForgettingCurveUtils::MIN_VISIBLE_LEVEL = 1; const int ForgettingCurveUtils::MAX_ELAPSED_TIME_STEP_COUNT = 15; const int ForgettingCurveUtils::DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD = 14; @@ -41,25 +41,34 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT // TODO: Revise the logic to decide the initial probability depending on the given probability. /* static */ const HistoricalInfo ForgettingCurveUtils::createUpdatedHistoricalInfo( - const HistoricalInfo *const originalHistoricalInfo, - const int newProbability, const int timestamp, const HeaderPolicy *const headerPolicy) { + const HistoricalInfo *const originalHistoricalInfo, const int newProbability, + const HistoricalInfo *const newHistoricalInfo, const HeaderPolicy *const headerPolicy) { + const int timestamp = newHistoricalInfo->getTimeStamp(); if (newProbability != NOT_A_PROBABILITY && originalHistoricalInfo->getLevel() == 0) { - return HistoricalInfo(timestamp, MIN_VALID_LEVEL /* level */, 0 /* count */); - } else if (!originalHistoricalInfo->isValid()) { + // Add entry as a valid word. + const int level = clampToVisibleEntryLevelRange(newHistoricalInfo->getLevel()); + const int count = clampToValidCountRange(newHistoricalInfo->getCount(), headerPolicy); + return HistoricalInfo(timestamp, level, count); + } else if (!originalHistoricalInfo->isValid() + || originalHistoricalInfo->getLevel() < newHistoricalInfo->getLevel() + || (originalHistoricalInfo->getLevel() == newHistoricalInfo->getLevel() + && originalHistoricalInfo->getCount() < newHistoricalInfo->getCount())) { // Initial information. - return HistoricalInfo(timestamp, 0 /* level */, 1 /* count */); + const int level = clampToValidLevelRange(newHistoricalInfo->getLevel()); + const int count = clampToValidCountRange(newHistoricalInfo->getCount(), headerPolicy); + return HistoricalInfo(timestamp, level, count); } else { const int updatedCount = originalHistoricalInfo->getCount() + 1; if (updatedCount >= headerPolicy->getForgettingCurveOccurrencesToLevelUp()) { // The count exceeds the max value the level can be incremented. if (originalHistoricalInfo->getLevel() >= MAX_LEVEL) { // The level is already max. - return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel(), - originalHistoricalInfo->getCount()); + return HistoricalInfo(timestamp, + originalHistoricalInfo->getLevel(), originalHistoricalInfo->getCount()); } else { // Level up. - return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel() + 1, - 0 /* count */); + return HistoricalInfo(timestamp, + originalHistoricalInfo->getLevel() + 1, 0 /* count */); } } else { return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel(), updatedCount); @@ -73,8 +82,8 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT headerPolicy->getForgettingCurveDurationToLevelDown()); return sProbabilityTable.getProbability( headerPolicy->getForgettingCurveProbabilityValuesTableId(), - std::min(std::max(historicalInfo->getLevel(), 0), MAX_LEVEL), - std::min(std::max(elapsedTimeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT)); + clampToValidLevelRange(historicalInfo->getLevel()), + clampToValidTimeStepCountRange(elapsedTimeStepCount)); } /* static */ int ForgettingCurveUtils::getProbability(const int unigramProbability, @@ -155,6 +164,23 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT return elapsedTimeInSeconds / timeStepDurationInSeconds; } +/* static */ int ForgettingCurveUtils::clampToVisibleEntryLevelRange(const int level) { + return std::min(std::max(level, MIN_VISIBLE_LEVEL), MAX_LEVEL); +} + +/* static */ int ForgettingCurveUtils::clampToValidCountRange(const int count, + const HeaderPolicy *const headerPolicy) { + return std::min(std::max(count, 0), headerPolicy->getForgettingCurveOccurrencesToLevelUp() - 1); +} + +/* static */ int ForgettingCurveUtils::clampToValidLevelRange(const int level) { + return std::min(std::max(level, 0), MAX_LEVEL); +} + +/* static */ int ForgettingCurveUtils::clampToValidTimeStepCountRange(const int timeStepCount) { + return std::min(std::max(timeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT); +} + const int ForgettingCurveUtils::ProbabilityTable::PROBABILITY_TABLE_COUNT = 4; const int ForgettingCurveUtils::ProbabilityTable::WEAK_PROBABILITY_TABLE_ID = 0; const int ForgettingCurveUtils::ProbabilityTable::MODEST_PROBABILITY_TABLE_ID = 1; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h index bb8690939..3ff80aeec 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h @@ -30,7 +30,7 @@ class ForgettingCurveUtils { public: static const HistoricalInfo createUpdatedHistoricalInfo( const HistoricalInfo *const originalHistoricalInfo, const int newProbability, - const int timestamp, const HeaderPolicy *const headerPolicy); + const HistoricalInfo *const newHistoricalInfo, const HeaderPolicy *const headerPolicy); static const HistoricalInfo createHistoricalInfoToSave( const HistoricalInfo *const originalHistoricalInfo, @@ -93,7 +93,7 @@ class ForgettingCurveUtils { static const int DECAY_INTERVAL_SECONDS; static const int MAX_LEVEL; - static const int MIN_VALID_LEVEL; + static const int MIN_VISIBLE_LEVEL; static const int MAX_ELAPSED_TIME_STEP_COUNT; static const int DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD; @@ -103,8 +103,11 @@ class ForgettingCurveUtils { static const ProbabilityTable sProbabilityTable; static int backoff(const int unigramProbability); - static int getElapsedTimeStepCount(const int timestamp, const int durationToLevelDown); + static int clampToVisibleEntryLevelRange(const int level); + static int clampToValidLevelRange(const int level); + static int clampToValidCountRange(const int count, const HeaderPolicy *const headerPolicy); + static int clampToValidTimeStepCountRange(const int timeStepCount); }; } // namespace latinime #endif /* LATINIME_FORGETTING_CURVE_UTILS_H */ 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 cd3c403fa..1916ea560 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -25,6 +25,20 @@ const uint32_t FormatUtils::MAGIC_NUMBER = 0x9BC13AFE; // Magic number (4 bytes), version (2 bytes), flags (2 bytes), header size (4 bytes) = 12 const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; +/* static */ FormatUtils::FORMAT_VERSION FormatUtils::getFormatVersion(const int formatVersion) { + switch (formatVersion) { + case VERSION_2: + return VERSION_2; + 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; + default: + return UNKNOWN_VERSION; + } +} /* static */ FormatUtils::FORMAT_VERSION FormatUtils::detectFormatVersion( const uint8_t *const dict, const int dictSize) { // The magic number is stored big-endian. @@ -36,7 +50,7 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0); switch (magicNumber) { case MAGIC_NUMBER: - // Version 2 header is as follows: + // The layout of the header is as follows: // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE // Dictionary format version number (2 bytes) // Options (2 bytes) @@ -44,13 +58,7 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; // Conceptually this converts the hardcoded value of the bytes in the file into // the symbolic value we use in the code. But we want the constants to be the // same so we use them for both here. - if (ByteArrayUtils::readUint16(dict, 4) == VERSION_2) { - return VERSION_2; - } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4) { - return VERSION_4; - } else { - return UNKNOWN_VERSION; - } + return getFormatVersion(ByteArrayUtils::readUint16(dict, 4)); 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 759b1c9b2..55ad5799f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -31,7 +31,9 @@ class FormatUtils { enum FORMAT_VERSION { // These MUST have the same values as the relevant constants in FormatSpec.java. VERSION_2 = 2, - VERSION_4 = 401, + VERSION_4_ONLY_FOR_TESTING = 399, + VERSION_4 = 402, + VERSION_4_DEV = 403, UNKNOWN_VERSION = -1 }; @@ -39,6 +41,7 @@ class FormatUtils { // unsupported or obsolete dictionary formats. static const uint32_t MAGIC_NUMBER; + static FORMAT_VERSION getFormatVersion(const int formatVersion); static FORMAT_VERSION detectFormatVersion(const uint8_t *const dict, const int dictSize); private: diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp index d3e0c237f..4a126ff85 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp @@ -33,7 +33,7 @@ namespace latinime { const int mmapFd = open(path, O_RDONLY); if (mmapFd < 0) { AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno); - return MmappedBufferPtr(nullptr); + return nullptr; } const int pagesize = sysconf(_SC_PAGESIZE); const int offset = bufferOffset % pagesize; @@ -45,13 +45,13 @@ namespace latinime { if (mmappedBuffer == MAP_FAILED) { AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); close(mmapFd); - return MmappedBufferPtr(nullptr); + return nullptr; } uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset; if (!buffer) { AKLOGE("DICT: buffer is null"); close(mmapFd); - return MmappedBufferPtr(nullptr); + return nullptr; } return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, mmapFd, isUpdatable)); @@ -61,7 +61,7 @@ namespace latinime { const char *const path, const bool isUpdatable) { const int fileSize = FileUtils::getFileSize(path); if (fileSize == -1) { - return MmappedBufferPtr(nullptr); + return nullptr; } else if (fileSize == 0) { return MmappedBufferPtr(new MmappedBuffer(isUpdatable)); } else { @@ -76,7 +76,7 @@ namespace latinime { const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath, fileName); if (filePathLength >= filePathBufferSize) { - return MmappedBufferPtr(nullptr); + return nullptr; } return openBuffer(filePath, isUpdatable); } diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp index fa9600c74..3fc566e7a 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp @@ -37,6 +37,7 @@ const float ScoringParams::DISTANCE_WEIGHT_LENGTH = 0.1524f; const float ScoringParams::PROXIMITY_COST = 0.0694f; const float ScoringParams::FIRST_CHAR_PROXIMITY_COST = 0.072f; const float ScoringParams::FIRST_PROXIMITY_COST = 0.07788f; +const float ScoringParams::INTENTIONAL_OMISSION_COST = 0.1f; const float ScoringParams::OMISSION_COST = 0.467f; const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.345f; const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.5256f; diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h index b66962019..b12de6d87 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h @@ -44,6 +44,7 @@ class ScoringParams { static const float PROXIMITY_COST; static const float FIRST_CHAR_PROXIMITY_COST; static const float FIRST_PROXIMITY_COST; + static const float INTENTIONAL_OMISSION_COST; static const float OMISSION_COST; static const float OMISSION_COST_SAME_CHAR; static const float OMISSION_COST_FIRST_CHAR; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 0ba439b47..84077174d 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -54,12 +54,15 @@ class TypingWeighting : public Weighting { float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const { const bool isZeroCostOmission = parentDicNode->isZeroCostOmission(); + const bool isIntentionalOmission = parentDicNode->canBeIntentionalOmission(); const bool sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode); // If the traversal omitted the first letter then the dicNode should now be on the second. const bool isFirstLetterOmission = dicNode->getNodeCodePointCount() == 2; float cost = 0.0f; if (isZeroCostOmission) { cost = 0.0f; + } else if (isIntentionalOmission) { + cost = ScoringParams::INTENTIONAL_OMISSION_COST; } else if (isFirstLetterOmission) { cost = ScoringParams::OMISSION_COST_FIRST_CHAR; } else { diff --git a/native/jni/src/utils/char_utils.cpp b/native/jni/src/utils/char_utils.cpp index adc474b4c..b17e0847d 100644 --- a/native/jni/src/utils/char_utils.cpp +++ b/native/jni/src/utils/char_utils.cpp @@ -22,6 +22,9 @@ namespace latinime { +const int CharUtils::MIN_UNICODE_CODE_POINT = 0; +const int CharUtils::MAX_UNICODE_CODE_POINT = 0x10FFFF; + struct LatinCapitalSmallPair { unsigned short capital; unsigned short small; diff --git a/native/jni/src/utils/char_utils.h b/native/jni/src/utils/char_utils.h index 239419d5b..f28ed5682 100644 --- a/native/jni/src/utils/char_utils.h +++ b/native/jni/src/utils/char_utils.h @@ -18,6 +18,7 @@ #define LATINIME_CHAR_UTILS_H #include <cctype> +#include <cstring> #include <vector> #include "defines.h" @@ -86,12 +87,32 @@ class CharUtils { return spaceCount; } + static AK_FORCE_INLINE int isInUnicodeSpace(const int codePoint) { + return codePoint >= MIN_UNICODE_CODE_POINT && codePoint <= MAX_UNICODE_CODE_POINT; + } + static unsigned short latin_tolower(const unsigned short c); static const std::vector<int> EMPTY_STRING; + // Returns updated code point count. Returns 0 when the code points cannot be marked as a + // Beginning-of-Sentence. + static AK_FORCE_INLINE int attachBeginningOfSentenceMarker(int *const codePoints, + const int codePointCount, const int maxCodePoint) { + if (codePointCount >= maxCodePoint) { + // the code points cannot be marked as a Beginning-of-Sentence. + return 0; + } + memmove(codePoints + 1, codePoints, sizeof(int) * codePointCount); + codePoints[0] = CODE_POINT_BEGINNING_OF_SENTENCE; + return codePointCount + 1; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(CharUtils); + static const int MIN_UNICODE_CODE_POINT; + static const int MAX_UNICODE_CODE_POINT; + /** * Table mapping most combined Latin, Greek, and Cyrillic characters * to their base characters. If c is in range, BASE_CHARS[c] == c diff --git a/java/src/com/android/inputmethod/research/FeedbackLog.java b/native/jni/src/utils/jni_data_utils.cpp index 5af194c32..5555293d5 100644 --- a/java/src/com/android/inputmethod/research/FeedbackLog.java +++ b/native/jni/src/utils/jni_data_utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 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,19 +14,11 @@ * limitations under the License. */ -package com.android.inputmethod.research; +#include "utils/jni_data_utils.h" -import android.content.Context; +namespace latinime { -import java.io.File; +const int JniDataUtils::CODE_POINT_REPLACEMENT_CHARACTER = 0xFFFD; +const int JniDataUtils::CODE_POINT_NULL = 0; -public class FeedbackLog extends ResearchLog { - public FeedbackLog(final File outputFile, final Context context) { - super(outputFile, context); - } - - @Override - public boolean isFeedbackLog() { - return true; - } -} +} // namespace latinime diff --git a/native/jni/src/utils/jni_data_utils.h b/native/jni/src/utils/jni_data_utils.h index 2ce02dc05..67a66fdfe 100644 --- a/native/jni/src/utils/jni_data_utils.h +++ b/native/jni/src/utils/jni_data_utils.h @@ -23,6 +23,7 @@ #include "jni.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" +#include "utils/char_utils.h" namespace latinime { @@ -65,8 +66,44 @@ class JniDataUtils { return attributeMap; } + static void outputCodePoints(JNIEnv *env, jintArray intArrayToOutputCodePoints, const int start, + const int maxLength, const int *const codePoints, const int codePointCount, + const bool needsNullTermination) { + const int outputCodePointCount = std::min(maxLength, codePointCount); + int outputCodePonts[outputCodePointCount]; + for (int i = 0; i < outputCodePointCount; ++i) { + const int codePoint = codePoints[i]; + if (!CharUtils::isInUnicodeSpace(codePoint)) { + outputCodePonts[i] = CODE_POINT_REPLACEMENT_CHARACTER; + } else if (codePoint >= 0x01 && codePoint <= 0x1F) { + // Control code. + outputCodePonts[i] = CODE_POINT_REPLACEMENT_CHARACTER; + } else { + outputCodePonts[i] = codePoint; + } + } + env->SetIntArrayRegion(intArrayToOutputCodePoints, start, outputCodePointCount, + outputCodePonts); + if (needsNullTermination && outputCodePointCount < maxLength) { + env->SetIntArrayRegion(intArrayToOutputCodePoints, start + outputCodePointCount, + 1 /* len */, &CODE_POINT_NULL); + } + } + + static void putIntToArray(JNIEnv *env, jintArray array, const int index, const int value) { + env->SetIntArrayRegion(array, index, 1 /* len */, &value); + } + + static void putFloatToArray(JNIEnv *env, jfloatArray array, const int index, + const float value) { + env->SetFloatArrayRegion(array, index, 1 /* len */, &value); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(JniDataUtils); + + static const int CODE_POINT_REPLACEMENT_CHARACTER; + static const int CODE_POINT_NULL; }; } // namespace latinime #endif // LATINIME_JNI_DATA_UTILS_H diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java index 06139b808..96f925554 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java @@ -29,9 +29,14 @@ import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @MediumTest -public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTestsBase { +public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetTestsBase { + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_KLP; + } + private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet, - final int elementId, final String label, final int iconId) { + final int elementId, final CharSequence label, final int iconId) { final Keyboard keyboard = layoutSet.getKeyboard(elementId); final Key enterKey = keyboard.getKey(Constants.CODE_ENTER); assertNotNull(tag + " enter key on " + keyboard.mId, enterKey); @@ -39,7 +44,7 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId()); } - private void doTestActionLabel(final String tag, final InputMethodSubtype subtype, + protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype, final int actionId, final int labelResId) { final EditorInfo editorInfo = new EditorInfo(); editorInfo.imeOptions = actionId; @@ -57,6 +62,11 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe } else { label = job.runInLocale(res, SubtypeLocaleUtils.getSubtypeLocale(subtype)); } + doTestActionLabel(tag, subtype, editorInfo, label); + } + + protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype, + final EditorInfo editorInfo, final CharSequence label) { // Test text layouts. editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); @@ -82,7 +92,7 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe label, KeyboardIconsSet.ICON_UNDEFINED); } - private void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype, + protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype, final int actionId, final String iconName) { final int iconId = KeyboardIconsSet.getIconId(iconName); final EditorInfo editorInfo = new EditorInfo(); @@ -111,14 +121,16 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe for (final InputMethodSubtype subtype : getAllSubtypesList()) { final String tag = "unspecifiled " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); - doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED, "enter_key"); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED, + KeyboardIconsSet.NAME_ENTER_KEY); } } public void testActionNone() { for (final InputMethodSubtype subtype : getAllSubtypesList()) { final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); - doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, "enter_key"); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, + KeyboardIconsSet.NAME_ENTER_KEY); } } @@ -132,7 +144,8 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe public void testActionSearch() { for (final InputMethodSubtype subtype : getAllSubtypesList()) { final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); - doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH, "search_key"); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH, + KeyboardIconsSet.NAME_SEARCH_KEY); } } @@ -164,4 +177,15 @@ public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTe tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, R.string.label_previous_key); } } + + public void testActionCustom() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + final CharSequence customLabel = "customLabel"; + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED; + editorInfo.actionLabel = customLabel; + doTestActionLabel(tag, subtype, editorInfo, customLabel); + } + } } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java new file mode 100644 index 000000000..7747ac5f9 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java @@ -0,0 +1,97 @@ +/* + * 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; + +import android.test.suitebuilder.annotation.MediumTest; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +@MediumTest +public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActionLabelKlpTests { + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_LXX_DARK; + } + + @Override + public void testActionUnspecified() { + super.testActionUnspecified(); + } + + @Override + public void testActionNone() { + super.testActionNone(); + } + + @Override + public void testActionGo() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_GO, + KeyboardIconsSet.NAME_GO_KEY); + } + } + + @Override + public void testActionSearch() { + super.testActionSearch(); + } + + @Override + public void testActionSend() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEND, + KeyboardIconsSet.NAME_SEND_KEY); + } + } + + @Override + public void testActionNext() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NEXT, + KeyboardIconsSet.NAME_NEXT_KEY); + } + } + + @Override + public void testActionDone() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_DONE, + KeyboardIconsSet.NAME_DONE_KEY); + } + } + + @Override + public void testActionPrevious() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, + KeyboardIconsSet.NAME_PREVIOUS_KEY); + } + } + + @Override + public void testActionCustom() { + super.testActionCustom(); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java index e4aaf0317..bf19a8b46 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java @@ -25,10 +25,15 @@ import java.util.ArrayList; @SmallTest public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase { - private static final int NUMBER_OF_SUBTYPES = 70; + private static final int NUMBER_OF_SUBTYPES = 68; private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 45; private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2; + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_KLP; + } + private static String toString(final ArrayList<InputMethodSubtype> subtypeList) { final StringBuilder sb = new StringBuilder(); for (int index = 0; index < subtypeList.size(); index++) { diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index 0fb6ff2b4..ab7d1b28d 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -19,7 +19,6 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.res.Resources; import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; import android.view.ContextThemeWrapper; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; @@ -31,34 +30,31 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; import java.util.Locale; -@SmallTest -public class KeyboardLayoutSetTestsBase extends AndroidTestCase { - private static final KeyboardTheme DEFAULT_KEYBOARD_THEME = - KeyboardTheme.getDefaultKeyboardTheme(); - +public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { // All input method subtypes of LatinIME. - private final ArrayList<InputMethodSubtype> mAllSubtypesList = CollectionUtils.newArrayList(); - private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = - CollectionUtils.newArrayList(); - private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = - CollectionUtils.newArrayList(); + private final ArrayList<InputMethodSubtype> mAllSubtypesList = new ArrayList<>(); + private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = new ArrayList<>(); + private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = new ArrayList<>(); private Context mThemeContext; private int mScreenMetrics; + protected abstract int getKeyboardThemeForTests(); + @Override protected void setUp() throws Exception { super.setUp(); mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics); - mThemeContext = new ContextThemeWrapper(mContext, DEFAULT_KEYBOARD_THEME.mStyleId); + final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById( + getKeyboardThemeForTests()); + mThemeContext = new ContextThemeWrapper(mContext, keyboardTheme.mStyleId); RichInputMethodManager.init(mThemeContext); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); @@ -118,13 +114,13 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase { protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, final EditorInfo editorInfo) { - return createKeyboardLayoutSet(subtype, editorInfo, false /* isShortcutImeEnabled */, - false /* showsVoiceInputKey */, false /* isLanguageSwitchKeyEnabled */); + return createKeyboardLayoutSet(subtype, editorInfo, false /* voiceInputKeyEnabled */, + false /* languageSwitchKeyEnabled */); } protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, - final EditorInfo editorInfo, final boolean isShortcutImeEnabled, - final boolean showsVoiceInputKey, final boolean isLanguageSwitchKeyEnabled) { + final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, + final boolean languageSwitchKeyEnabled) { final Context context = mThemeContext; final Resources res = context.getResources(); final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); @@ -132,7 +128,8 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase { final Builder builder = new Builder(context, editorInfo); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight) .setSubtype(subtype) - .setOptions(isShortcutImeEnabled, showsVoiceInputKey, isLanguageSwitchKeyEnabled); + .setVoiceInputKeyEnabled(voiceInputKeyEnabled) + .setLanguageSwitchKeyEnabled(languageSwitchKeyEnabled); return builder.build(); } } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java new file mode 100644 index 000000000..f9d98afa2 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -0,0 +1,337 @@ +/* + * 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; + +import android.content.SharedPreferences; +import android.os.Build.VERSION_CODES; +import android.preference.PreferenceManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +@SmallTest +public class KeyboardThemeTests extends AndroidTestCase { + private SharedPreferences mPrefs; + + // TODO: Remove this constant once the *next* version becomes available. + private static final int VERSION_CODES_LXX = VERSION_CODES.CUR_DEVELOPMENT; + + private static final int THEME_ID_NULL = -1; + private static final int THEME_ID_UNKNOWN = -2; + private static final int THEME_ID_ILLEGAL = -3; + private static final String ILLEGAL_THEME_ID_STRING = "ThisCausesNumberFormatExecption"; + private static final int THEME_ID_ICS = KeyboardTheme.THEME_ID_ICS; + private static final int THEME_ID_KLP = KeyboardTheme.THEME_ID_KLP; + private static final int THEME_ID_LXX_DARK = KeyboardTheme.THEME_ID_LXX_DARK; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + } + + /* + * Helper functions. + */ + + private static boolean isValidKeyboardThemeId(final int themeId) { + switch (themeId) { + case THEME_ID_ICS: + case THEME_ID_KLP: + case THEME_ID_LXX_DARK: + return true; + default: + return false; + } + } + + private void setKeyboardThemePreference(final String prefKey, final int themeId) { + final String themeIdString = Integer.toString(themeId); + if (isValidKeyboardThemeId(themeId) || themeId == THEME_ID_UNKNOWN) { + // Set valid theme id to preference. + mPrefs.edit().putString(prefKey, themeIdString).apply(); + return; + } + if (themeId == THEME_ID_NULL) { + // Simulate undefined preference. + mPrefs.edit().remove(prefKey).apply(); + return; + } + // themeId == THEME_ID_ILLEGAL + // Simulate illegal format theme id in preference. + mPrefs.edit().putString(prefKey, ILLEGAL_THEME_ID_STRING).apply(); + } + + private void assertKeyboardTheme(final int sdkVersion, final int expectedThemeId) { + assertEquals(expectedThemeId, KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion).mThemeId); + } + + /* + * Test keyboard theme preference on the same platform version and the same keyboard version. + */ + + private void assertKeyboardThemePreference(final int sdkVersion, final int previousThemeId, + final int expectedThemeId) { + // Clear preferences before testing. + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + // Set the preference of the sdkVersion to previousThemeId. + final String prefKey = KeyboardTheme.getPreferenceKey(sdkVersion); + setKeyboardThemePreference(prefKey, previousThemeId); + assertKeyboardTheme(sdkVersion, expectedThemeId); + } + + private void assertKeyboardThemePreferenceOnKlp(final int sdkVersion) { + final int defaultThemeId = THEME_ID_KLP; + assertKeyboardThemePreference(sdkVersion, THEME_ID_NULL, defaultThemeId); + assertKeyboardThemePreference(sdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertKeyboardThemePreference(sdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_DARK, THEME_ID_LXX_DARK); + assertKeyboardThemePreference(sdkVersion, THEME_ID_UNKNOWN, defaultThemeId); + assertKeyboardThemePreference(sdkVersion, THEME_ID_ILLEGAL, defaultThemeId); + } + + public void testKeyboardThemePreferenceOnKlp() { + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH); + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN); + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN_MR1); + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.JELLY_BEAN_MR2); + assertKeyboardThemePreferenceOnKlp(VERSION_CODES.KITKAT); + } + + private void assertKeyboardThemePreferenceOnLxx(final int sdkVersion) { + final int defaultThemeId = THEME_ID_LXX_DARK; + assertKeyboardThemePreference(sdkVersion, THEME_ID_NULL, defaultThemeId); + assertKeyboardThemePreference(sdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertKeyboardThemePreference(sdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertKeyboardThemePreference(sdkVersion, THEME_ID_LXX_DARK, THEME_ID_LXX_DARK); + assertKeyboardThemePreference(sdkVersion, THEME_ID_UNKNOWN, defaultThemeId); + assertKeyboardThemePreference(sdkVersion, THEME_ID_ILLEGAL, defaultThemeId); + } + + public void testKeyboardThemePreferenceOnLxx() { + assertKeyboardThemePreferenceOnLxx(VERSION_CODES_LXX); + } + + /* + * Test default keyboard theme based on the platform version. + */ + + private void assertDefaultKeyboardTheme(final int sdkVersion, final int previousThemeId, + final int expectedThemeId) { + final String oldPrefKey = KeyboardTheme.KLP_KEYBOARD_THEME_KEY; + setKeyboardThemePreference(oldPrefKey, previousThemeId); + + final KeyboardTheme defaultTheme = + KeyboardTheme.getDefaultKeyboardTheme(mPrefs, sdkVersion); + + assertNotNull(defaultTheme); + assertEquals(expectedThemeId, defaultTheme.mThemeId); + if (sdkVersion <= VERSION_CODES.KITKAT) { + // Old preference must be retained if it is valid. Otherwise it must be pruned. + assertEquals(isValidKeyboardThemeId(previousThemeId), mPrefs.contains(oldPrefKey)); + return; + } + // Old preference must be removed. + assertFalse(mPrefs.contains(oldPrefKey)); + } + + private void assertDefaultKeyboardThemeOnKlp(final int sdkVersion) { + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_NULL, THEME_ID_KLP); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP); + } + + public void testDefaultKeyboardThemeOnKlp() { + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH); + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN); + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN_MR1); + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.JELLY_BEAN_MR2); + assertDefaultKeyboardThemeOnKlp(VERSION_CODES.KITKAT); + } + + private void assertDefaultKeyboardThemeOnLxx(final int sdkVersion) { + // Forced to switch to LXX theme. + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_NULL, THEME_ID_LXX_DARK); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ICS, THEME_ID_LXX_DARK); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_KLP, THEME_ID_LXX_DARK); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_DARK); + assertDefaultKeyboardTheme(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_DARK); + } + + public void testDefaultKeyboardThemeOnLxx() { + assertDefaultKeyboardThemeOnLxx(VERSION_CODES_LXX); + } + + /* + * Test keyboard theme preference while upgrading the keyboard that doesn't support LXX theme + * to the keyboard that supports LXX theme. + */ + + private void assertUpgradeKeyboardToLxxOn(final int sdkVersion, final int previousThemeId, + final int expectedThemeId) { + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, previousThemeId); + // Clean up new keyboard theme preference to simulate "upgrade to LXX keyboard". + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + + final KeyboardTheme theme = KeyboardTheme.getKeyboardTheme(mPrefs, sdkVersion); + + assertNotNull(theme); + assertEquals(expectedThemeId, theme.mThemeId); + if (sdkVersion <= VERSION_CODES.KITKAT) { + // New preference must not exist. + assertFalse(mPrefs.contains(KeyboardTheme.LXX_KEYBOARD_THEME_KEY)); + // Old preference must be retained if it is valid. Otherwise it must be pruned. + assertEquals(isValidKeyboardThemeId(previousThemeId), + mPrefs.contains(KeyboardTheme.KLP_KEYBOARD_THEME_KEY)); + if (isValidKeyboardThemeId(previousThemeId)) { + // Old preference must have an expected value. + assertEquals(mPrefs.getString(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, null), + Integer.toString(expectedThemeId)); + } + return; + } + // Old preference must be removed. + assertFalse(mPrefs.contains(KeyboardTheme.KLP_KEYBOARD_THEME_KEY)); + // New preference must not exist. + assertFalse(mPrefs.contains(KeyboardTheme.LXX_KEYBOARD_THEME_KEY)); + } + + private void assertUpgradeKeyboardToLxxOnKlp(final int sdkVersion) { + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_NULL, THEME_ID_KLP); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP); + } + + // Upgrading keyboard on I,J and K. + public void testUpgradeKeyboardToLxxOnKlp() { + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH); + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN); + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN_MR1); + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.JELLY_BEAN_MR2); + assertUpgradeKeyboardToLxxOnKlp(VERSION_CODES.KITKAT); + } + + private void assertUpgradeKeyboardToLxxOnLxx(final int sdkVersion) { + // Forced to switch to LXX theme. + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_NULL, THEME_ID_LXX_DARK); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ICS, THEME_ID_LXX_DARK); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_KLP, THEME_ID_LXX_DARK); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_DARK); + assertUpgradeKeyboardToLxxOn(sdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_DARK); + } + + // Upgrading keyboard on L. + public void testUpgradeKeyboardToLxxOnLxx() { + assertUpgradeKeyboardToLxxOnLxx(VERSION_CODES_LXX); + } + + /* + * Test keyboard theme preference while upgrading platform version. + */ + + private void assertUpgradePlatformFromTo(final int oldSdkVersion, final int newSdkVersion, + final int previousThemeId, final int expectedThemeId) { + if (newSdkVersion < oldSdkVersion) { + // No need to test. + return; + } + // Clean up preferences. + setKeyboardThemePreference(KeyboardTheme.KLP_KEYBOARD_THEME_KEY, THEME_ID_NULL); + setKeyboardThemePreference(KeyboardTheme.LXX_KEYBOARD_THEME_KEY, THEME_ID_NULL); + + final String oldPrefKey = KeyboardTheme.getPreferenceKey(oldSdkVersion); + setKeyboardThemePreference(oldPrefKey, previousThemeId); + + assertKeyboardTheme(newSdkVersion, expectedThemeId); + } + + private void assertUpgradePlatformFromKlpToKlp(final int oldSdkVersion, + final int newSdkVersion) { + assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_KLP); + assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_KLP); + assertUpgradePlatformFromTo(oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_KLP); + } + + private void assertUpgradePlatformToKlpFrom(final int oldSdkVersion) { + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.ICE_CREAM_SANDWICH); + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN); + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN_MR1); + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.JELLY_BEAN_MR2); + assertUpgradePlatformFromKlpToKlp(oldSdkVersion, VERSION_CODES.KITKAT); + } + + // Update platform from I,J, and K to I,J, and K + public void testUpgradePlatformToKlpFromKlp() { + assertUpgradePlatformToKlpFrom(VERSION_CODES.ICE_CREAM_SANDWICH); + assertUpgradePlatformToKlpFrom(VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN); + assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN_MR1); + assertUpgradePlatformToKlpFrom(VERSION_CODES.JELLY_BEAN_MR2); + assertUpgradePlatformToKlpFrom(VERSION_CODES.KITKAT); + } + + private void assertUpgradePlatformToLxxFrom(final int oldSdkVersion) { + // Forced to switch to LXX theme. + final int newSdkVersion = VERSION_CODES_LXX; + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_DARK); + } + + // Update platform from I,J, and K to L + public void testUpgradePlatformToLxx() { + assertUpgradePlatformToLxxFrom(VERSION_CODES.ICE_CREAM_SANDWICH); + assertUpgradePlatformToLxxFrom(VERSION_CODES.ICE_CREAM_SANDWICH_MR1); + assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN); + assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN_MR1); + assertUpgradePlatformToLxxFrom(VERSION_CODES.JELLY_BEAN_MR2); + assertUpgradePlatformToLxxFrom(VERSION_CODES.KITKAT); + } + + // Update platform from L to L. + public void testUpgradePlatformToLxxFromLxx() { + final int oldSdkVersion = VERSION_CODES_LXX; + final int newSdkVersion = VERSION_CODES_LXX; + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_NULL, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_ICS, THEME_ID_ICS); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_KLP, THEME_ID_KLP); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_UNKNOWN, THEME_ID_LXX_DARK); + assertUpgradePlatformFromTo( + oldSdkVersion, newSdkVersion, THEME_ID_ILLEGAL, THEME_ID_LXX_DARK); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java index eb906cd9f..72211015f 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSetTests.java @@ -23,7 +23,6 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.RichInputMethodManager; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; @@ -42,7 +41,7 @@ public final class KeyboardTextsSetTests extends AndroidTestCase { RichInputMethodManager.init(getContext()); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); - final ArrayList<InputMethodSubtype> allSubtypesList = CollectionUtils.newArrayList(); + final ArrayList<InputMethodSubtype> allSubtypesList = new ArrayList<>(); final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme(); final int subtypeCount = imi.getSubtypeCount(); for (int index = 0; index < subtypeCount; index++) { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java index 514ad1cf0..29b169d80 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java @@ -22,8 +22,6 @@ import android.content.res.Resources; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.MediumTest; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; @@ -53,7 +51,7 @@ public class MoreKeySpecSplitTests extends InstrumentationTestCase { } private static String[] getAllResourceIdNames(final Class<?> resourceIdClass) { - final ArrayList<String> names = CollectionUtils.newArrayList(); + final ArrayList<String> names = new ArrayList<>(); for (final Field field : resourceIdClass.getFields()) { if (field.getType() == Integer.TYPE) { names.add(field.getName()); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java index b0493d3f1..fa818654e 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java @@ -74,7 +74,12 @@ public final class Arabic extends LayoutBase { // U+060C: "،" ARABIC COMMA return joinKeys(key("\u060C", SETTINGS_KEY)); } - return super.getKeysLeftToSpacebar(isPhone); + // U+060C: "،" ARABIC COMMA + // U+061F: "؟" ARABIC QUESTION MARK + // U+061B: "؛" ARABIC SEMICOLON + return joinKeys(key("\u060C", joinMoreKeys( + ":", "!", "\u061F", "\u061B", "-", "\"", "'", SETTINGS_KEY)), + "_"); } @Override @@ -85,9 +90,7 @@ public final class Arabic extends LayoutBase { // U+060C: "،" ARABIC COMMA // U+061F: "؟" ARABIC QUESTION MARK // U+061B: "؛" ARABIC SEMICOLON - return joinKeys( - key("\u060C", joinMoreKeys(":", "!", "\u061F", "\u061B", "-", "/", "\"", "'")), - key(".", getPunctuationMoreKeys(isPhone))); + return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone))); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java index 204bb01f7..eb64b832b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java @@ -56,11 +56,19 @@ public final class ArmenianPhonetic extends LayoutBase { } @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + // U+002C: "," COMMA + // U+055D: "՝" ARMENIAN COMMA + return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY)) + : joinKeys(key("\u055D", SETTINGS_KEY), "_"); + } + + @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+0589: "։" ARMENIAN FULL STOP // U+055D: "՝" ARMENIAN COMMA final ExpectedKey fullStopKey = key("\u0589", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(fullStopKey) : joinKeys("\u055D", fullStopKey); + return isPhone ? joinKeys(fullStopKey) : joinKeys("/", fullStopKey); } @Override @@ -121,7 +129,7 @@ public final class ArmenianPhonetic extends LayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(5, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java index 99cf6e50e..e75cfd0ff 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java @@ -51,7 +51,8 @@ public final class Dvorak extends LayoutBase { @Override public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { - return isPhone ? joinKeys(key("q", SHORTCUT_KEY, SETTINGS_KEY)) : joinKeys(key("/")); + return isPhone ? joinKeys(key("q", SETTINGS_KEY)) : + joinKeys(SETTINGS_KEY, key("_", moreKey("-"))); } @Override @@ -60,7 +61,7 @@ public final class Dvorak extends LayoutBase { convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone)); return isPhone ? joinKeys(key("z", punctuationMoreKeys)) - : joinKeys(key("?", moreKey("!")), key("-", moreKey("_"))); + : joinKeys("/", key("?", moreKey("!"))); } private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys( diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java index a0070891a..a513740e7 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java @@ -72,7 +72,13 @@ public final class Farsi extends LayoutBase { // U+060C: "،" ARABIC COMMA return joinKeys(key("\u060C", SETTINGS_KEY)); } - return super.getKeysLeftToSpacebar(isPhone); + // U+060C: "،" ARABIC COMMA + // U+061F: "؟" ARABIC QUESTION MARK + // U+061B: "؛" ARABIC SEMICOLON + return joinKeys(key("\u060C", joinMoreKeys( + ":", "!", "\u061F", "\u061B", "-", RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL, + SETTINGS_KEY)), + "_"); } @Override @@ -80,18 +86,12 @@ public final class Farsi extends LayoutBase { if (isPhone) { return super.getKeysRightToSpacebar(isPhone); } - // U+060C: "،" ARABIC COMMA - // U+061F: "؟" ARABIC QUESTION MARK - // U+061B: "؛" ARABIC SEMICOLON - return joinKeys( - key("\u060C", joinMoreKeys(":", "!", "\u061F", "\u061B", "-", "/", - RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL)), - key(".", getPunctuationMoreKeys(isPhone))); + return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone))); } @Override public ExpectedKey[] getSpaceKeys(final boolean isPhone) { - return joinKeys(SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); + return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java index afd26e428..a7f682340 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java @@ -16,29 +16,12 @@ package com.android.inputmethod.keyboard.layout; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.SIGN_ANUSVARA; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.SIGN_CANDRABINDU; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.SIGN_NUKTA; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.SIGN_VIRAMA; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.SIGN_VISARGA; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_AA; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_AI; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_AU; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_CANDRA_E; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_CANDRA_O; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_E; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_I; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_II; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_O; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_U; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_UU; -import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.VOWEL_SIGN_VOCALIC_R; +import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*; import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer; import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; -import com.android.inputmethod.latin.Constants; import java.util.Locale; @@ -67,7 +50,7 @@ public final class HindiCompact extends LayoutBase { public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+0964: "।" DEVANAGARI DANDA final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(periodKey) : joinKeys(",", periodKey); + return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java index e7f6a6552..143ccf6eb 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java @@ -94,7 +94,7 @@ public final class Khmer extends LayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(5, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java index 6f2ef216f..e7be9982a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java @@ -98,7 +98,7 @@ public final class Lao extends LayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(5, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java index 4123a22ef..c5223720c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java @@ -29,7 +29,6 @@ import java.util.Locale; * The base class of keyboard layout. */ public abstract class LayoutBase extends AbstractLayoutBase { - /** * This class is used to customize common keyboard layout to language specific layout. */ @@ -152,7 +151,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { * keyboard. */ public ExpectedKey[] getSpaceKeys(final boolean isPhone) { - return joinKeys(SPACE_KEY); + return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY); } /** @@ -161,7 +160,9 @@ public abstract class LayoutBase extends AbstractLayoutBase { * @return the array of {@link ExpectedKey} that should be placed at left of the spacebar. */ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { - return isPhone ? joinKeys(key(",", SETTINGS_KEY)) : joinKeys("/"); + // U+002C: "," COMMA + return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY)) + : joinKeys(key("\u002C", SETTINGS_KEY), "_"); } /** @@ -171,7 +172,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { */ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(periodKey) : joinKeys(",", periodKey); + return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); } /** @@ -296,7 +297,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(2, ENTER_KEY) - .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(4, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(3, (Object[])customizer.getLeftShiftKeys(isPhone)) @@ -306,6 +307,10 @@ public abstract class LayoutBase extends AbstractLayoutBase { /** * Get common alphabet layout. This layout doesn't contain any special keys. + * + * A keyboard layout is an array of rows, and a row consists of an array of + * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s. + * * @param isPhone true if requesting phone's layout. * @return the common alphabet keyboard layout. */ @@ -313,6 +318,10 @@ public abstract class LayoutBase extends AbstractLayoutBase { /** * Get common alphabet shifted layout. This layout doesn't contain any special keys. + * + * A keyboard layout is an array of rows, and a row consists of an array of + * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s. + * * @param isPhone true if requesting phone's layout. * @param elementId the element id of the requesting shifted mode. * @return the common alphabet shifted keyboard layout. @@ -327,9 +336,13 @@ public abstract class LayoutBase extends AbstractLayoutBase { /** * Get the complete expected keyboard layout. + * + * A keyboard layout is an array of rows, and a row consists of an array of + * {@link ExpectedKey}s. Each row may have different number of {@link ExpectedKey}s. + * * @param isPhone true if requesting phone's layout. * @param elementId the element id of the requesting keyboard mode. - * @return + * @return the keyboard layout of the <code>elementId</code>. */ public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) { if (elementId == KeyboardId.ELEMENT_SYMBOLS) { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java new file mode 100644 index 000000000..00cf838f9 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/Marathi.java @@ -0,0 +1,179 @@ +/* + * 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.layout; + +import static com.android.inputmethod.keyboard.layout.DevanagariLetterConstants.*; + +import com.android.inputmethod.keyboard.layout.Hindi.HindiCustomizer; +import com.android.inputmethod.keyboard.layout.Hindi.HindiSymbols; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; + +import java.util.Locale; + +/** + * The Marathi keyboard. + */ +public final class Marathi extends LayoutBase { + private static final String LAYOUT_NAME = "marathi"; + + public Marathi(final LayoutCustomizer customizer) { + super(customizer, HindiSymbols.class, SymbolsShifted.class); + } + + @Override + public String getName() { return LAYOUT_NAME; } + + public static class MarathiCustomizer extends HindiCustomizer { + public MarathiCustomizer(final Locale locale) { super(locale); } + + @Override + public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { + return EMPTY_KEYS; + } + } + + @Override + ExpectedKey[][] getCommonAlphabetLayout(boolean isPhone) { return ALPHABET_COMMON; } + + @Override + ExpectedKey[][] getCommonAlphabetShiftLayout(final boolean isPhone, final int elementId) { + return null; + } + + private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() + .setKeysOfRow(1, + // U+094C: "ौ" DEVANAGARI VOWEL SIGN AU + // U+0914: "औ" DEVANAGARI LETTER AU + // U+0967: "१" DEVANAGARI DIGIT ONE + key(VOWEL_SIGN_AU, "\u094C", joinMoreKeys("\u0914", "\u0967", "1")), + // U+0948: "ै" DEVANAGARI VOWEL SIGN AI + // U+0910: "ऐ" DEVANAGARI LETTER AI + // U+0968: "२" DEVANAGARI DIGIT TWO + key(VOWEL_SIGN_AI, "\u0948", joinMoreKeys("\u0910", "\u0968", "2")), + // U+093E: "ा" DEVANAGARI VOWEL SIGN AA + // U+0906: "आ" DEVANAGARI LETTER AA + // U+0969: "३" DEVANAGARI DIGIT THREE + key(VOWEL_SIGN_AA, "\u093E", joinMoreKeys("\u0906", "\u0969", "3")), + // U+0940: "ी" DEVANAGARI VOWEL SIGN II + // U+0908: "ई" DEVANAGARI LETTER II + // U+096A: "४" DEVANAGARI DIGIT FOUR + key(VOWEL_SIGN_II, "\u0940", joinMoreKeys("\u0908", "\u096A", "4")), + // U+0942: "ू" DEVANAGARI VOWEL SIGN UU + // U+090A: "ऊ" DEVANAGARI LETTER UU + // U+096B: "५" DEVANAGARI DIGIT FIVE + key(VOWEL_SIGN_UU, "\u0942", joinMoreKeys("\u090A", "\u096B", "5")), + // U+092C: "ब" DEVANAGARI LETTER BA + // U+092D: "भ" DEVANAGARI LETTER BHA + // U+096C: "६" DEVANAGARI DIGIT SIX + key("\u092C", joinMoreKeys("\u092D", "\u096C", "6")), + // U+0939: "ह" DEVANAGARI LETTER HA + // U+096D: "७" DEVANAGARI DIGIT SEVEN + key("\u0939", joinMoreKeys("\u096D", "7")), + // U+0917: "ग" DEVANAGARI LETTER GA + // U+0918: "घ" DEVANAGARI LETTER GHA + // U+096E: "८" DEVANAGARI DIGIT EIGHT + key("\u0917", joinMoreKeys("\u0918", "\u096E", "8")), + // U+0926: "द" DEVANAGARI LETTER DA + // U+0927: "ध" DEVANAGARI LETTER DHA + // U+096F: "९" DEVANAGARI DIGIT NINE + key("\u0926", joinMoreKeys("\u0927", "\u096F", "9")), + // U+091C: "ज" DEVANAGARI LETTER JA + // U+091D: "झ" DEVANAGARI LETTER JHA + // U+091C/U+094D/U+091E: + // "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA + // U+0966: "०" DEVANAGARI DIGIT ZERO + key("\u091C", joinMoreKeys("\u091D", "\u091C\u094D\u091E", "\u0966", "0")), + // U+0921: "ड" DEVANAGARI LETTER DDA + // U+0922: "ढ" DEVANAGARI LETTER DDHA + key("\u0921", moreKey("\u0922"))) + .setKeysOfRow(2, + // U+094B: "ो" DEVANAGARI VOWEL SIGN O + // U+0913: "ओ" DEVANAGARI LETTER O + key(VOWEL_SIGN_O, "\u094B", moreKey("\u0913")), + // U+0947: "े" DEVANAGARI VOWEL SIGN E + // U+090F: "ए" DEVANAGARI LETTER SHORT E + key(VOWEL_SIGN_E, "\u0947", moreKey("\u090F")), + // U+094D: "्" DEVANAGARI SIGN VIRAMA + // U+0905: "अ" DEVANAGARI LETTER A + key(SIGN_VIRAMA, "\u094D", moreKey("\u0905")), + // U+093F: "ि" DEVANAGARI VOWEL SIGN I + // U+0907: "इ" DEVANAGARI LETTER I + key(VOWEL_SIGN_I, "\u093F", moreKey("\u0907")), + // U+0941: "ु" DEVANAGARI VOWEL SIGN U + // U+0909: "उ" DEVANAGARI LETTER U + key(VOWEL_SIGN_U, "\u0941", moreKey("\u0909")), + // U+092A: "प" DEVANAGARI LETTER PA + // U+092B: "फ" DEVANAGARI LETTER PHA + key("\u092A", moreKey("\u092B")), + // U+0930: "र" DEVANAGARI LETTER RA + // U+0931: "ऱ" DEVANAGARI LETTER RRA + // U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R + // U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R + key("\u0930", joinMoreKeys( + "\u0931", "\u090B", moreKey(VOWEL_SIGN_VOCALIC_R, "\u0943"))), + // U+0915: "क" DEVANAGARI LETTER KA + // U+0916: "ख" DEVANAGARI LETTER KHA + key("\u0915", moreKey("\u0916")), + // U+0924: "त" DEVANAGARI LETTER TA + // U+0925: "थ" DEVANAGARI LETTER THA + // U+0924/U+094D/U+0930: + // "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA + key("\u0924", joinMoreKeys("\u0925", "\u0924\u094D\u0930")), + // U+091A: "च" DEVANAGARI LETTER CA + // U+091B: "छ" DEVANAGARI LETTER CHA + key("\u091A", moreKey("\u091B")), + // U+091F: "ट" DEVANAGARI LETTER TTA + // U+0920: "ठ" DEVANAGARI LETTER TTHA + key("\u091F", moreKey("\u0920"))) + .setKeysOfRow(3, + // U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O + // U+0911: "ऑ" DEVANAGARI LETTER CANDRA O + key(VOWEL_SIGN_CANDRA_O, "\u0949", moreKey("\u0911")), + // U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E + // U+090D: "ऍ" DEVANAGARI LETTER CANDRA E + key(VOWEL_SIGN_CANDRA_E, "\u0945", moreKey("\u090D")), + // U+0902: "ं" DEVANAGARI SIGN ANUSVARA + // U+0903: "ः" DEVANAGARI SIGN VISARGA + // U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU + key(SIGN_ANUSVARA, "\u0902", joinMoreKeys( + moreKey(SIGN_VISARGA, "\u0903"), moreKey(SIGN_CANDRABINDU, "\u0901"))), + // U+092E: "म" DEVANAGARI LETTER MA + "\u092E", + // U+0928: "न" DEVANAGARI LETTER NA + // U+0923: "ण" DEVANAGARI LETTER NNA + // U+091E: "ञ" DEVANAGARI LETTER NYA + // U+0919: "ङ" DEVANAGARI LETTER NGA + key("\u0928", joinMoreKeys("\u0923", "\u091E", "\u0919")), + // U+0935: "व" DEVANAGARI LETTER VA + "\u0935", + // U+0932: "ल" DEVANAGARI LETTER LA + // U+0933: "ळ" DEVANAGARI LETTER LLA + key("\u0932", moreKey("\u0933")), + // U+0938: "स" DEVANAGARI LETTER SA + // U+0936: "श" DEVANAGARI LETTER SHA + // U+0937: "ष" DEVANAGARI LETTER SSA + // U+0936/U+094D/U+0930: + // "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA + key("\u0938", joinMoreKeys("\u0936", "\u0937", "\u0936\u094D\u0930")), + // U+092F: "य" DEVANAGARI LETTER YA + "\u092F", + // U+0915/U+094D/U+0937: + // "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA + "\u0915\u094D\u0937") + .build(); +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java index 2d1c901b9..1b571acc6 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java @@ -48,12 +48,18 @@ public final class Myanmar extends LayoutBase { } @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + // U+002C: "," COMMA + // U+104A: "၊" MYANMAR SIGN LITTLE SECTION + return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY)) + : joinKeys(key("\u104A", moreKey(","), SETTINGS_KEY), "_"); + } + + @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+104B: "။" MYANMAR SIGN SECTION - // U+104A: "၊" MYANMAR SIGN LITTLE SECTION final ExpectedKey periodKey = key("\u104B", getPunctuationMoreKeys(isPhone)); - final ExpectedKey commaKey = key("\u104A", moreKey(",")); - return isPhone ? joinKeys(periodKey) : joinKeys(commaKey, periodKey); + return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); } @Override @@ -106,7 +112,7 @@ public final class Myanmar extends LayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(5, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java index 7048dbb73..7933d078c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/NepaliRomanized.java @@ -47,7 +47,7 @@ public final class NepaliRomanized extends LayoutBase { @Override public ExpectedKey[] getSpaceKeys(final boolean isPhone) { - return joinKeys(SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); + return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY)); } // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java index 726fefc68..5f3e4b196 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java @@ -167,7 +167,7 @@ public class Symbols extends AbstractLayoutBase { // U+00BF: "¿" INVERTED QUESTION MARK key("?", moreKey("\u00BF"))) .setKeysOfRow(4, - key("_"), key("/"), SPACE_KEY, key(","), + key(","), key("_"), SPACE_KEY, key("/"), // U+2026: "…" HORIZONTAL ELLIPSIS key(".", moreKey("\u2026"))) .build(); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java index f611310af..3265e10e1 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java @@ -117,15 +117,16 @@ public class SymbolsShifted extends AbstractLayoutBase { // U+2105: "℅" CARE OF "\\", "\u00A9", "\u00AE", "\u2122", "\u2105", "[", "]") .setKeysOfRow(4, + ",", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+2264: "≤" LESS-THAN OR EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")), + SPACE_KEY, // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+2265: "≥" GREATER-THAN EQUAL TO // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK key(">", joinMoreKeys("\u203A", "\u2265", "\u00BB")), - SPACE_KEY, ",", // U+2026: "…" HORIZONTAL ELLIPSIS key(".", moreKey("\u2026"))) .build(); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java index 253c93b83..af4abea93 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java @@ -111,7 +111,7 @@ public final class Thai extends LayoutBase { } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey(), SETTINGS_KEY) + .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) .addKeysOnTheRightOfRow(5, EMOJI_KEY); } builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java index 3365b92ec..6e721047c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java @@ -21,9 +21,9 @@ import java.util.Arrays; /** * This class builds a keyboard that is a two dimensional array of elements <code>E</code>. * - * A keyboard consists of array of rows, and a row consists of array of elements. Each row may have - * different number of elements. A element of a keyboard can be specified by a row number and a - * column number, both numbers starts from 1. + * A keyboard consists of an array of rows, and a row consists of an array of elements. Each row + * may have different number of elements. A element of a keyboard can be specified by a row number + * and a column number, both numbers starts from 1. * * @param <E> the type of a keyboard element. A keyboard element must be an immutable object. */ @@ -39,8 +39,7 @@ abstract class AbstractKeyboardBuilder<E> { abstract E[][] newArrayOfArray(final int size); /** - * Construct a builder filled with the default element. - * @param dimensions the integer array of each row's size. + * Construct an empty builder. */ AbstractKeyboardBuilder() { mRows = newArrayOfArray(0); @@ -80,7 +79,7 @@ abstract class AbstractKeyboardBuilder<E> { * Get the current contents of the specified row. * @param row the row number to get the contents. * @return the array of elements at row number <code>row</code>. - * @throws {@link RuntimeException} if <code>row</code> is illegal. + * @throws RuntimeException if <code>row</code> is illegal. */ E[] getRowAt(final int row) { final int rowIndex = row - 1; @@ -94,7 +93,7 @@ abstract class AbstractKeyboardBuilder<E> { * Set an array of elements to the specified row. * @param row the row number to set <code>elements</code>. * @param elements the array of elements to set at row number <code>row</code>. - * @throws {@link RuntimeException} if <code>row</code> is illegal. + * @throws RuntimeException if <code>row</code> is illegal. */ void setRowAt(final int row, final E[] elements) { final int rowIndex = row - 1; @@ -114,7 +113,7 @@ abstract class AbstractKeyboardBuilder<E> { * @param element the element to set or insert at <code>row,column</code>. * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>. * Otherwise the <code>element</code> replace the element at <code>row,column</code>. - * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal. + * @throws RuntimeException if <code>row</code> or <code>column</code> is illegal. */ void setElementAt(final int row, final int column, final E element, final boolean insert) { final E[] elements = getRowAt(row); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java index 6176f6a3e..9e0039d84 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java @@ -19,7 +19,6 @@ package com.android.inputmethod.keyboard.layout.expected; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.StringUtils; /** * Base class to create an expected keyboard for unit test. @@ -109,6 +108,8 @@ public abstract class AbstractLayoutBase { // Icon ids. private static final int ICON_DELETE = KeyboardIconsSet.getIconId( KeyboardIconsSet.NAME_DELETE_KEY); + private static final int ICON_SPACE = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_SPACE_KEY); private static final int ICON_TAB = KeyboardIconsSet.getIconId( KeyboardIconsSet.NAME_TAB_KEY); private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId( @@ -131,6 +132,5 @@ public abstract class AbstractLayoutBase { ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH); public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER); public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI); - public static final ExpectedKey SPACE_KEY = key( - StringUtils.newSingleCodePointString(Constants.CODE_SPACE)); + public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java index 26d2e2ad2..56149189f 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java @@ -20,7 +20,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; @@ -28,10 +27,13 @@ import java.util.List; /** * This class builds an actual keyboard for unit test. + * + * An actual keyboard is an array of rows, and a row consists of an array of {@link Key}s. + * Each row may have different number of {@link Key}s. */ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { private static ArrayList<Key> filterOutSpacer(final List<Key> keys) { - final ArrayList<Key> filteredKeys = CollectionUtils.newArrayList(); + final ArrayList<Key> filteredKeys = new ArrayList<>(); for (final Key key : keys) { if (key.isSpacer()) { continue; @@ -43,7 +45,8 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { /** * Create the keyboard that consists of the array of rows of the actual keyboard's keys. - * @param sortedKeys the sorted list of keys of the actual keyboard. + * @param sortedKeys keys list of the actual keyboard that is sorted from top-left to + * bottom-right. * @return the actual keyboard grouped with rows. */ public static Key[][] buildKeyboard(final List<Key> sortedKeys) { @@ -51,15 +54,15 @@ public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { final ArrayList<Key> filteredSortedKeys = filterOutSpacer(sortedKeys); // Grouping keys into rows. - final ArrayList<ArrayList<Key>> rows = CollectionUtils.newArrayList(); - ArrayList<Key> elements = CollectionUtils.newArrayList(); + final ArrayList<ArrayList<Key>> rows = new ArrayList<>(); + ArrayList<Key> elements = new ArrayList<>(); int lastY = filteredSortedKeys.get(0).getY(); for (final Key key : filteredSortedKeys) { if (lastY != key.getY()) { // A new row is starting. lastY = key.getY(); rows.add(elements); - elements = CollectionUtils.newArrayList(); + elements = new ArrayList<>(); } elements.add(key); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java index ad08ba5a6..0e1c71cd1 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java @@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard.layout.expected; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.internal.MoreKeySpec; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; @@ -73,9 +72,8 @@ public class ExpectedKey { // The additional more keys can be defined independently from other more keys. // The position of the additional more keys in the long press popup keyboard can be // controlled by specifying special marker "%" in the usual more keys definitions. - final ArrayList<ExpectedKey> moreKeysList = CollectionUtils.newArrayList(); - final ArrayList<ExpectedAdditionalMoreKey> additionalMoreKeys = - CollectionUtils.newArrayList(); + final ArrayList<ExpectedKey> moreKeysList = new ArrayList<>(); + final ArrayList<ExpectedAdditionalMoreKey> additionalMoreKeys = new ArrayList<>(); int firstAdditionalMoreKeyIndex = -1; for (int index = 0; index < moreKeys.length; index++) { final ExpectedKey moreKey = moreKeys[index]; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java index f068ad11d..9b7de88ea 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java @@ -16,14 +16,17 @@ package com.android.inputmethod.keyboard.layout.expected; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; /** * This class builds an expected keyboard for unit test. + * + * An expected keyboard is an array of rows, and a row consists of an array of {@link ExpectedKey}s. + * Each row may have different number of {@link ExpectedKey}s. While building an expected keyboard, + * an {@link ExpectedKey} can be specified by a row number and a column number, both numbers starts + * from 1. */ public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<ExpectedKey> { public ExpectedKeyboardBuilder() { @@ -111,7 +114,7 @@ public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<Expec // Helper method to create {@link ExpectedKey} array by joining {@link ExpectedKey}, // {@link ExpectedKey} array, and {@link String}. static ExpectedKey[] joinKeys(final Object ... keys) { - final ArrayList<ExpectedKey> list = CollectionUtils.newArrayList(); + final ArrayList<ExpectedKey> list = new ArrayList<>(); for (final Object key : keys) { if (key instanceof ExpectedKey) { list.add((ExpectedKey)key); @@ -212,7 +215,7 @@ public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<Expec * @param keys the array of keys to insert at <code>row,column</code>. Each key can be * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}. * @return this builder. - * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal. + * @throws RuntimeException if <code>row</code> or <code>column</code> is illegal. */ public ExpectedKeyboardBuilder insertKeysAtRow(final int row, final int column, final Object ... keys) { @@ -229,7 +232,7 @@ public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<Expec * @param keys the array of keys to add on the left most of the row. Each key can be * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}. * @return this builder. - * @throws {@link RuntimeException} if <code>row</code> is illegal. + * @throws RuntimeException if <code>row</code> is illegal. */ public ExpectedKeyboardBuilder addKeysOnTheLeftOfRow(final int row, final Object ... keys) { @@ -247,7 +250,7 @@ public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<Expec * @param keys the array of keys to add on the right most of the row. Each key can be * {@link ExpectedKey}, {@link ExpectedKey} array, and {@link String}. * @return this builder. - * @throws {@link RuntimeException} if <code>row</code> is illegal. + * @throws RuntimeException if <code>row</code> is illegal. */ public ExpectedKeyboardBuilder addKeysOnTheRightOfRow(final int row, final Object ... keys) { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java index 29264ff3b..3e82f65bf 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/EnglishCustomizer.java @@ -27,34 +27,34 @@ class EnglishCustomizer extends LayoutCustomizer { @Override public ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) { return builder - // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON - .setMoreKeysOf("e", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113") + .setMoreKeysOf("e", "\u00E9", "\u00E8", "\u00EA", "\u00EB", "\u0113") + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON - .setMoreKeysOf("u", "\u00FB", "\u00FC", "\u00F9", "\u00FA", "\u016B") + .setMoreKeysOf("u", "\u00FA", "\u00FB", "\u00FC", "\u00F9", "\u016B") + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE - .setMoreKeysOf("i", "\u00EE", "\u00EF", "\u00ED", "\u012B", "\u00EC") + .setMoreKeysOf("i", "\u00ED", "\u00EE", "\u00EF", "\u012B", "\u00EC") + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE // U+0153: "œ" LATIN SMALL LIGATURE OE // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE .setMoreKeysOf("o", - "\u00F4", "\u00F6", "\u00F2", "\u00F3", "\u0153", "\u00F8", "\u014D", + "\u00F3", "\u00F4", "\u00F6", "\u00F2", "\u0153", "\u00F8", "\u014D", "\u00F5") // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java index 4002c49c2..a22ed60ac 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java @@ -24,6 +24,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase; +import com.android.inputmethod.keyboard.KeyboardTheme; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.expected.AbstractLayoutBase; import com.android.inputmethod.keyboard.layout.expected.ActualKeyboardBuilder; @@ -51,7 +52,14 @@ abstract class LayoutTestsBase extends KeyboardLayoutSetTestsBase { mSubtype = getSubtype(mLayout.getLocale(), mLayout.getName()); mLogTag = SubtypeLocaleUtils.getSubtypeNameForLogging(mSubtype) + "/" + (isPhone() ? "phone" : "tablet"); - mKeyboardLayoutSet = createKeyboardLayoutSet(mSubtype, null /* editorInfo */); + // TODO: Test with language switch key enabled and disabled. + mKeyboardLayoutSet = createKeyboardLayoutSet(mSubtype, null /* editorInfo */, + true /* voiceInputKeyEnabled */, true /* languageSwitchKeyEnabled */); + } + + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_KLP; } // Those helper methods have a lower case name to be readable when defining expected keyboard diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java index 2e676df26..6380da524 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsHindiCompact.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import com.android.inputmethod.keyboard.layout.HindiCompact; import com.android.inputmethod.keyboard.layout.HindiCompact.HindiCompactCustomizer; @@ -27,7 +27,7 @@ import java.util.Locale; /** * hi: Hindi/hindi_compact */ -@SmallTest +@Suppress public final class TestsHindiCompact extends LayoutTestsBase { private static final Locale LOCALE = new Locale("hi"); private static final LayoutBase LAYOUT = new HindiCompact(new HindiCompactCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java new file mode 100644 index 000000000..d45d99d10 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMarathiIN.java @@ -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. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.Suppress; + +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Marathi; +import com.android.inputmethod.keyboard.layout.Marathi.MarathiCustomizer; + +import java.util.Locale; + +/** + * mr_IN: Marathi (India)/marathi + */ +@Suppress +public final class TestsMarathiIN extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("mr", "IN"); + private static final LayoutBase LAYOUT = new Marathi(new MarathiCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java index e6d3b3b92..a0bd50c9a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Myanmar; @@ -27,7 +27,7 @@ import java.util.Locale; /** * my_MM: Myanmar (Myanmar)/myanmar */ -@SmallTest +@Suppress public final class TestsMyanmarMM extends LayoutTestsBase { private static final Locale LOCALE = new Locale("my", "MM"); private static final LayoutBase LAYOUT = new Myanmar(new MyanmarCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index ae2205b36..28cce834c 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -46,6 +46,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; 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 }; private int mCurrentTime = 0; @@ -61,20 +63,29 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { super.tearDown(); } + private static boolean supportsBeginningOfSentence(final int formatVersion) { + return formatVersion > FormatSpec.VERSION401; + } + private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, final int probability) { - binaryDictionary.addUnigramWord(word, probability, "" /* shortcutTarget */, + binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, - false /* isNotAWord */, false /* isBlacklisted */, - mCurrentTime /* timestamp */); + false /* isBeginningOfSentence */, false /* isNotAWord */, + false /* isBlacklisted */, mCurrentTime /* timestamp */); } private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, final String word1, final int probability) { - binaryDictionary.addBigramWords(word0, word1, probability, + binaryDictionary.addNgramEntry(new PrevWordsInfo(word0), word1, probability, mCurrentTime /* timestamp */); } + private static boolean isValidBigram(final BinaryDictionary binaryDictionary, + final String word0, final String word1) { + return binaryDictionary.isValidNgram(new PrevWordsInfo(word0), word1); + } + private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { // 30 days. final int timeToElapse = (int)TimeUnit.SECONDS.convert(30, TimeUnit.DAYS); @@ -93,19 +104,22 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private File createEmptyDictionaryAndGetFile(final String dictId, final int formatVersion) throws IOException { - if (formatVersion == FormatSpec.VERSION4) { - return createEmptyVer4DictionaryAndGetFile(dictId); + if (formatVersion == FormatSpec.VERSION4 + || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION4_DEV) { + return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); } else { throw new IOException("Dictionary format version " + formatVersion + " is not supported."); } } - private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { + private File createEmptyVer4DictionaryAndGetFile(final String dictId, final int formatVersion) + throws IOException { final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<String, String>(); + Map<String, String> attributeMap = new HashMap<>(); attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); @@ -113,12 +127,12 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { DictionaryHeader.ATTRIBUTE_VALUE_TRUE); attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY, DictionaryHeader.ATTRIBUTE_VALUE_TRUE); - if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4, + if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) { return file; } else { throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); + + " cannot be created. Foramt version: " + formatVersion); } } @@ -131,7 +145,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testReadDictInJavaSide() { - testReadDictInJavaSide(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testReadDictInJavaSide(formatVersion); + } } private void testReadDictInJavaSide(final int formatVersion) { @@ -176,10 +192,6 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testControlCurrentTime() { - testControlCurrentTime(FormatSpec.VERSION4); - } - - private void testControlCurrentTime(final int formatVersion) { final int TEST_COUNT = 1000; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); @@ -195,7 +207,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddValidAndInvalidWords() { - testAddValidAndInvalidWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddValidAndInvalidWords(formatVersion); + } } private void testAddValidAndInvalidWords(final int formatVersion) { @@ -219,26 +233,28 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { assertTrue(binaryDictionary.isValidWord("b")); addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); - assertFalse(binaryDictionary.isValidBigram("a", "b")); + assertFalse(isValidBigram(binaryDictionary, "a", "b")); addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); - assertTrue(binaryDictionary.isValidBigram("a", "b")); + assertTrue(isValidBigram(binaryDictionary, "a", "b")); addUnigramWord(binaryDictionary, "c", DUMMY_PROBABILITY); addBigramWords(binaryDictionary, "a", "c", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidBigram("a", "c")); + assertTrue(isValidBigram(binaryDictionary, "a", "c")); // Add bigrams of not valid unigrams. addBigramWords(binaryDictionary, "x", "y", Dictionary.NOT_A_PROBABILITY); - assertFalse(binaryDictionary.isValidBigram("x", "y")); + assertFalse(isValidBigram(binaryDictionary, "x", "y")); addBigramWords(binaryDictionary, "x", "y", DUMMY_PROBABILITY); - assertFalse(binaryDictionary.isValidBigram("x", "y")); + assertFalse(isValidBigram(binaryDictionary, "x", "y")); binaryDictionary.close(); dictFile.delete(); } public void testDecayingProbability() { - testDecayingProbability(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDecayingProbability(formatVersion); + } } private void testDecayingProbability(final int formatVersion) { @@ -269,9 +285,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidBigram("a", "b")); + assertTrue(isValidBigram(binaryDictionary, "a", "b")); forcePassingShortTime(binaryDictionary); - assertFalse(binaryDictionary.isValidBigram("a", "b")); + assertFalse(isValidBigram(binaryDictionary, "a", "b")); addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); @@ -282,18 +298,20 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); - assertTrue(binaryDictionary.isValidBigram("a", "b")); + assertTrue(isValidBigram(binaryDictionary, "a", "b")); forcePassingShortTime(binaryDictionary); - assertTrue(binaryDictionary.isValidBigram("a", "b")); + assertTrue(isValidBigram(binaryDictionary, "a", "b")); forcePassingLongTime(binaryDictionary); - assertFalse(binaryDictionary.isValidBigram("a", "b")); + assertFalse(isValidBigram(binaryDictionary, "a", "b")); binaryDictionary.close(); dictFile.delete(); } public void testAddManyUnigramsToDecayingDict() { - testAddManyUnigramsToDecayingDict(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyUnigramsToDecayingDict(formatVersion); + } } private void testAddManyUnigramsToDecayingDict(final int formatVersion) { @@ -315,7 +333,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { setCurrentTimeForTestMode(mCurrentTime); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<String>(); + final ArrayList<String> words = new ArrayList<>(); for (int i = 0; i < unigramCount; i++) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -352,7 +370,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testOverflowUnigrams() { - testOverflowUnigrams(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testOverflowUnigrams(formatVersion); + } } private void testOverflowUnigrams(final int formatVersion) { @@ -411,7 +431,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddManyBigramsToDecayingDict() { - testAddManyBigramsToDecayingDict(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyBigramsToDecayingDict(formatVersion); + } } private void testAddManyBigramsToDecayingDict(final int formatVersion) { @@ -434,8 +456,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { setCurrentTimeForTestMode(mCurrentTime); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<String>(); - final ArrayList<Pair<String, String>> bigrams = new ArrayList<Pair<String, String>>(); + final ArrayList<String> words = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigrams = new ArrayList<>(); for (int i = 0; i < unigramCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -449,7 +471,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } final String word0 = words.get(word0Index); final String word1 = words.get(word1Index); - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + final Pair<String, String> bigram = new Pair<>(word0, word1); bigrams.add(bigram); } @@ -485,7 +507,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testOverflowBigrams() { - testOverflowBigrams(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testOverflowBigrams(formatVersion); + } } private void testOverflowBigrams(final int formatVersion) { @@ -511,7 +535,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { setCurrentTimeForTestMode(mCurrentTime); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<String>(); + final ArrayList<String> words = new ArrayList<>(); for (int i = 0; i < unigramCount; i++) { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); @@ -534,8 +558,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { for (int j = 0; j < weakBigramTypedCount; j++) { addBigramWords(binaryDictionary, weak, target, DUMMY_PROBABILITY); } - assertTrue(binaryDictionary.isValidBigram(strong, target)); - assertTrue(binaryDictionary.isValidBigram(weak, target)); + assertTrue(isValidBigram(binaryDictionary, strong, target)); + assertTrue(isValidBigram(binaryDictionary, weak, target)); for (int i = 0; i < bigramCount; i++) { final int word0Index = random.nextInt(words.size()); @@ -556,10 +580,112 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { Integer.parseInt(binaryDictionary.getPropertyForTest( BinaryDictionary.BIGRAM_COUNT_QUERY)); assertTrue(bigramCountBeforeGC > bigramCountAfterGC); - assertTrue(binaryDictionary.isValidBigram(strong, target)); - assertFalse(binaryDictionary.isValidBigram(weak, target)); + assertTrue(isValidBigram(binaryDictionary, strong, target)); + assertFalse(isValidBigram(binaryDictionary, weak, target)); break; } } } + + public void testDictMigration() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } + } + + private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { + setCurrentTimeForTestMode(mCurrentTime); + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); + assertTrue(binaryDictionary.isValidWord("aaa")); + addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY); + assertFalse(binaryDictionary.isValidWord("bbb")); + addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "ccc", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "abc", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "aaa", "abc", DUMMY_PROBABILITY); + assertTrue(isValidBigram(binaryDictionary, "aaa", "abc")); + addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY); + assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); + + assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion()); + assertTrue(binaryDictionary.migrateTo(toFormatVersion)); + assertTrue(binaryDictionary.isValidDictionary()); + assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); + assertTrue(binaryDictionary.isValidWord("aaa")); + assertFalse(binaryDictionary.isValidWord("bbb")); + assertTrue(binaryDictionary.getFrequency("aaa") < binaryDictionary.getFrequency("ccc")); + addUnigramWord(binaryDictionary, "bbb", Dictionary.NOT_A_PROBABILITY); + assertTrue(binaryDictionary.isValidWord("bbb")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "abc")); + assertFalse(isValidBigram(binaryDictionary, "aaa", "bbb")); + addBigramWords(binaryDictionary, "aaa", "bbb", Dictionary.NOT_A_PROBABILITY); + assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); + binaryDictionary.close(); + dictFile.delete(); + } + + public void testBeginningOfSentence() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + if (supportsBeginningOfSentence(formatVersion)) { + testBeginningOfSentence(formatVersion); + } + } + } + + private void testBeginningOfSentence(final int formatVersion) { + setCurrentTimeForTestMode(mCurrentTime); + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + binaryDictionary.addUnigramEntry("", DUMMY_PROBABILITY, "" /* shortcutTarget */, + BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, + true /* isBeginningOfSentence */, true /* isNotAWord */, false /* isBlacklisted */, + mCurrentTime); + final PrevWordsInfo prevWordsInfoStartOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE; + addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); + binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + mCurrentTime); + assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); + binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + mCurrentTime); + addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); + binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, + mCurrentTime); + assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); + assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + + forcePassingLongTime(binaryDictionary); + assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); + assertFalse(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + + addUnigramWord(binaryDictionary, "aaa", DUMMY_PROBABILITY); + binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "aaa", DUMMY_PROBABILITY, + mCurrentTime); + addUnigramWord(binaryDictionary, "bbb", DUMMY_PROBABILITY); + binaryDictionary.addNgramEntry(prevWordsInfoStartOfSentence, "bbb", DUMMY_PROBABILITY, + mCurrentTime); + assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "aaa")); + assertTrue(binaryDictionary.isValidNgram(prevWordsInfoStartOfSentence, "bbb")); + binaryDictionary.close(); + dictFile.delete(); + } } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 0fb0fa587..160b08c4f 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -43,34 +43,49 @@ import java.util.Random; 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 }; + + private static boolean canCheckBigramProbability(final int formatVersion) { + return formatVersion > FormatSpec.VERSION401; + } + + private static boolean supportsBeginningOfSentence(final int formatVersion) { + return formatVersion > FormatSpec.VERSION401; + } private File createEmptyDictionaryAndGetFile(final String dictId, final int formatVersion) throws IOException { - if (formatVersion == FormatSpec.VERSION4) { - return createEmptyVer4DictionaryAndGetFile(dictId); + if (formatVersion == FormatSpec.VERSION4 + || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION4_DEV) { + return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); } else { throw new IOException("Dictionary format version " + formatVersion + " is not supported."); } } - private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { + private File createEmptyVer4DictionaryAndGetFile(final String dictId, + final int formatVersion) throws IOException { final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); file.delete(); file.mkdir(); - Map<String, String> attributeMap = new HashMap<String, String>(); - if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4, + Map<String, String> attributeMap = new HashMap<>(); + if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), formatVersion, Locale.ENGLISH, attributeMap)) { return file; } else { throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); + + " cannot be created. Format version: " + formatVersion); } } public void testIsValidDictionary() { - testIsValidDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testIsValidDictionary(formatVersion); + } } private void testIsValidDictionary(final int formatVersion) { @@ -98,7 +113,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testConstructingDictionaryOnMemory() { - testConstructingDictionaryOnMemory(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testConstructingDictionaryOnMemory(formatVersion); + } } private void testConstructingDictionaryOnMemory(final int formatVersion) { @@ -129,7 +146,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddTooLongWord() { - testAddTooLongWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddTooLongWord(formatVersion); + } } private void testAddTooLongWord(final int formatVersion) { @@ -155,8 +174,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { addUnigramWord(binaryDictionary, validLongWord, probability); addUnigramWord(binaryDictionary, invalidLongWord, probability); // Too long short cut. - binaryDictionary.addUnigramWord("a", probability, invalidLongWord, - 10 /* shortcutProbability */, false /* isNotAWord */, false /* isBlacklisted */, + binaryDictionary.addUnigramEntry("a", probability, invalidLongWord, + 10 /* shortcutProbability */, false /* isBeginningOfSentence */, + false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); addUnigramWord(binaryDictionary, "abc", probability); final int updatedProbability = 200; @@ -173,22 +193,39 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } - private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, + private static void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, final int probability) { - binaryDictionary.addUnigramWord(word, probability, "" /* shortcutTarget */, + binaryDictionary.addUnigramEntry(word, probability, "" /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, - false /* isNotAWord */, false /* isBlacklisted */, - BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + false /* isBeginningOfSentence */, false /* isNotAWord */, + false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } - private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, + private static void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, final String word1, final int probability) { - binaryDictionary.addBigramWords(word0, word1, probability, + binaryDictionary.addNgramEntry(new PrevWordsInfo(word0), word1, probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); } + private static boolean isValidBigram(final BinaryDictionary binaryDictionary, + final String word0, final String word1) { + return binaryDictionary.isValidNgram(new PrevWordsInfo(word0), word1); + } + + private static void removeBigramEntry(final BinaryDictionary binaryDictionary, + final String word0, final String word1) { + binaryDictionary.removeNgramEntry(new PrevWordsInfo(word0), word1); + } + + private static int getBigramProbability(final BinaryDictionary binaryDictionary, + final String word0, final String word1) { + return binaryDictionary.getNgramProbability(new PrevWordsInfo(word0), word1); + } + public void testAddUnigramWord() { - testAddUnigramWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddUnigramWord(formatVersion); + } } private void testAddUnigramWord(final int formatVersion) { @@ -230,7 +267,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRandomlyAddUnigramWord() { - testRandomlyAddUnigramWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomlyAddUnigramWord(formatVersion); + } } private void testRandomlyAddUnigramWord(final int formatVersion) { @@ -248,7 +287,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final HashMap<String, Integer> probabilityMap = new HashMap<String, Integer>(); + final HashMap<String, Integer> probabilityMap = new HashMap<>(); // Test a word that isn't contained within the dictionary. final Random random = new Random(seed); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); @@ -266,7 +305,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWords() { - testAddBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddBigramWords(formatVersion); + } } private void testAddBigramWords(final int formatVersion) { @@ -281,8 +322,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); final int unigramProbability = 100; - final int bigramProbability = 10; - final int updatedBigramProbability = 15; + final int bigramProbability = 150; + final int updatedBigramProbability = 200; addUnigramWord(binaryDictionary, "aaa", unigramProbability); addUnigramWord(binaryDictionary, "abb", unigramProbability); addUnigramWord(binaryDictionary, "bcc", unigramProbability); @@ -291,31 +332,32 @@ public class BinaryDictionaryTests extends AndroidTestCase { addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); - final int probability = binaryDictionary.calculateProbability(unigramProbability, - bigramProbability); - assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); - assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc")); - assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa")); - assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc")); - assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb")); - assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc")); - assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa")); - assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "abb")); + 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")); + } addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability); - final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability, - updatedBigramProbability); - assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb")); + if (canCheckBigramProbability(formatVersion)) { + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "aaa", "abb")); + } - assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa")); - assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc")); - assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa")); + assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); + assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); + assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa")); assertEquals(Dictionary.NOT_A_PROBABILITY, - binaryDictionary.getBigramProbability("bcc", "aaa")); + getBigramProbability(binaryDictionary, "bcc", "aaa")); assertEquals(Dictionary.NOT_A_PROBABILITY, - binaryDictionary.getBigramProbability("bcc", "bbc")); + getBigramProbability(binaryDictionary, "bcc", "bbc")); assertEquals(Dictionary.NOT_A_PROBABILITY, - binaryDictionary.getBigramProbability("aaa", "aaa")); + getBigramProbability(binaryDictionary, "aaa", "aaa")); // Testing bigram link. addUnigramWord(binaryDictionary, "abcde", unigramProbability); @@ -324,17 +366,26 @@ public class BinaryDictionaryTests extends AndroidTestCase { addUnigramWord(binaryDictionary, "fgh", unigramProbability); addUnigramWord(binaryDictionary, "abc", unigramProbability); addUnigramWord(binaryDictionary, "f", unigramProbability); - assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij")); + + if (canCheckBigramProbability(formatVersion)) { + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, "abcde", "fghij")); + } assertEquals(Dictionary.NOT_A_PROBABILITY, - binaryDictionary.getBigramProbability("abcde", "fgh")); + getBigramProbability(binaryDictionary, "abcde", "fgh")); addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability); - assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij")); + if (canCheckBigramProbability(formatVersion)) { + assertEquals(updatedBigramProbability, + getBigramProbability(binaryDictionary, "abcde", "fghij")); + } dictFile.delete(); } public void testRandomlyAddBigramWords() { - testRandomlyAddBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomlyAddBigramWords(formatVersion); + } } private void testRandomlyAddBigramWords(final int formatVersion) { @@ -354,12 +405,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final ArrayList<String> words = new ArrayList<String>(); - final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>(); + final ArrayList<String> words = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = - new HashMap<Pair<String, String>, Integer>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); for (int i = 0; i < wordCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -375,27 +425,32 @@ public class BinaryDictionaryTests extends AndroidTestCase { if (TextUtils.equals(word0, word1)) { continue; } - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + final Pair<String, String> bigram = new Pair<>(word0, word1); bigramWords.add(bigram); - final int bigramProbability = random.nextInt(0xF); + final int unigramProbability = unigramProbabilities.get(word1); + final int bigramProbability = + unigramProbability + random.nextInt(0xFF - unigramProbability); bigramProbabilities.put(bigram, bigramProbability); addBigramWords(binaryDictionary, word0, word1, bigramProbability); } for (final Pair<String, String> bigram : bigramWords) { - final int unigramProbability = unigramProbabilities.get(bigram.second); final int bigramProbability = bigramProbabilities.get(bigram); - final int probability = binaryDictionary.calculateProbability(unigramProbability, - bigramProbability); - assertEquals(probability, - binaryDictionary.getBigramProbability(bigram.first, bigram.second)); + assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, + isValidBigram(binaryDictionary, bigram.first, bigram.second)); + if (canCheckBigramProbability(formatVersion)) { + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); + } } dictFile.delete(); } public void testRemoveBigramWords() { - testRemoveBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRemoveBigramWords(formatVersion); + } } private void testRemoveBigramWords(final int formatVersion) { @@ -409,7 +464,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); final int unigramProbability = 100; - final int bigramProbability = 10; + final int bigramProbability = 150; addUnigramWord(binaryDictionary, "aaa", unigramProbability); addUnigramWord(binaryDictionary, "abb", unigramProbability); addUnigramWord(binaryDictionary, "bcc", unigramProbability); @@ -418,34 +473,36 @@ public class BinaryDictionaryTests extends AndroidTestCase { addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); - assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); - assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc")); - assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa")); - assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "abb")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc")); + assertTrue(isValidBigram(binaryDictionary, "abb", "aaa")); + assertTrue(isValidBigram(binaryDictionary, "abb", "bcc")); - binaryDictionary.removeBigramWords("aaa", "abb"); - assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb")); + removeBigramEntry(binaryDictionary, "aaa", "abb"); + assertFalse(isValidBigram(binaryDictionary, "aaa", "abb")); addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); - assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "abb")); - binaryDictionary.removeBigramWords("aaa", "bcc"); - assertEquals(false, binaryDictionary.isValidBigram("aaa", "bcc")); - binaryDictionary.removeBigramWords("abb", "aaa"); - assertEquals(false, binaryDictionary.isValidBigram("abb", "aaa")); - binaryDictionary.removeBigramWords("abb", "bcc"); - assertEquals(false, binaryDictionary.isValidBigram("abb", "bcc")); + removeBigramEntry(binaryDictionary, "aaa", "bcc"); + assertFalse(isValidBigram(binaryDictionary, "aaa", "bcc")); + removeBigramEntry(binaryDictionary, "abb", "aaa"); + assertFalse(isValidBigram(binaryDictionary, "abb", "aaa")); + removeBigramEntry(binaryDictionary, "abb", "bcc"); + assertFalse(isValidBigram(binaryDictionary, "abb", "bcc")); - binaryDictionary.removeBigramWords("aaa", "abb"); + removeBigramEntry(binaryDictionary, "aaa", "abb"); // Test remove non-existing bigram operation. - binaryDictionary.removeBigramWords("aaa", "abb"); - binaryDictionary.removeBigramWords("bcc", "aaa"); + removeBigramEntry(binaryDictionary, "aaa", "abb"); + removeBigramEntry(binaryDictionary, "bcc", "aaa"); dictFile.delete(); } public void testFlushDictionary() { - testFlushDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testFlushDictionary(formatVersion); + } } private void testFlushDictionary(final int formatVersion) { @@ -497,7 +554,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testFlushWithGCDictionary() { - testFlushWithGCDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testFlushWithGCDictionary(formatVersion); + } } private void testFlushWithGCDictionary(final int formatVersion) { @@ -512,7 +571,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); final int unigramProbability = 100; - final int bigramProbability = 10; + final int bigramProbability = 150; addUnigramWord(binaryDictionary, "aaa", unigramProbability); addUnigramWord(binaryDictionary, "abb", unigramProbability); addUnigramWord(binaryDictionary, "bcc", unigramProbability); @@ -526,18 +585,18 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final int probability = binaryDictionary.calculateProbability(unigramProbability, - bigramProbability); assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); assertEquals(unigramProbability, binaryDictionary.getFrequency("abb")); assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc")); - assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb")); - assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc")); - assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa")); - assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc")); - assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa")); - assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc")); - assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa")); + 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")); + } + assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa")); + assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc")); + assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa")); binaryDictionary.flushWithGC(); binaryDictionary.close(); @@ -545,7 +604,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWordsAndFlashWithGC() { - testAddBigramWordsAndFlashWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddBigramWordsAndFlashWithGC(formatVersion); + } } // TODO: Evaluate performance of GC @@ -567,12 +628,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final ArrayList<String> words = new ArrayList<String>(); - final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>(); + final ArrayList<String> words = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = - new HashMap<Pair<String, String>, Integer>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); for (int i = 0; i < wordCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -588,9 +648,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { if (TextUtils.equals(word0, word1)) { continue; } - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + final Pair<String, String> bigram = new Pair<>(word0, word1); bigramWords.add(bigram); - final int bigramProbability = random.nextInt(0xF); + final int unigramProbability = unigramProbabilities.get(word1); + final int bigramProbability = + unigramProbability + random.nextInt(0xFF - unigramProbability); bigramProbabilities.put(bigram, bigramProbability); addBigramWords(binaryDictionary, word0, word1, bigramProbability); } @@ -601,20 +663,24 @@ public class BinaryDictionaryTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + for (final Pair<String, String> bigram : bigramWords) { - final int unigramProbability = unigramProbabilities.get(bigram.second); final int bigramProbability = bigramProbabilities.get(bigram); - final int probability = binaryDictionary.calculateProbability(unigramProbability, - bigramProbability); - assertEquals(probability, - binaryDictionary.getBigramProbability(bigram.first, bigram.second)); + assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, + isValidBigram(binaryDictionary, bigram.first, bigram.second)); + if (canCheckBigramProbability(formatVersion)) { + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); + } } dictFile.delete(); } public void testRandomOperationsAndFlashWithGC() { - testRandomOperationsAndFlashWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomOperationsAndFlashWithGC(formatVersion); + } } private void testRandomOperationsAndFlashWithGC(final int formatVersion) { @@ -639,12 +705,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final ArrayList<String> words = new ArrayList<String>(); - final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>(); + final ArrayList<String> words = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigramWords = new ArrayList<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = - new HashMap<Pair<String, String>, Integer>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); for (int i = 0; i < initialUnigramCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); @@ -680,8 +745,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { if (TextUtils.equals(word0, word1)) { continue; } - final int bigramProbability = random.nextInt(0xF); - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + final int unigramProbability = unigramProbabilities.get(word1); + final int bigramProbability = + unigramProbability + random.nextInt(0xFF - unigramProbability); + final Pair<String, String> bigram = new Pair<>(word0, word1); bigramWords.add(bigram); bigramProbabilities.put(bigram, bigramProbability); addBigramWords(binaryDictionary, word0, word1, bigramProbability); @@ -692,7 +759,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final Pair<String, String> bigram = bigramWords.get(bigramIndex); bigramWords.remove(bigramIndex); bigramProbabilities.remove(bigram); - binaryDictionary.removeBigramWords(bigram.first, bigram.second); + removeBigramEntry(binaryDictionary, bigram.first, bigram.second); } } @@ -705,17 +772,20 @@ public class BinaryDictionaryTests extends AndroidTestCase { // Test whether the all bigram operations are collectlly handled. for (int i = 0; i < bigramWords.size(); i++) { final Pair<String, String> bigram = bigramWords.get(i); - final int unigramProbability = unigramProbabilities.get(bigram.second); final int probability; if (bigramProbabilities.containsKey(bigram)) { final int bigramProbability = bigramProbabilities.get(bigram); - probability = binaryDictionary.calculateProbability(unigramProbability, - bigramProbability); + probability = bigramProbability; } else { probability = Dictionary.NOT_A_PROBABILITY; } - assertEquals(probability, - binaryDictionary.getBigramProbability(bigram.first, bigram.second)); + + if (canCheckBigramProbability(formatVersion)) { + assertEquals(probability, + getBigramProbability(binaryDictionary, bigram.first, bigram.second)); + } + assertEquals(probability != Dictionary.NOT_A_PROBABILITY, + isValidBigram(binaryDictionary, bigram.first, bigram.second)); } binaryDictionary.flushWithGC(); binaryDictionary.close(); @@ -725,7 +795,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddManyUnigramsAndFlushWithGC() { - testAddManyUnigramsAndFlushWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyUnigramsAndFlushWithGC(formatVersion); + } } private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) { @@ -742,8 +814,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { fail("IOException while writing an initial dictionary : " + e); } - final ArrayList<String> words = new ArrayList<String>(); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final ArrayList<String> words = new ArrayList<>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); BinaryDictionary binaryDictionary; @@ -773,7 +845,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testUnigramAndBigramCount() { - testUnigramAndBigramCount(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testUnigramAndBigramCount(formatVersion); + } } private void testUnigramAndBigramCount(final int formatVersion) { @@ -791,8 +865,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { fail("IOException while writing an initial dictionary : " + e); } - final ArrayList<String> words = new ArrayList<String>(); - final HashSet<Pair<String, String>> bigrams = new HashSet<Pair<String, String>>(); + final ArrayList<String> words = new ArrayList<>(); + final HashSet<Pair<String, String>> bigrams = new HashSet<>(); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); BinaryDictionary binaryDictionary; @@ -812,18 +886,18 @@ public class BinaryDictionaryTests extends AndroidTestCase { if (TextUtils.equals(word0, word1)) { continue; } - bigrams.add(new Pair<String, String>(word0, word1)); + bigrams.add(new Pair<>(word0, word1)); final int bigramProbability = random.nextInt(0xF); addBigramWords(binaryDictionary, word0, word1, bigramProbability); } - assertEquals(new HashSet<String>(words).size(), Integer.parseInt( + assertEquals(new HashSet<>(words).size(), Integer.parseInt( binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); - assertEquals(new HashSet<Pair<String, String>>(bigrams).size(), Integer.parseInt( + assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt( binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); binaryDictionary.flushWithGC(); - assertEquals(new HashSet<String>(words).size(), Integer.parseInt( + assertEquals(new HashSet<>(words).size(), Integer.parseInt( binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY))); - assertEquals(new HashSet<Pair<String, String>>(bigrams).size(), Integer.parseInt( + assertEquals(new HashSet<>(bigrams).size(), Integer.parseInt( binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); binaryDictionary.close(); } @@ -832,7 +906,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddMultipleDictionaryEntries() { - testAddMultipleDictionaryEntries(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddMultipleDictionaryEntries(formatVersion); + } } private void testAddMultipleDictionaryEntries(final int formatVersion) { @@ -850,16 +926,15 @@ public class BinaryDictionaryTests extends AndroidTestCase { } final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = - new HashMap<Pair<String, String>, Integer>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); final LanguageModelParam[] languageModelParams = new LanguageModelParam[lmParamCount]; String prevWord = null; for (int i = 0; i < languageModelParams.length; i++) { final String word = CodePointUtils.generateWord(random, codePointSet); final int probability = random.nextInt(0xFF); - final int bigramProbability = random.nextInt(0xF); + final int bigramProbability = probability + random.nextInt(0xFF - probability); unigramProbabilities.put(word, probability); if (prevWord == null) { languageModelParams[i] = new LanguageModelParam(word, probability, @@ -867,7 +942,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { } else { languageModelParams[i] = new LanguageModelParam(prevWord, word, probability, bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - bigramProbabilities.put(new Pair<String, String>(prevWord, word), + bigramProbabilities.put(new Pair<>(prevWord, word), bigramProbability); } prevWord = (random.nextDouble() < bigramContinueRate) ? word : null; @@ -885,16 +960,20 @@ public class BinaryDictionaryTests extends AndroidTestCase { for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) { final String word0 = entry.getKey().first; final String word1 = entry.getKey().second; - final int unigramProbability = unigramProbabilities.get(word1); final int bigramProbability = entry.getValue(); - final int probability = binaryDictionary.calculateProbability( - unigramProbability, bigramProbability); - assertEquals(probability, binaryDictionary.getBigramProbability(word0, word1)); + assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY, + isValidBigram(binaryDictionary, word0, word1)); + if (canCheckBigramProbability(formatVersion)) { + assertEquals(bigramProbability, + getBigramProbability(binaryDictionary, word0, word1)); + } } } public void testGetWordProperties() { - testGetWordProperties(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testGetWordProperties(formatVersion); + } } private void testGetWordProperties(final int formatVersion) { @@ -918,11 +997,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord"); assertFalse(invalidWordProperty.isValid()); - final ArrayList<String> words = new ArrayList<String>(); - final HashMap<String, Integer> wordProbabilities = new HashMap<String, Integer>(); - final HashMap<String, HashSet<String>> bigrams = new HashMap<String, HashSet<String>>(); - final HashMap<Pair<String, String>, Integer> bigramProbabilities = - new HashMap<Pair<String, String>, Integer>(); + final ArrayList<String> words = new ArrayList<>(); + final HashMap<String, Integer> wordProbabilities = new HashMap<>(); + final HashMap<String, HashSet<String>> bigrams = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); for (int i = 0; i < UNIGRAM_COUNT; i++) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -930,9 +1008,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { final boolean isNotAWord = random.nextBoolean(); final boolean isBlacklisted = random.nextBoolean(); // TODO: Add tests for historical info. - binaryDictionary.addUnigramWord(word, unigramProbability, + binaryDictionary.addUnigramEntry(word, unigramProbability, null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY, - isNotAWord, isBlacklisted, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { binaryDictionary.flushWithGC(); } @@ -957,18 +1036,19 @@ public class BinaryDictionaryTests extends AndroidTestCase { } final String word0 = words.get(word0Index); final String word1 = words.get(word1Index); - final int bigramProbability = random.nextInt(0xF); - binaryDictionary.addBigramWords(word0, word1, bigramProbability, - BinaryDictionary.NOT_A_VALID_TIMESTAMP); + final int unigramProbability = wordProbabilities.get(word1); + final int bigramProbability = + unigramProbability + random.nextInt(0xFF - unigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { binaryDictionary.flushWithGC(); } if (!bigrams.containsKey(word0)) { - final HashSet<String> bigramWord1s = new HashSet<String>(); + final HashSet<String> bigramWord1s = new HashSet<>(); bigrams.put(word0, bigramWord1s); } bigrams.get(word0).add(word1); - bigramProbabilities.put(new Pair<String, String>(word0, word1), bigramProbability); + bigramProbabilities.put(new Pair<>(word0, word1), bigramProbability); } for (int i = 0; i < words.size(); i++) { @@ -982,18 +1062,18 @@ public class BinaryDictionaryTests extends AndroidTestCase { for (int j = 0; j < wordProperty.mBigrams.size(); j++) { final String word1 = wordProperty.mBigrams.get(j).mWord; assertTrue(bigramWord1s.contains(word1)); - final int bigramProbabilityDelta = bigramProbabilities.get( - new Pair<String, String>(word0, word1)); - final int unigramProbability = wordProbabilities.get(word1); - final int bigramProbablity = binaryDictionary.calculateProbability( - unigramProbability, bigramProbabilityDelta); - assertEquals(wordProperty.mBigrams.get(j).getProbability(), bigramProbablity); + if (canCheckBigramProbability(formatVersion)) { + final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1)); + assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability()); + } } } } public void testIterateAllWords() { - testIterateAllWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testIterateAllWords(formatVersion); + } } private void testIterateAllWords(final int formatVersion) { @@ -1017,12 +1097,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord"); assertFalse(invalidWordProperty.isValid()); - final ArrayList<String> words = new ArrayList<String>(); - final HashMap<String, Integer> wordProbabilitiesToCheckLater = - new HashMap<String, Integer>(); - final HashMap<String, HashSet<String>> bigrams = new HashMap<String, HashSet<String>>(); + final ArrayList<String> words = new ArrayList<>(); + final HashMap<String, Integer> wordProbabilitiesToCheckLater = new HashMap<>(); + final HashMap<String, HashSet<String>> bigrams = new HashMap<>(); final HashMap<Pair<String, String>, Integer> bigramProbabilitiesToCheckLater = - new HashMap<Pair<String, String>, Integer>(); + new HashMap<>(); for (int i = 0; i < UNIGRAM_COUNT; i++) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -1043,24 +1122,24 @@ public class BinaryDictionaryTests extends AndroidTestCase { } final String word0 = words.get(word0Index); final String word1 = words.get(word1Index); - final int bigramProbability = random.nextInt(0xF); - binaryDictionary.addBigramWords(word0, word1, bigramProbability, - BinaryDictionary.NOT_A_VALID_TIMESTAMP); + final int unigramProbability = wordProbabilitiesToCheckLater.get(word1); + final int bigramProbability = + unigramProbability + random.nextInt(0xFF - unigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { binaryDictionary.flushWithGC(); } if (!bigrams.containsKey(word0)) { - final HashSet<String> bigramWord1s = new HashSet<String>(); + final HashSet<String> bigramWord1s = new HashSet<>(); bigrams.put(word0, bigramWord1s); } bigrams.get(word0).add(word1); - bigramProbabilitiesToCheckLater.put( - new Pair<String, String>(word0, word1), bigramProbability); + bigramProbabilitiesToCheckLater.put(new Pair<>(word0, word1), bigramProbability); } - final HashSet<String> wordSet = new HashSet<String>(words); + final HashSet<String> wordSet = new HashSet<>(words); final HashSet<Pair<String, String>> bigramSet = - new HashSet<Pair<String,String>>(bigramProbabilitiesToCheckLater.keySet()); + new HashSet<>(bigramProbabilitiesToCheckLater.keySet()); int token = 0; do { final BinaryDictionary.GetNextWordPropertyResult result = @@ -1074,12 +1153,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { for (int j = 0; j < wordProperty.mBigrams.size(); j++) { final String word1 = wordProperty.mBigrams.get(j).mWord; assertTrue(bigramWord1s.contains(word1)); - final int unigramProbability = wordProbabilitiesToCheckLater.get(word1); - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); - final int bigramProbabilityDelta = bigramProbabilitiesToCheckLater.get(bigram); - final int bigramProbablity = binaryDictionary.calculateProbability( - unigramProbability, bigramProbabilityDelta); - assertEquals(wordProperty.mBigrams.get(j).getProbability(), bigramProbablity); + final Pair<String, String> bigram = new Pair<>(word0, word1); + if (canCheckBigramProbability(formatVersion)) { + final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram); + assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability()); + } bigramSet.remove(bigram); } token = result.mNextToken; @@ -1089,7 +1167,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddShortcuts() { - testAddShortcuts(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddShortcuts(formatVersion); + } } private void testAddShortcuts(final int formatVersion) { @@ -1105,26 +1185,26 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int unigramProbability = 100; final int shortcutProbability = 10; - binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz", - shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, - 0 /* timestamp */); + binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", + shortcutProbability, false /* isBeginningOfSentence */, + false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); WordProperty wordProperty = binaryDictionary.getWordProperty("aaa"); assertEquals(1, wordProperty.mShortcutTargets.size()); assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord); assertEquals(shortcutProbability, wordProperty.mShortcutTargets.get(0).getProbability()); final int updatedShortcutProbability = 2; - binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz", - updatedShortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, - 0 /* timestamp */); + binaryDictionary.addUnigramEntry("aaa", unigramProbability, "zzz", + updatedShortcutProbability, false /* isBeginningOfSentence */, + false /* isNotAWord */, false /* isBlacklisted */, 0 /* timestamp */); wordProperty = binaryDictionary.getWordProperty("aaa"); assertEquals(1, wordProperty.mShortcutTargets.size()); assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord); assertEquals(updatedShortcutProbability, wordProperty.mShortcutTargets.get(0).getProbability()); - binaryDictionary.addUnigramWord("aaa", unigramProbability, "yyy", - shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, - 0 /* timestamp */); - final HashMap<String, Integer> shortcutTargets = new HashMap<String, Integer>(); + binaryDictionary.addUnigramEntry("aaa", unigramProbability, "yyy", + shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, + false /* isBlacklisted */, 0 /* timestamp */); + final HashMap<String, Integer> shortcutTargets = new HashMap<>(); shortcutTargets.put("zzz", updatedShortcutProbability); shortcutTargets.put("yyy", shortcutProbability); wordProperty = binaryDictionary.getWordProperty("aaa"); @@ -1149,7 +1229,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddManyShortcuts() { - testAddManyShortcuts(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyShortcuts(formatVersion); + } } private void testAddManyShortcuts(final int formatVersion) { @@ -1160,10 +1242,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int codePointSetSize = 20; final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final ArrayList<String> words = new ArrayList<String>(); - final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); - final HashMap<String, HashMap<String, Integer>> shortcutTargets = - new HashMap<String, HashMap<String, Integer>>(); + final ArrayList<String> words = new ArrayList<>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<String, HashMap<String, Integer>> shortcutTargets = new HashMap<>(); File dictFile = null; try { @@ -1190,15 +1271,14 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int shortcutProbability = random.nextInt(0xF); final String word = words.get(random.nextInt(words.size())); final int unigramProbability = unigramProbabilities.get(word); - binaryDictionary.addUnigramWord(word, unigramProbability, shortcutTarget, - shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, - 0 /* timestamp */); + binaryDictionary.addUnigramEntry(word, unigramProbability, shortcutTarget, + shortcutProbability, false /* isBeginningOfSentence */, false /* isNotAWord */, + false /* isBlacklisted */, 0 /* timestamp */); if (shortcutTargets.containsKey(word)) { final HashMap<String, Integer> shortcutTargetsOfWord = shortcutTargets.get(word); shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); } else { - final HashMap<String, Integer> shortcutTargetsOfWord = - new HashMap<String, Integer>(); + final HashMap<String, Integer> shortcutTargetsOfWord = new HashMap<>(); shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); shortcutTargets.put(word, shortcutTargetsOfWord); } @@ -1223,4 +1303,198 @@ public class BinaryDictionaryTests extends AndroidTestCase { } } } + + public void testDictMigration() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } + } + + private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final int unigramProbability = 100; + addUnigramWord(binaryDictionary, "aaa", unigramProbability); + addUnigramWord(binaryDictionary, "bbb", unigramProbability); + final int bigramProbability = 150; + addBigramWords(binaryDictionary, "aaa", "bbb", bigramProbability); + final int shortcutProbability = 10; + binaryDictionary.addUnigramEntry("ccc", unigramProbability, "xxx", shortcutProbability, + false /* isBeginningOfSentence */, false /* isNotAWord */, + false /* isBlacklisted */, 0 /* timestamp */); + binaryDictionary.addUnigramEntry("ddd", unigramProbability, null /* shortcutTarget */, + Dictionary.NOT_A_PROBABILITY, false /* isBeginningOfSentence */, + true /* isNotAWord */, true /* isBlacklisted */, 0 /* timestamp */); + assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); + assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); + assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); + assertEquals(fromFormatVersion, binaryDictionary.getFormatVersion()); + assertTrue(binaryDictionary.migrateTo(toFormatVersion)); + assertTrue(binaryDictionary.isValidDictionary()); + assertEquals(toFormatVersion, binaryDictionary.getFormatVersion()); + assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa")); + assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb")); + if (canCheckBigramProbability(toFormatVersion)) { + assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb")); + } + assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb")); + WordProperty wordProperty = binaryDictionary.getWordProperty("ccc"); + assertEquals(1, wordProperty.mShortcutTargets.size()); + assertEquals("xxx", wordProperty.mShortcutTargets.get(0).mWord); + wordProperty = binaryDictionary.getWordProperty("ddd"); + assertTrue(wordProperty.mIsBlacklistEntry); + assertTrue(wordProperty.mIsNotAWord); + } + + public void testLargeDictMigration() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } + } + + private void testLargeDictMigration(final int fromFormatVersion, final int toFormatVersion) { + final int UNIGRAM_COUNT = 3000; + final int BIGRAM_COUNT = 3000; + final int codePointSetSize = 50; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", fromFormatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final ArrayList<String> words = new ArrayList<>(); + final ArrayList<Pair<String, String>> bigrams = new ArrayList<>(); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final HashMap<String, Integer> unigramProbabilities = new HashMap<>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = new HashMap<>(); + + for (int i = 0; i < UNIGRAM_COUNT; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + final int unigramProbability = random.nextInt(0xFF); + addUnigramWord(binaryDictionary, word, unigramProbability); + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); + } + words.add(word); + unigramProbabilities.put(word, unigramProbability); + } + + for (int i = 0; i < BIGRAM_COUNT; i++) { + final int word0Index = random.nextInt(words.size()); + final int word1Index = random.nextInt(words.size()); + if (word0Index == word1Index) { + continue; + } + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int unigramProbability = unigramProbabilities.get(word1); + final int bigramProbability = + random.nextInt(0xFF - unigramProbability) + unigramProbability; + addBigramWords(binaryDictionary, word0, word1, bigramProbability); + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); + } + final Pair<String, String> bigram = new Pair<>(word0, word1); + bigrams.add(bigram); + bigramProbabilities.put(bigram, bigramProbability); + } + assertTrue(binaryDictionary.migrateTo(toFormatVersion)); + + for (final String word : words) { + assertEquals((int)unigramProbabilities.get(word), binaryDictionary.getFrequency(word)); + } + assertEquals(unigramProbabilities.size(), Integer.parseInt( + binaryDictionary.getPropertyForTest(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)); + } + assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second)); + } + assertEquals(bigramProbabilities.size(), Integer.parseInt( + binaryDictionary.getPropertyForTest(BinaryDictionary.BIGRAM_COUNT_QUERY))); + } + + public void testBeginningOfSentence() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + if (supportsBeginningOfSentence(formatVersion)) { + testBeginningOfSentence(formatVersion); + } + } + } + + private void testBeginningOfSentence(final int formatVersion) { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + final int dummyProbability = 0; + final PrevWordsInfo prevWordsInfoBeginningOfSentence = PrevWordsInfo.BEGINNING_OF_SENTENCE; + final int bigramProbability = 200; + addUnigramWord(binaryDictionary, "aaa", dummyProbability); + binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + assertEquals(bigramProbability, + binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa")); + binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "aaa", bigramProbability, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + addUnigramWord(binaryDictionary, "bbb", dummyProbability); + binaryDictionary.addNgramEntry(prevWordsInfoBeginningOfSentence, "bbb", bigramProbability, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + binaryDictionary.flushWithGC(); + assertEquals(bigramProbability, + binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "aaa")); + assertEquals(bigramProbability, + binaryDictionary.getNgramProbability(prevWordsInfoBeginningOfSentence, "bbb")); + } + + public void testGetMaxFrequencyOfExactMatches() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testGetMaxFrequencyOfExactMatches(formatVersion); + } + } + + private void testGetMaxFrequencyOfExactMatches(final int formatVersion) { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + addUnigramWord(binaryDictionary, "abc", 10); + addUnigramWord(binaryDictionary, "aBc", 15); + assertEquals(15, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + addUnigramWord(binaryDictionary, "ab'c", 20); + assertEquals(20, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + addUnigramWord(binaryDictionary, "a-b-c", 25); + assertEquals(25, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + addUnigramWord(binaryDictionary, "ab-'-'-'-c", 30); + assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + addUnigramWord(binaryDictionary, "ab c", 255); + assertEquals(30, binaryDictionary.getMaxFrequencyOfExactMatches("abc")); + } } diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java new file mode 100644 index 000000000..70b8f530a --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java @@ -0,0 +1,151 @@ +/* + * 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; + +import java.util.Locale; + +import android.test.suitebuilder.annotation.LargeTest; + +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches; + +/** + * Unit test for DistracterFilter + */ +@LargeTest +public class DistracterFilterTest extends InputTestsBase { + private DistracterFilterCheckingExactMatches mDistracterFilter; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mDistracterFilter = new DistracterFilterCheckingExactMatches(getContext()); + mDistracterFilter.updateEnabledSubtypes(mLatinIME.getEnabledSubtypesForTest()); + } + + public void testIsDistractorToWordsInDictionaries() { + final PrevWordsInfo EMPTY_PREV_WORDS_INFO = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; + + final Locale localeEnUs = new Locale("en", "US"); + String typedWord; + + typedWord = "Bill"; + // For this test case, we consider "Bill" is a distracter to "bill". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "nOt"; + // For this test case, we consider "nOt" is a distracter to "not". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "youre"; + // For this test case, we consider "youre" is a distracter to "you're". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "Banana"; + // For this test case, we consider "Banana" is a distracter to "banana". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "orange"; + // For this test case, we consider "orange" is not a distracter to any word in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "Orange"; + // For this test case, we consider "Orange" is a distracter to "orange". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "café"; + // For this test case, we consider "café" is a distracter to "cafe". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "cafe"; + // For this test case, we consider "cafe" is not a distracter to any word in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "I'll"; + // For this test case, we consider "I'll" is not a distracter to any word in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "ill"; + // For this test case, we consider "ill" is a distracter to "I'll" + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "asdfd"; + // For this test case, we consider "asdfd" is not a distracter to any word in dictionaries. + assertFalse( + mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "thank"; + // For this test case, we consider "thank" is not a distracter to any other word + // in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + final Locale localeDeDe = new Locale("de", "DE"); + + typedWord = "fuer"; + // For this test case, we consider "fuer" is a distracter to "für". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + + typedWord = "fUEr"; + // For this test case, we consider "fUEr" is a distracter to "für". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + + typedWord = "fur"; + // For this test case, we consider "fur" is a distracter to "für". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + + final Locale localeFrFr = new Locale("fr", "FR"); + + typedWord = "a"; + // For this test case, we consider "a" is a distracter to "à". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); + + typedWord = "à"; + // For this test case, we consider "à" is not a distracter to any word in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); + + typedWord = "etre"; + // For this test case, we consider "etre" is a distracter to "être". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); + + typedWord = "États-unis"; + // For this test case, we consider "États-unis" is a distracter to "États-Unis". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); + + typedWord = "ÉtatsUnis"; + // For this test case, we consider "ÉtatsUnis" is a distracter to "États-Unis". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeFrFr)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index d2dd29262..460f600ac 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -334,6 +334,18 @@ public class InputLogicTests extends InputTestsBase { assertEquals("manual pick then separator", EXPECTED_RESULT, mEditText.getText().toString()); } + // This test matches the one in InputLogicTestsNonEnglish. In some non-English languages, + // ! and ? are clustering punctuation signs. + public void testClusteringPunctuation() { + final String WORD1_TO_TYPE = "test"; + final String WORD2_TO_TYPE = "!!?!:!"; + final String EXPECTED_RESULT = "test!!?!:!"; + type(WORD1_TO_TYPE); + pickSuggestionManually(0, WORD1_TO_TYPE); + type(WORD2_TO_TYPE); + assertEquals("clustering punctuation", EXPECTED_RESULT, mEditText.getText().toString()); + } + public void testManualPickThenStripperThenPick() { final String WORD_TO_TYPE = "this"; final String STRIPPER = "\n"; @@ -469,6 +481,27 @@ public class InputLogicTests extends InputTestsBase { suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); } + public void testPredictionsWithDoubleSpaceToPeriod() { + final String WORD_TO_TYPE = "Barack "; + type(WORD_TO_TYPE); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + runMessages(); + // No need to test here, testPredictionsAfterSpace is testing it already + type(" "); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + runMessages(); + // Test the predictions have been cleared + SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); + assertEquals("predictions cleared after double-space-to-period", suggestedWords.size(), 0); + type(Constants.CODE_DELETE); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + runMessages(); + // Test the first prediction is displayed + suggestedWords = mLatinIME.getSuggestedWordsForTest(); + assertEquals("predictions after cancel double-space-to-period", "Obama", + suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null); + } + public void testPredictionsAfterManualPick() { final String WORD_TO_TYPE = "Barack"; type(WORD_TO_TYPE); @@ -588,4 +621,16 @@ public class InputLogicTests extends InputTestsBase { assertEquals("type words letter by letter", EXPECTED_RESULT, mEditText.getText().toString()); } + + public void testSwitchLanguages() { + final String WORD_TO_TYPE_FIRST_PART = "com"; + final String WORD_TO_TYPE_SECOND_PART = "md "; + final String EXPECTED_RESULT = "comme "; + changeLanguage("en"); + type(WORD_TO_TYPE_FIRST_PART); + changeLanguage("fr"); + type(WORD_TO_TYPE_SECOND_PART); + assertEquals("Composing continues after switching languages", EXPECTED_RESULT, + mEditText.getText().toString()); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java index e38ba721e..2560407dc 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -19,8 +19,6 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; import android.view.inputmethod.BaseInputConnection; -import com.android.inputmethod.latin.suggestions.SuggestionStripView; - @LargeTest public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { public void testAutoCorrectForLanguageWithoutSpaces() { diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java index 1257ae297..914b0bb23 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java @@ -18,8 +18,6 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; -import com.android.inputmethod.latin.suggestions.SuggestionStripView; - @LargeTest public class InputLogicTestsNonEnglish extends InputTestsBase { final String NEXT_WORD_PREDICTION_OPTION = "next_word_prediction"; @@ -45,6 +43,19 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { mEditText.getText().toString()); } + public void testClusteringPunctuationForFrench() { + final String WORD1_TO_TYPE = "test"; + final String WORD2_TO_TYPE = "!!?!:!"; + // In English, the expected result would be "test!!?!:!" + final String EXPECTED_RESULT = "test !!?! : !"; + changeLanguage("fr"); + type(WORD1_TO_TYPE); + pickSuggestionManually(0, WORD1_TO_TYPE); + type(WORD2_TO_TYPE); + assertEquals("clustering punctuation for French", EXPECTED_RESULT, + mEditText.getText().toString()); + } + public void testWordThenSpaceThenPunctuationFromStripTwiceForFrench() { final String WORD_TO_TYPE = "test "; final String PUNCTUATION_FROM_STRIP = "!"; diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java new file mode 100644 index 000000000..61eae4e8b --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Pair; + +/* + * Relevant characters for this test : + * Spurs the need to reorder : + * U+1031 MYANMAR VOWEL SIGN E : ေ + * U+1004 U+103A U+1039 Kinzi. It's a compound character. + * + * List of consonants : + * U+1000 MYANMAR LETTER KA က + * U+1001 MYANMAR LETTER KHA ခ + * U+1002 MYANMAR LETTER GA ဂ + * U+1003 MYANMAR LETTER GHA ဃ + * U+1004 MYANMAR LETTER NGA င + * U+1005 MYANMAR LETTER CA စ + * U+1006 MYANMAR LETTER CHA ဆ + * U+1007 MYANMAR LETTER JA ဇ + * U+1008 MYANMAR LETTER JHA ဈ + * U+1009 MYANMAR LETTER NYA ဉ + * U+100A MYANMAR LETTER NNYA ည + * U+100B MYANMAR LETTER TTA ဋ + * U+100C MYANMAR LETTER TTHA ဌ + * U+100D MYANMAR LETTER DDA ဍ + * U+100E MYANMAR LETTER DDHA ဎ + * U+100F MYANMAR LETTER NNA ဏ + * U+1010 MYANMAR LETTER TA တ + * U+1011 MYANMAR LETTER THA ထ + * U+1012 MYANMAR LETTER DA ဒ + * U+1013 MYANMAR LETTER DHA ဓ + * U+1014 MYANMAR LETTER NA န + * U+1015 MYANMAR LETTER PA ပ + * U+1016 MYANMAR LETTER PHA ဖ + * U+1017 MYANMAR LETTER BA ဗ + * U+1018 MYANMAR LETTER BHA ဘ + * U+1019 MYANMAR LETTER MA မ + * U+101A MYANMAR LETTER YA ယ + * U+101B MYANMAR LETTER RA ရ + * U+101C MYANMAR LETTER LA လ + * U+101D MYANMAR LETTER WA ဝ + * U+101E MYANMAR LETTER SA သ + * U+101F MYANMAR LETTER HA ဟ + * U+1020 MYANMAR LETTER LLA ဠ + * U+103F MYANMAR LETTER GREAT SA ဿ + * + * List of medials : + * U+103B MYANMAR CONSONANT SIGN MEDIAL YA ျ + * U+103C MYANMAR CONSONANT SIGN MEDIAL RA ြ + * U+103D MYANMAR CONSONANT SIGN MEDIAL WA ွ + * U+103E MYANMAR CONSONANT SIGN MEDIAL HA ှ + * U+105E MYANMAR CONSONANT SIGN MON MEDIAL NA ၞ + * U+105F MYANMAR CONSONANT SIGN MON MEDIAL MA ၟ + * U+1060 MYANMAR CONSONANT SIGN MON MEDIAL LA ၠ + * U+1082 MYANMAR CONSONANT SIGN SHAN MEDIAL WA ႂ + * + * Other relevant characters : + * U+200C ZERO WIDTH NON-JOINER + * U+200B ZERO WIDTH SPACE + */ + +@LargeTest +@SuppressWarnings("rawtypes") +public class InputLogicTestsReorderingMyanmar extends InputTestsBase { + // The tests are formatted as follows. + // Each test is an entry in the array of Pair arrays. + + // One test is an array of pairs. Each pair contains, in the `first' member, + // the code points that the next key press should contain. In the `second' + // member is stored the string that should be in the text view after this + // key press. + + private static final Pair[][] TESTS = { + + // Tests for U+1031 MYANMAR VOWEL SIGN E : ေ + new Pair[] { // Type : U+1031 U+1000 U+101F ေ က ဟ + Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ + Pair.create(new int[] { 0x1000 }, "\u1000\u1031"), // ကေ + Pair.create(new int[] { 0x101F }, "\u1000\u1031\u101F") // ကေဟ + }, + + new Pair[] { // Type : U+1000 U+1031 U+101F က ေ ဟ + Pair.create(new int[] { 0x1000 }, "\u1000"), // က + Pair.create(new int[] { 0x1031 }, "\u1000\u200B\u1031"), // ကေ + Pair.create(new int[] { 0x101F }, "\u1000\u101F\u1031") // ကဟေ + }, + + new Pair[] { // Type : U+1031 U+101D U+103E U+1018 ေ ဝ ှ ဘ + Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ + Pair.create(new int[] { 0x101D }, "\u101D\u1031"), // ဝေ + Pair.create(new int[] { 0x103E }, "\u101D\u103E\u1031"), // ဝှေ + Pair.create(new int[] { 0x1018 }, "\u101D\u103E\u1031\u1018") // ဝှေဘ + }, + + new Pair[] { // Type : U+1031 U+1014 U+1031 U+1000 U+102C U+1004 U+103A U+1038 U+101C + // U+102C U+1038 U+104B ေ န ေ က ာ င ် း လ ာ း ။ + Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ + Pair.create(new int[] { 0x1014 }, "\u1014\u1031"), // နေ + Pair.create(new int[] { 0x1031 }, "\u1014\u1031\u1031"), // နေေ + Pair.create(new int[] { 0x1000 }, "\u1014\u1031\u1000\u1031"), // နေကေ + Pair.create(new int[] { 0x102C }, "\u1014\u1031\u1000\u1031\u102C"), // နေကော + Pair.create(new int[] { 0x1004 }, "\u1014\u1031\u1000\u1031\u102C\u1004"), // နေကောင + Pair.create(new int[] { 0x103A }, // နေကောင် + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A"), + Pair.create(new int[] { 0x1038 }, // နေကောင်း + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038"), + Pair.create(new int[] { 0x101C }, // နေကောင်းလ + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C"), + Pair.create(new int[] { 0x102C }, // နေကောင်းလာ + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C"), + Pair.create(new int[] { 0x1038 }, // နေကောင်းလား + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038"), + Pair.create(new int[] { 0x104B }, // နေကောင်းလား။ + "\u1014\u1031\u1000\u1031\u102C\u1004\u103A\u1038\u101C\u102C\u1038\u104B") + }, + + new Pair[] { // Type : U+1031 U+1031 U+1031 U+1000 ေ ေ ေ က + Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ + Pair.create(new int[] { 0x1031 }, "\u1031\u1031"), // ေေ + Pair.create(new int[] { 0x1031 }, "\u1031\u1031\u1031"), // U+1031ေေေ + Pair.create(new int[] { 0x1000 }, "\u1031\u1031\u1000\u1031") // ေေကေ + }, + + new Pair[] { // Type : U+1031 U+1001 U+103B U+103D U+1038 ေ ခ ျ ွ း + Pair.create(new int[] { 0x1031 }, "\u1031"), // ေ + Pair.create(new int[] { 0x1001 }, "\u1001\u1031"), // ခေ + Pair.create(new int[] { 0x103B }, "\u1001\u103B\u1031"), // ချေ + Pair.create(new int[] { 0x103D }, "\u1001\u103B\u103D\u1031"), // ချွေ + Pair.create(new int[] { 0x1038 }, "\u1001\u103B\u103D\u1031\u1038") // ချွေး + }, + + // Tests for Kinzi U+1004 U+103A U+1039 : + + /* Kinzi reordering is not implemented yet. Uncomment these tests when it is. + + new Pair[] { // Type : U+1021 U+1002 (U+1004 U+103A U+1039) + // U+101C U+1014 U+103A အ ဂ (င ် ္) လ န ် + Pair.create(new int[] { 0x1021 }, "\u1021"), // အ + Pair.create(new int[] { 0x1002 }, "\u1021\u1002"), // အဂ + Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // အင်္ဂ + "\u1021\u1004\u103A\u1039\u1002"), + Pair.create(new int[] { 0x101C }, // အင်္ဂလ + "\u1021\u1004\u103A\u1039\u1002\u101C"), + Pair.create(new int[] { 0x1014 }, // အင်္ဂလန + "\u1021\u1004\u103A\u1039\u1002\u101C\u1014"), + Pair.create(new int[] { 0x103A }, // အင်္ဂလန် + "\u1021\u1004\u103A\u1039\u1002\u101C\u1014\u103A") + }, + + new Pair[] { //Type : kinzi after a whole syllable U+101E U+1001 U+103B U+102D U+102F + // (U+1004 U+103A U+1039) U+1004 U+103A U+1038 သ ခ ျ ိ ု င ် ္ င ် း + Pair.create(new int[] { 0x101E }, "\u101E"), // သခ + Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ + Pair.create(new int[] { 0x103B }, "\u101E\u1001\u103B"), // သချ + Pair.create(new int[] { 0x102D }, "\u101E\u1001\u103B\u102D"), // သချိ + Pair.create(new int[] { 0x102F }, "\u101E\u1001\u103B\u102D\u102F"), // သချို + Pair.create(new int[] { 0x1004, 0x103A, 0x1039}, // သင်္ချို + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"), + Pair.create(new int[] { 0x1004 }, // သင်္ချိုင + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"), + Pair.create(new int[] { 0x103A }, // သင်္ချိုင် + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"), + Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038") + }, + + new Pair[] { // Type : kinzi after the consonant U+101E U+1001 (U+1004 U+103A U+1039) + // U+103B U+102D U+102F U+1004 U+103A U+1038 သ ခ င ် ္ ျ ိ ု င ် း + Pair.create(new int[] { 0x101E }, "\u101E"), // သခ + Pair.create(new int[] { 0x1001 }, "\u101E\u1001"), // သခ + Pair.create(new int[] { 0x1004, 0x103A, 0x1039 }, // သင်္ခ + "\u101E\u1004\u103A\u1039\u1001"), + Pair.create(new int[] { 0x103B }, // သင်္ချ + "\u101E\u1004\u103A\u1039\u1001\u103B"), + Pair.create(new int[] { 0x102D }, // သင်္ချိ + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D"), + Pair.create(new int[] { 0x102F }, // သင်္ချို + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F"), + Pair.create(new int[] { 0x1004 }, // သင်္ချိုင + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004"), + Pair.create(new int[] { 0x103A }, // သင်္ချိုင် + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A"), + Pair.create(new int[] { 0x1038 }, // သင်္ချိုင်း + "\u101E\u1004\u103A\u1039\u1001\u103B\u102D\u102F\u1004\u103A\u1038") + }, + */ + }; + + @SuppressWarnings("unchecked") + private void doMyanmarTest(final int testNumber, final Pair[] test) { + int stepNumber = 0; + for (final Pair<int[], String> step : test) { + ++stepNumber; + final int[] input = step.first; + final String expectedResult = step.second; + if (input.length > 1) { + mLatinIME.onTextInput(new String(input, 0, input.length)); + } else { + type(input[0]); + } + assertEquals("Myanmar reordering test " + testNumber + ", step " + stepNumber, + expectedResult, mEditText.getText().toString()); + } + } + + public void testMyanmarReordering() { + int testNumber = 0; + changeLanguage("my_MM", "CombiningRules=MyanmarReordering"); + for (final Pair[] test : TESTS) { + // Small trick to reset LatinIME : setText("") and send updateSelection with values + // LatinIME has never seen, and cursor pos 0,0. + mEditText.setText(""); + mLatinIME.onUpdateSelection(1, 1, 0, 0, -1, -1); + doMyanmarTest(++testNumber, test); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index 260e534ee..8406c9099 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -162,9 +162,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { return previousSetting; } - // returns the previous setting value - protected boolean setDebugMode(final boolean value) { - return setBooleanPreference(DebugSettings.PREF_DEBUG_MODE, value, false); + protected void setDebugMode(final boolean value) { + setBooleanPreference(DebugSettings.PREF_DEBUG_MODE, value, false); + setBooleanPreference(Settings.PREF_KEY_IS_INTERNAL, value, false); } protected EditorInfo enrichEditorInfo(final EditorInfo ei) { @@ -299,11 +299,15 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } protected void changeLanguage(final String locale) { - changeLanguageWithoutWait(locale); + changeLanguage(locale, null); + } + + protected void changeLanguage(final String locale, final String combiningSpec) { + changeLanguageWithoutWait(locale, combiningSpec); waitForDictionariesToBeLoaded(); } - protected void changeLanguageWithoutWait(final String locale) { + protected void changeLanguageWithoutWait(final String locale, final String combiningSpec) { mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale); // TODO: this is forcing a QWERTY keyboard for all locales, which is wrong. // It's still better than using whatever keyboard is the current one, but we @@ -314,7 +318,8 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE - + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE + + null == combiningSpec ? "" : ("," + combiningSpec); final InputMethodSubtype subtype = InputMethodSubtypeCompatUtils.newInputMethodSubtype( R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, @@ -325,7 +330,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { false /* overridesImplicitlyEnabledSubtype */, 0 /* id */); SubtypeSwitcher.getInstance().forceSubtype(subtype); - mLatinIME.loadKeyboard(); + mLatinIME.onCurrentInputMethodSubtypeChanged(subtype); runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); mLatinIME.clearPersonalizedDictionariesForTest(); diff --git a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java index db14b8329..f5e993de8 100644 --- a/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java +++ b/tests/src/com/android/inputmethod/latin/LatinImeStressTests.java @@ -30,10 +30,10 @@ public class LatinImeStressTests extends InputTestsBase { final int maxWordCountToTypeInEachIteration = 20; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); - final int codePointSetSize = 30; final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER; for (int i = 0; i < switchCount; ++i) { - changeLanguageWithoutWait(locales[random.nextInt(locales.length)]); + changeLanguageWithoutWait(locales[random.nextInt(locales.length)], + null /* combiningSpec */); final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration); for (int j = 0; j < wordCount; ++j) { final String word = CodePointUtils.generateWord(random, codePointSet); @@ -50,7 +50,8 @@ public class LatinImeStressTests extends InputTestsBase { final int codePointSetSize = 30; final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); for (int i = 0; i < switchCount; ++i) { - changeLanguageWithoutWait(locales[random.nextInt(locales.length)]); + changeLanguageWithoutWait(locales[random.nextInt(locales.length)], + null /* combiningSpec */); final int wordCount = random.nextInt(maxWordCountToTypeInEachIteration); for (int j = 0; j < wordCount; ++j) { final String word = CodePointUtils.generateWord(random, codePointSet); diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index 842f3f3a9..2c92bb3d6 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -155,13 +155,17 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { */ public void testGetPreviousWord() { // If one of the following cases breaks, the bigram suggestions won't work. - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def", mSpacingAndPunctuations, 2), "abc"); - assertNull(RichInputConnection.getNthPreviousWord( - "abc", mSpacingAndPunctuations, 2)); - assertNull(RichInputConnection.getNthPreviousWord( - "abc. def", mSpacingAndPunctuations, 2)); - + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 2).mPrevWord, "abc"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); + + assertFalse(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 2).mIsBeginningOfSentence); + assertTrue(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc", mSpacingAndPunctuations, 2).mIsBeginningOfSentence); // The following tests reflect the current behavior of the function // RichInputConnection#getNthPreviousWord. // TODO: However at this time, the code does never go @@ -169,23 +173,33 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // this function if needed - especially since it does not seem very // logical. These tests are just there to catch any unintentional // changes in the behavior of the RichInputConnection#getPreviousWord method. - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def ", mSpacingAndPunctuations, 2), "abc"); - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def.", mSpacingAndPunctuations, 2), "abc"); - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def .", mSpacingAndPunctuations, 2), "def"); - assertNull(RichInputConnection.getNthPreviousWord( - "abc ", mSpacingAndPunctuations, 2)); - - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def", mSpacingAndPunctuations, 1), "def"); - assertEquals(RichInputConnection.getNthPreviousWord( - "abc def ", mSpacingAndPunctuations, 1), "def"); - assertNull(RichInputConnection.getNthPreviousWord( - "abc def.", mSpacingAndPunctuations, 1)); - assertNull(RichInputConnection.getNthPreviousWord( - "abc def .", mSpacingAndPunctuations, 1)); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def ", mSpacingAndPunctuations, 2).mPrevWord, "abc"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def.", mSpacingAndPunctuations, 2).mPrevWord, "abc"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def .", mSpacingAndPunctuations, 2).mPrevWord, "def"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE); + + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def", mSpacingAndPunctuations, 1).mPrevWord, "def"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def ", mSpacingAndPunctuations, 1).mPrevWord, "def"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc 'def", mSpacingAndPunctuations, 1).mPrevWord, "'def"); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc def .", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc, def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc? def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc! def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); + assertEquals(RichInputConnection.getPrevWordsInfoFromNthPreviousWord( + "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); } /** diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java index 6fc9df793..a319ffda6 100644 --- a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java +++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.os.Build; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; @@ -78,4 +79,56 @@ public class ShiftModeTests extends InputTestsBase { runMessages(); assertTrue("Caps after a while after repeating Backspace a lot", isCapsModeAutoShifted()); } + + public void testAutoCapsAfterDigitsPeriod() { + changeLanguage("en"); + type("On 22.11."); + assertFalse("(English) Auto caps after digits-period", isCapsModeAutoShifted()); + type(" "); + assertTrue("(English) Auto caps after digits-period-whitespace", isCapsModeAutoShifted()); + mEditText.setText(""); + changeLanguage("fr"); + type("Le 22."); + assertFalse("(French) Auto caps after digits-period", isCapsModeAutoShifted()); + type(" "); + assertTrue("(French) Auto caps after digits-period-whitespace", isCapsModeAutoShifted()); + mEditText.setText(""); + changeLanguage("de"); + type("Am 22."); + assertFalse("(German) Auto caps after digits-period", isCapsModeAutoShifted()); + type(" "); + // For German, no auto-caps in this case + assertFalse("(German) Auto caps after digits-period-whitespace", isCapsModeAutoShifted()); + } + + public void testAutoCapsAfterInvertedMarks() { + changeLanguage("es"); + assertTrue("(Spanish) Auto caps at start", isCapsModeAutoShifted()); + type("Hey. ¿"); + assertTrue("(Spanish) Auto caps after inverted what", isCapsModeAutoShifted()); + mEditText.setText(""); + type("¡"); + assertTrue("(Spanish) Auto caps after inverted bang", isCapsModeAutoShifted()); + } + + public void DISABLED_testOtherSentenceSeparators() { + // We only run this test on Kitkat+ because previous versions of Android don't + // have an Armenian locale. For some reason I don't know, when the requested + // locale is not present as a device locale, then the application under test can't + // access the resources in that locale -- though it works when the app is actually + // running on the device and not under test. If we ever figure out what's going + // on, remove this test. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + changeLanguage("hy-AM"); + assertTrue("(Armenian) Auto caps at start", isCapsModeAutoShifted()); + type("Hey. "); + assertFalse("(Armenian) No auto-caps after latin period", isCapsModeAutoShifted()); + type("Hey\u0589"); + assertFalse("(Armenian) No auto-caps directly after armenian period", + isCapsModeAutoShifted()); + type(" "); + assertTrue("(Armenian) Auto-caps after armenian period-whitespace", + isCapsModeAutoShifted()); + } + } } diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 8fe473523..66b4a9c71 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -16,12 +16,10 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; - import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.Locale; @@ -33,7 +31,7 @@ public class SuggestedWordsTests extends AndroidTestCase { final String TYPED_WORD = "typed"; final int TYPED_WORD_FREQ = 5; final int NUMBER_OF_ADDED_SUGGESTIONS = 5; - final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList(); + final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ, SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, @@ -53,16 +51,16 @@ public class SuggestedWordsTests extends AndroidTestCase { false /* isPrediction*/); assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size()); assertEquals("typed", words.getWord(0)); - assertEquals(SuggestedWordInfo.KIND_TYPED, words.getInfo(0).mKind); + assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED)); assertEquals("0", words.getWord(1)); - assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(1).mKind); + assertTrue(words.getInfo(1).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); assertEquals("4", words.getWord(5)); - assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(5).mKind); + assertTrue(words.getInfo(5).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord(); assertEquals(words.size() - 1, wordsWithoutTyped.size()); assertEquals("0", wordsWithoutTyped.getWord(0)); - assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind); + assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); } // Helper for testGetTransformedWordInfo diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java index d68bb5c54..c44544f3d 100644 --- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -40,8 +40,7 @@ public class WordComposerTests extends AndroidTestCase { final int[] COORDINATES_WITHIN_BMP = CoordinateUtils.newCoordinateArray(CODEPOINTS_WITHIN_BMP.length, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - final String PREVWORD = "prevword"; - wc.setComposingWord(CODEPOINTS_WITHIN_BMP, COORDINATES_WITHIN_BMP, PREVWORD); + wc.setComposingWord(CODEPOINTS_WITHIN_BMP, COORDINATES_WITHIN_BMP); assertEquals(wc.size(), STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length())); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); wc.setCursorPositionWithinWord(2); @@ -56,15 +55,12 @@ public class WordComposerTests extends AndroidTestCase { // Move the cursor to after the 'f' assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); - // Check the previous word is still there - assertEquals(PREVWORD, wc.getPreviousWordForSuggestion()); // Move the cursor past the end of the word assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15)); // Do what LatinIME does when the cursor is moved outside of the word, // and check the behavior is correct. wc.reset(); - assertNull(wc.getPreviousWordForSuggestion()); // \uD861\uDED7 is 𨛗, a character outside the BMP final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh"; @@ -73,8 +69,8 @@ public class WordComposerTests extends AndroidTestCase { final int[] COORDINATES_WITH_SUPPLEMENTARY_CHAR = CoordinateUtils.newCoordinateArray(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - null /* previousWord */); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); assertEquals(wc.size(), CODEPOINTS_WITH_SUPPLEMENTARY_CHAR.length); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); wc.setCursorPositionWithinWord(3); @@ -83,48 +79,43 @@ public class WordComposerTests extends AndroidTestCase { assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); - assertNull(wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - STR_WITHIN_BMP); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); - assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - STR_WITH_SUPPLEMENTARY_CHAR); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); - assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - STR_WITHIN_BMP); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1)); - assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - null /* previousWord */); + + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); wc.setCursorPositionWithinWord(3); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9)); - assertNull(wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - STR_WITH_SUPPLEMENTARY_CHAR); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10)); - assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion()); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - null /* previousWord */); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11)); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - null /* previousWord */); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); - wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, COORDINATES_WITH_SUPPLEMENTARY_CHAR, - null /* previousWord */); + wc.setComposingWord(CODEPOINTS_WITH_SUPPLEMENTARY_CHAR, + COORDINATES_WITH_SUPPLEMENTARY_CHAR); wc.setCursorPositionWithinWord(2); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index f29fc21c1..4b332ca84 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -28,10 +28,8 @@ import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; -import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; import java.io.IOException; @@ -61,15 +59,12 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private static final int NUM_OF_NODES_HAVING_SHORTCUTS = 50; private static final int NUM_OF_SHORTCUTS = 5; - private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); - private static final ArrayList<String> sWordsWithVariousCodePoints = - CollectionUtils.newArrayList(); - private static final SparseArray<List<Integer>> sEmptyBigrams = - CollectionUtils.newSparseArray(); - private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray(); - private static final SparseArray<List<Integer>> sChainBigrams = - CollectionUtils.newSparseArray(); - private static final HashMap<String, List<String>> sShortcuts = CollectionUtils.newHashMap(); + private static final ArrayList<String> sWords = new ArrayList<>(); + private static final ArrayList<String> sWordsWithVariousCodePoints = new ArrayList<>(); + private static final SparseArray<List<Integer>> sEmptyBigrams = new SparseArray<>(); + private static final SparseArray<List<Integer>> sStarBigrams = new SparseArray<>(); + private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>(); + private static final HashMap<String, List<String>> sShortcuts = new HashMap<>(); public BinaryDictDecoderEncoderTests() { this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); @@ -125,7 +120,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private void generateWords(final int number, final Random random) { final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random); - final Set<String> wordSet = CollectionUtils.newHashSet(); + final Set<String> wordSet = new HashSet<>(); while (wordSet.size() < number) { wordSet.add(CodePointUtils.generateWord(random, codePointSet)); } @@ -147,7 +142,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final List<String> words, final HashMap<String, List<String>> shortcutMap) { for (int i = 0; i < number; ++i) { final String word = words.get(i); - final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList(); + final ArrayList<WeightedString> shortcuts = new ArrayList<>(); if (shortcutMap != null && shortcutMap.containsKey(word)) { for (final String shortcut : shortcutMap.get(word)) { shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ)); @@ -325,7 +320,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testReadAndWriteWithByteBuffer() { - final List<String> results = CollectionUtils.newArrayList(); + final List<String> results = new ArrayList<>(); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, BinaryDictUtils.VERSION2_OPTIONS); @@ -339,7 +334,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testReadAndWriteWithByteArray() { - final List<String> results = CollectionUtils.newArrayList(); + final List<String> results = new ArrayList<>(); runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, BinaryDictUtils.VERSION2_OPTIONS); @@ -362,8 +357,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams, final boolean checkProbability) { // check unigrams - final Set<String> actualWordsSet = new HashSet<String>(resultWords.values()); - final Set<String> expectedWordsSet = new HashSet<String>(expectedWords); + final Set<String> actualWordsSet = new HashSet<>(resultWords.values()); + final Set<String> expectedWordsSet = new HashSet<>(expectedWords); assertEquals(actualWordsSet, expectedWordsSet); if (checkProbability) { for (int freq : resultFrequencies.values()) { @@ -372,7 +367,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } // check bigrams - final HashMap<String, Set<String>> expBigrams = new HashMap<String, Set<String>>(); + final HashMap<String, Set<String>> expBigrams = new HashMap<>(); for (int i = 0; i < expectedBigrams.size(); ++i) { final String word1 = expectedWords.get(expectedBigrams.keyAt(i)); for (int w2 : expectedBigrams.valueAt(i)) { @@ -383,7 +378,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } } - final HashMap<String, Set<String>> actBigrams = new HashMap<String, Set<String>>(); + final HashMap<String, Set<String>> actBigrams = new HashMap<>(); for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) { final String word1 = resultWords.get(entry.getKey()); final int unigramFreq = resultFrequencies.get(entry.getKey()); @@ -407,10 +402,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, final SparseArray<List<Integer>> bigrams, final int bufferType, final boolean checkProbability) { - final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap(); - final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = - CollectionUtils.newTreeMap(); - final TreeMap<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap(); + final TreeMap<Integer, String> resultWords = new TreeMap<>(); + final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams = new TreeMap<>(); + final TreeMap<Integer, Integer> resultFreqs = new TreeMap<>(); long now = -1, diff = -1; try { @@ -468,7 +462,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testReadUnigramsAndBigramsBinaryWithByteBuffer() { - final ArrayList<String> results = CollectionUtils.newArrayList(); + final ArrayList<String> results = new ArrayList<>(); runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, BinaryDictUtils.VERSION2_OPTIONS); @@ -479,7 +473,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testReadUnigramsAndBigramsBinaryWithByteArray() { - final ArrayList<String> results = CollectionUtils.newArrayList(); + final ArrayList<String> results = new ArrayList<>(); runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, BinaryDictUtils.VERSION2_OPTIONS); @@ -590,7 +584,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } public void testGetTerminalPosition() { - final ArrayList<String> results = CollectionUtils.newArrayList(); + final ArrayList<String> results = new ArrayList<>(); runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, BinaryDictUtils.VERSION2_OPTIONS); @@ -656,15 +650,15 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { 0 /* offset */, file.length(), true /* useFullEditDistance */, Locale.ENGLISH, dictName, false /* isUpdatable */); - final HashSet<String> wordSet = new HashSet<String>(words); - final HashSet<Pair<String, String>> bigramSet = new HashSet<Pair<String,String>>(); + final HashSet<String> wordSet = new HashSet<>(words); + final HashSet<Pair<String, String>> bigramSet = new HashSet<>(); for (int i = 0; i < words.size(); i++) { final List<Integer> bigramList = bigrams.get(i); if (bigramList != null) { for (final Integer word1Index : bigramList) { final String word1 = words.get(word1Index); - bigramSet.add(new Pair<String, String>(words.get(i), word1)); + bigramSet.add(new Pair<>(words.get(i), word1)); } } } @@ -689,7 +683,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } for (int j = 0; j < wordProperty.mBigrams.size(); j++) { final String word1 = wordProperty.mBigrams.get(j).mWord; - final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + final Pair<String, String> bigram = new Pair<>(word0, word1); assertTrue(bigramSet.contains(bigram)); bigramSet.remove(bigram); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 6f8b07a34..96604a197 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -32,9 +32,6 @@ import java.nio.ByteBuffer; * TODO: Rename this class to DictDecoderUtils. */ public final class BinaryDictDecoderUtils { - - private static final boolean DBG = MakedictLog.DBG; - private BinaryDictDecoderUtils() { // This utility class is not publicly instantiable. } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index 39bd98bad..084371944 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -258,7 +258,7 @@ public class BinaryDictEncoderUtils { final PtNodeArray rootNodeArray) { final int treeSize = FusionDictionary.countPtNodes(rootNodeArray); MakedictLog.i("Counted nodes : " + treeSize); - final ArrayList<PtNodeArray> flatTree = new ArrayList<PtNodeArray>(treeSize); + final ArrayList<PtNodeArray> flatTree = new ArrayList<>(treeSize); return flattenTreeInner(flatTree, rootNodeArray); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 42a50be66..9c3b37387 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -90,7 +90,7 @@ public final class BinaryDictIOUtils { final Map<Integer, ArrayList<PendingAttribute>> bigrams) { int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1]; - Stack<Position> stack = new Stack<Position>(); + Stack<Position> stack = new Stack<>(); int index = 0; Position initPos = new Position(bodyOffset, 0); diff --git a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index f60b3af4f..4a8c178b5 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/tests/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -57,7 +57,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { int mCachedParentAddress = 0; public PtNodeArray() { - mData = new ArrayList<PtNode>(); + mData = new ArrayList<>(); } public PtNodeArray(ArrayList<PtNode> data) { Collections.sort(data, PTNODE_COMPARATOR); @@ -161,14 +161,14 @@ public final class FusionDictionary implements Iterable<WordProperty> { // We don't want write permission to escape outside the package, so we return a copy if (null == mShortcutTargets) return null; final ArrayList<WeightedString> copyOfShortcutTargets = - new ArrayList<WeightedString>(mShortcutTargets); + new ArrayList<>(mShortcutTargets); return copyOfShortcutTargets; } public ArrayList<WeightedString> getBigrams() { // We don't want write permission to escape outside the package, so we return a copy if (null == mBigrams) return null; - final ArrayList<WeightedString> copyOfBigrams = new ArrayList<WeightedString>(mBigrams); + final ArrayList<WeightedString> copyOfBigrams = new ArrayList<>(mBigrams); return copyOfBigrams; } @@ -183,7 +183,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { */ public void addBigram(final String word, final ProbabilityInfo probabilityInfo) { if (mBigrams == null) { - mBigrams = new ArrayList<WeightedString>(); + mBigrams = new ArrayList<>(); } WeightedString bigram = getBigram(word); if (bigram != null) { @@ -571,7 +571,6 @@ public final class FusionDictionary implements Iterable<WordProperty> { /** * Helper method to find a word in a given branch. */ - @SuppressWarnings("unused") public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) { int index = 0; final StringBuilder checker = DBG ? new StringBuilder() : null; @@ -651,7 +650,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { public DictionaryIterator(ArrayList<PtNode> ptRoot) { mCurrentString = new StringBuilder(); - mPositions = new LinkedList<Position>(); + mPositions = new LinkedList<>(); final Position rootPos = new Position(ptRoot); mPositions.add(rootPos); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java index 7091c119e..65b84d5f7 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java @@ -20,7 +20,6 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; import java.io.FileNotFoundException; @@ -34,8 +33,6 @@ import java.util.Arrays; // TODO: Separate logics that are used only for testing. @UsedForTesting public class Ver2DictDecoder extends AbstractDictDecoder { - private static final String TAG = Ver2DictDecoder.class.getSimpleName(); - /** * A utility class for reading a PtNode. */ @@ -233,7 +230,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder { final ArrayList<WeightedString> shortcutTargets; if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) { // readShortcut will add shortcuts to shortcutTargets. - shortcutTargets = new ArrayList<WeightedString>(); + shortcutTargets = new ArrayList<>(); addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets); } else { shortcutTargets = null; @@ -241,7 +238,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder { final ArrayList<PendingAttribute> bigrams; if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { - bigrams = new ArrayList<PendingAttribute>(); + bigrams = new ArrayList<>(); addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, addressPointer); if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { @@ -267,7 +264,7 @@ public class Ver2DictDecoder extends AbstractDictDecoder { final FusionDictionary fusionDict = new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions); int token = 0; - final ArrayList<WordProperty> wordProperties = CollectionUtils.newArrayList(); + final ArrayList<WordProperty> wordProperties = new ArrayList<>(); do { final BinaryDictionary.GetNextWordPropertyResult result = binaryDictionary.getNextWordProperty(token); diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java index 9dc2b1058..3882c2c55 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoderTests.java @@ -58,7 +58,6 @@ public class Ver2DictDecoderTests extends AndroidTestCase { } } - @SuppressWarnings("null") public void runTestOpenBuffer(final String testName, final DictionaryBufferFactory factory) { File testFile = null; try { @@ -102,7 +101,6 @@ public class Ver2DictDecoderTests extends AndroidTestCase { new DictionaryBufferFromWritableByteBufferFactory()); } - @SuppressWarnings("null") public void runTestGetBuffer(final String testName, final DictionaryBufferFactory factory) { File testFile = null; try { diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index f3fad7e99..5e8417ed6 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; @@ -31,8 +30,6 @@ import java.util.ArrayList; */ @UsedForTesting public class Ver4DictDecoder extends AbstractDictDecoder { - private static final String TAG = Ver4DictDecoder.class.getSimpleName(); - final File mDictDirectory; @UsedForTesting @@ -73,7 +70,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { final FusionDictionary fusionDict = new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions); int token = 0; - final ArrayList<WordProperty> wordProperties = CollectionUtils.newArrayList(); + final ArrayList<WordProperty> wordProperties = new ArrayList<>(); do { final BinaryDictionary.GetNextWordPropertyResult result = binaryDictionary.getNextWordProperty(token); diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index dab9a4315..8f32e5336 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; @@ -74,33 +75,54 @@ public class Ver4DictEncoder implements DictEncoder { for (final WordProperty wordProperty : dict) { // TODO: switch to addMultipleDictionaryEntries when they support shortcuts if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) { - binaryDict.addUnigramWord(wordProperty.mWord, wordProperty.getProbability(), + if (!binaryDict.addUnigramEntry(wordProperty.mWord, wordProperty.getProbability(), null /* shortcutTarget */, 0 /* shortcutProbability */, - wordProperty.mIsNotAWord, wordProperty.mIsBlacklistEntry, - 0 /* timestamp */); + wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, + wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { + MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord); + } } else { for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { - binaryDict.addUnigramWord(wordProperty.mWord, wordProperty.getProbability(), + if (!binaryDict.addUnigramEntry(wordProperty.mWord, + wordProperty.getProbability(), shortcutTarget.mWord, shortcutTarget.getProbability(), - wordProperty.mIsNotAWord, wordProperty.mIsBlacklistEntry, - 0 /* timestamp */); + wordProperty.mIsBeginningOfSentence, wordProperty.mIsNotAWord, + wordProperty.mIsBlacklistEntry, 0 /* timestamp */)) { + MakedictLog.e("Cannot add unigram entry for " + wordProperty.mWord + + ", shortcutTarget: " + shortcutTarget.mWord); + return; + } } } if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDict.flushWithGC(); + if (!binaryDict.flushWithGC()) { + MakedictLog.e("Cannot flush dict with GC."); + return; + } } } for (final WordProperty word0Property : dict) { if (null == word0Property.mBigrams) continue; for (final WeightedString word1 : word0Property.mBigrams) { - binaryDict.addBigramWords(word0Property.mWord, word1.mWord, word1.getProbability(), - 0 /* timestamp */); + final PrevWordsInfo prevWordsInfo = new PrevWordsInfo(word0Property.mWord); + if (!binaryDict.addNgramEntry(prevWordsInfo, word1.mWord, + word1.getProbability(), 0 /* timestamp */)) { + MakedictLog.e("Cannot add n-gram entry for " + + prevWordsInfo + " -> " + word1.mWord); + return; + } if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDict.flushWithGC(); + if (!binaryDict.flushWithGC()) { + MakedictLog.e("Cannot flush dict with GC."); + return; + } } } } - binaryDict.flushWithGC(); + if (!binaryDict.flushWithGC()) { + MakedictLog.e("Cannot flush dict with GC."); + return; + } binaryDict.close(); } diff --git a/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java new file mode 100644 index 000000000..0f2f9814b --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryTests.java @@ -0,0 +1,117 @@ +/* + * 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.personalization; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.ExpandableBinaryDictionary; +import com.android.inputmethod.latin.ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback; +import com.android.inputmethod.latin.makedict.CodePointUtils; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; + +/** + * Unit tests for personalization dictionary + */ +@LargeTest +public class PersonalizationDictionaryTests extends AndroidTestCase { + private static final String TAG = PersonalizationDictionaryTests.class.getSimpleName(); + + private static final Locale LOCALE_EN_US = new Locale("en", "US"); + private static final String DUMMY_PACKAGE_NAME = "test.package.name"; + private static final long TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS = 120; + + private DictionaryFacilitator getDictionaryFacilitator() { + final ArrayList<String> dictTypes = new ArrayList<>(); + dictTypes.add(Dictionary.TYPE_MAIN); + dictTypes.add(Dictionary.TYPE_PERSONALIZATION); + final DictionaryFacilitator dictionaryFacilitator = new DictionaryFacilitator(); + dictionaryFacilitator.resetDictionariesForTesting(getContext(), LOCALE_EN_US, dictTypes, + new HashMap<String, File>(), new HashMap<String, Map<String, String>>()); + return dictionaryFacilitator; + } + + public void testAddManyTokens() { + final DictionaryFacilitator dictionaryFacilitator = getDictionaryFacilitator(); + dictionaryFacilitator.clearPersonalizationDictionary(); + final int dataChunkCount = 20; + final int wordCountInOneChunk = 2000; + final Random random = new Random(System.currentTimeMillis()); + final int[] codePointSet = CodePointUtils.LATIN_ALPHABETS_LOWER; + + final SpacingAndPunctuations spacingAndPunctuations = + new SpacingAndPunctuations(getContext().getResources()); + + final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds( + System.currentTimeMillis()); + + for (int i = 0; i < dataChunkCount; i++) { + final ArrayList<String> tokens = new ArrayList<>(); + for (int j = 0; j < wordCountInOneChunk; j++) { + tokens.add(CodePointUtils.generateWord(random, codePointSet)); + } + final PersonalizationDataChunk personalizationDataChunk = new PersonalizationDataChunk( + true /* inputByUser */, tokens, timeStampInSeconds, DUMMY_PACKAGE_NAME); + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AddMultipleDictionaryEntriesCallback callback = + new AddMultipleDictionaryEntriesCallback() { + @Override + public void onFinished() { + countDownLatch.countDown(); + } + }; + dictionaryFacilitator.addEntriesToPersonalizationDictionary(personalizationDataChunk, + spacingAndPunctuations, callback); + try { + countDownLatch.await(TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS, + TimeUnit.SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e); + } + } + dictionaryFacilitator.flushPersonalizationDictionary(); + try { + dictionaryFacilitator.waitForLoadingDictionariesForTesting( + TIMEOUT_TO_WAIT_DICTIONARY_OPERATIONS_IN_SECONDS, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e); + } + final String dictName = ExpandableBinaryDictionary.getDictName( + PersonalizationDictionary.NAME, LOCALE_EN_US, null /* dictFile */); + final File dictFile = ExpandableBinaryDictionary.getDictFile( + getContext(), dictName, null /* dictFile */); + + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, 0 /* size */, + true /* useFullEditDistance */, LOCALE_EN_US, Dictionary.TYPE_PERSONALIZATION, + true /* isUpdatable */); + assertTrue(binaryDictionary.isValidDictionary()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index f2d7b76b2..48d3a1cad 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -21,16 +21,17 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import com.android.inputmethod.latin.ExpandableBinaryDictionary; +import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Random; -import java.util.Set; import java.util.concurrent.TimeUnit; /** @@ -101,19 +102,20 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } private static List<String> generateWords(final int number, final Random random) { - final Set<String> wordSet = CollectionUtils.newHashSet(); + final HashSet<String> wordSet = new HashSet<>(); while (wordSet.size() < number) { wordSet.add(generateWord(random.nextInt())); } - return new ArrayList<String>(wordSet); + return new ArrayList<>(wordSet); } private static void addToDict(final UserHistoryDictionary dict, final List<String> words) { - String prevWord = null; + PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; for (String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWord, word, true, - (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); - prevWord = word; + UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, + (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()), + DistracterFilter.EMPTY_DISTRACTER_FILTER); + prevWordsInfo = new PrevWordsInfo(word); } } @@ -132,7 +134,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { dict.waitAllTasksForTests(); for (int i = 0; i < numberOfWords; ++i) { final String word = words.get(i); - assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); + assertTrue(dict.isInDictionary(word)); } } // write to file. @@ -260,24 +262,25 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(getContext(), dummyLocale); dict.waitAllTasksForTests(); - String prevWord = null; + PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null); for (final String word : words) { - UserHistoryDictionary.addToDictionary(dict, prevWord, word, true, mCurrentTime); - prevWord = word; + UserHistoryDictionary.addToDictionary(dict, prevWordsInfo, word, true, mCurrentTime, + DistracterFilter.EMPTY_DISTRACTER_FILTER); + prevWordsInfo = new PrevWordsInfo(word); dict.waitAllTasksForTests(); - assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); + assertTrue(dict.isInDictionary(word)); } forcePassingShortTime(); dict.runGCIfRequired(); dict.waitAllTasksForTests(); for (final String word : words) { - assertTrue(dict.isInUnderlyingBinaryDictionaryForTests(word)); + assertTrue(dict.isInDictionary(word)); } forcePassingLongTime(); dict.runGCIfRequired(); dict.waitAllTasksForTests(); for (final String word : words) { - assertFalse(dict.isInUnderlyingBinaryDictionaryForTests(word)); + assertFalse(dict.isInDictionary(word)); } } } diff --git a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java index 995d7f07b..2272d6ba0 100644 --- a/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java +++ b/tests/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerServiceTest.java @@ -39,7 +39,7 @@ public class AndroidSpellCheckerServiceTest extends InputTestsBase { // it yields 5). assertTrue(suggestions.length >= 2); // We also assume the top suggestion should be "this". - assertEquals("", "this", suggestions[0]); + assertEquals("Test basic spell checking", "this", suggestions[0]); } public void testRussianSpellchecker() { @@ -62,4 +62,21 @@ public class AndroidSpellCheckerServiceTest extends InputTestsBase { // Russian dictionary. assertEquals("", "года", suggestions[0]); } + + public void testSpellcheckWithPeriods() { + changeLanguage("en_US"); + mEditText.setText("I'm.sure "); + mEditText.setSelection(mEditText.getText().length()); + mEditText.onAttachedToWindow(); + sleep(1000); + runMessages(); + sleep(1000); + + final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); + // If no span, the following will crash + final String[] suggestions = span.getSuggestions(); + // The first suggestion should be "I'm sure". + assertEquals("Test spell checking of mistyped period for space", "I'm sure", + suggestions[0]); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java index 7fd167977..1501e942a 100644 --- a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -45,27 +45,27 @@ public class AsyncResultHolderTests extends AndroidTestCase { } public void testGetWithoutSet() { - final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>(); final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); assertEquals(DEFAULT_VALUE, resultValue); } public void testGetBeforeSet() { - final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>(); setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS); final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); assertEquals(DEFAULT_VALUE, resultValue); } public void testGetAfterSet() { - final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>(); holder.set(SET_VALUE); final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); assertEquals(SET_VALUE, resultValue); } public void testGetBeforeTimeout() { - final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>(); + final AsyncResultHolder<Integer> holder = new AsyncResultHolder<>(); setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS); final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); assertEquals(SET_VALUE, resultValue); diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java index d86639101..a333ee9bc 100644 --- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java @@ -48,7 +48,7 @@ public class BinaryDictionaryUtilsTests extends AndroidTestCase { private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { final File file = getDictFile(dictId); FileUtils.deleteRecursively(file); - Map<String, String> attributeMap = new HashMap<String, String>(); + Map<String, String> attributeMap = new HashMap<>(); attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); diff --git a/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java new file mode 100644 index 000000000..ae2623d12 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/ExecutorUtilsTests.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Unit tests for ExecutorUtils. + */ +@MediumTest +public class ExecutorUtilsTests extends AndroidTestCase { + private static final String TAG = ExecutorUtilsTests.class.getSimpleName(); + + private static final String TEST_EXECUTOR_ID = "test"; + private static final int NUM_OF_TASKS = 10; + private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; + + public void testExecute() { + final ExecutorService executor = ExecutorUtils.getExecutor(TEST_EXECUTOR_ID); + final AtomicInteger v = new AtomicInteger(0); + for (int i = 0; i < NUM_OF_TASKS; ++i) { + executor.execute(new Runnable() { + @Override + public void run() { + v.incrementAndGet(); + } + }); + } + try { + executor.awaitTermination(DELAY_FOR_WAITING_TASKS_MILLISECONDS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping.", e); + } + + assertEquals(NUM_OF_TASKS, v.get()); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java deleted file mode 100644 index e0755483c..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.util.Log; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Unit tests for PrioritizedSerialExecutor. - * TODO: Add more detailed tests to make use of priorities, etc. - */ -@MediumTest -public class PrioritizedSerialExecutorTests extends AndroidTestCase { - private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName(); - - private static final int NUM_OF_TASKS = 10; - private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500; - - public void testExecute() { - final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); - final AtomicInteger v = new AtomicInteger(0); - for (int i = 0; i < NUM_OF_TASKS; ++i) { - executor.execute(new Runnable() { - @Override - public void run() { - v.incrementAndGet(); - } - }); - } - try { - Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); - } catch (InterruptedException e) { - Log.d(TAG, "Exception while sleeping.", e); - } - - assertEquals(NUM_OF_TASKS, v.get()); - } - - public void testExecutePrioritized() { - final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); - final AtomicInteger v = new AtomicInteger(0); - for (int i = 0; i < NUM_OF_TASKS; ++i) { - executor.executePrioritized(new Runnable() { - @Override - public void run() { - v.incrementAndGet(); - } - }); - } - try { - Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); - } catch (InterruptedException e) { - Log.d(TAG, "Exception while sleeping.", e); - } - - assertEquals(NUM_OF_TASKS, v.get()); - } - - public void testExecuteCombined() { - final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor(); - final AtomicInteger v = new AtomicInteger(0); - for (int i = 0; i < NUM_OF_TASKS; ++i) { - executor.execute(new Runnable() { - @Override - public void run() { - v.incrementAndGet(); - } - }); - } - - for (int i = 0; i < NUM_OF_TASKS; ++i) { - executor.executePrioritized(new Runnable() { - @Override - public void run() { - v.incrementAndGet(); - } - }); - } - - try { - Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS); - } catch (InterruptedException e) { - Log.d(TAG, "Exception while sleeping.", e); - } - - assertEquals(2 * NUM_OF_TASKS, v.get()); - } -} diff --git a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java index ada80c3fa..a3f2ce586 100644 --- a/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/RecapitalizeStatusTests.java @@ -29,25 +29,25 @@ public class RecapitalizeStatusTests extends AndroidTestCase { public void testTrim() { final RecapitalizeStatus status = new RecapitalizeStatus(); - status.initialize(30, 40, "abcdefghij", Locale.ENGLISH, SPACE); + status.start(30, 40, "abcdefghij", Locale.ENGLISH, SPACE); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(40, status.getNewCursorEnd()); - status.initialize(30, 44, " abcdefghij", Locale.ENGLISH, SPACE); + status.start(30, 44, " abcdefghij", Locale.ENGLISH, SPACE); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(34, status.getNewCursorStart()); assertEquals(44, status.getNewCursorEnd()); - status.initialize(30, 40, "abcdefgh ", Locale.ENGLISH, SPACE); + status.start(30, 40, "abcdefgh ", Locale.ENGLISH, SPACE); status.trim(); assertEquals("abcdefgh", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(38, status.getNewCursorEnd()); - status.initialize(30, 45, " abcdefghij ", Locale.ENGLISH, SPACE); + status.start(30, 45, " abcdefghij ", Locale.ENGLISH, SPACE); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(33, status.getNewCursorStart()); @@ -56,7 +56,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { public void testRotate() { final RecapitalizeStatus status = new RecapitalizeStatus(); - status.initialize(29, 40, "abcd efghij", Locale.ENGLISH, SPACE); + status.start(29, 40, "abcd efghij", Locale.ENGLISH, SPACE); status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -68,7 +68,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); - status.initialize(29, 40, "Abcd Efghij", Locale.ENGLISH, SPACE); + status.start(29, 40, "Abcd Efghij", Locale.ENGLISH, SPACE); status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -80,7 +80,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); - status.initialize(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, SPACE); + status.start(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, SPACE); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -92,7 +92,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status.initialize(29, 39, "AbCDefghij", Locale.ENGLISH, SPACE); + status.start(29, 39, "AbCDefghij", Locale.ENGLISH, SPACE); status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -106,7 +106,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); - status.initialize(29, 40, "Abcd efghij", Locale.ENGLISH, SPACE); + status.start(29, 40, "Abcd efghij", Locale.ENGLISH, SPACE); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -120,7 +120,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status.initialize(30, 34, "grüß", Locale.GERMAN, SPACE); + status.start(30, 34, "grüß", Locale.GERMAN, SPACE); status.rotate(); assertEquals("Grüß", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); @@ -138,7 +138,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(34, status.getNewCursorEnd()); - status.initialize(30, 33, "œuf", Locale.FRENCH, SPACE); + status.start(30, 33, "œuf", Locale.FRENCH, SPACE); status.rotate(); assertEquals("Œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); @@ -156,7 +156,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status.initialize(30, 33, "œUf", Locale.FRENCH, SPACE); + status.start(30, 33, "œUf", Locale.FRENCH, SPACE); status.rotate(); assertEquals("œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); @@ -178,7 +178,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status.initialize(30, 35, "école", Locale.FRENCH, SPACE); + status.start(30, 35, "école", Locale.FRENCH, SPACE); status.rotate(); assertEquals("École", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); diff --git a/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java index 3eb704093..8e764e40f 100644 --- a/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/ResourceUtilsTests.java @@ -24,10 +24,10 @@ import java.util.HashMap; @SmallTest public class ResourceUtilsTests extends AndroidTestCase { public void testFindConstantForKeyValuePairsSimple() { - final HashMap<String,String> anyKeyValue = CollectionUtils.newHashMap(); + final HashMap<String,String> anyKeyValue = new HashMap<>(); anyKeyValue.put("anyKey", "anyValue"); final HashMap<String,String> nullKeyValue = null; - final HashMap<String,String> emptyKeyValue = CollectionUtils.newHashMap(); + final HashMap<String,String> emptyKeyValue = new HashMap<>(); final String[] nullArray = null; assertNull(ResourceUtils.findConstantForKeyValuePairs(anyKeyValue, nullArray)); @@ -48,7 +48,7 @@ public class ResourceUtilsTests extends AndroidTestCase { "HARDWARE=mako,0.5", }; - final HashMap<String,String> keyValues = CollectionUtils.newHashMap(); + final HashMap<String,String> keyValues = new HashMap<>(); keyValues.put(HARDWARE_KEY, "grouper"); assertEquals("0.3", ResourceUtils.findConstantForKeyValuePairs(keyValues, array)); keyValues.put(HARDWARE_KEY, "mako"); @@ -88,7 +88,7 @@ public class ResourceUtilsTests extends AndroidTestCase { "HARDWARE=mantaray:MODEL=Nexus 10:MANUFACTURER=samsung,0.2" }; - final HashMap<String,String> keyValues = CollectionUtils.newHashMap(); + final HashMap<String,String> keyValues = new HashMap<>(); keyValues.put(HARDWARE_KEY, "grouper"); keyValues.put(MODEL_KEY, "Nexus 7"); keyValues.put(MANUFACTURER_KEY, "asus"); @@ -126,7 +126,7 @@ public class ResourceUtilsTests extends AndroidTestCase { "HARDWARE=manta.*:MODEL=Nexus 10:MANUFACTURER=samsung,0.2" }; - final HashMap<String,String> keyValues = CollectionUtils.newHashMap(); + final HashMap<String,String> keyValues = new HashMap<>(); keyValues.put(HARDWARE_KEY, "grouper"); keyValues.put(MODEL_KEY, "Nexus 7"); keyValues.put(MANUFACTURER_KEY, "asus"); diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java index ff1103e4f..4156de78b 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguagetUtilsTests.java @@ -31,7 +31,7 @@ import java.util.Locale; @SmallTest public class SpacebarLanguagetUtilsTests extends AndroidTestCase { // All input method subtypes of LatinIME. - private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList(); + private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>(); private RichInputMethodManager mRichImm; private Resources mRes; diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java index 2a4ead383..cd9a98356 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java @@ -285,20 +285,6 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertTrue(bytesStr.equals(bytesStr2)); } - public void testContainsOnlyWhitespace() { - assertTrue(StringUtils.containsOnlyWhitespace(" ")); - assertTrue(StringUtils.containsOnlyWhitespace("")); - assertTrue(StringUtils.containsOnlyWhitespace(" \n\t\t")); - // U+2002 : EN SPACE - // U+2003 : EM SPACE - // U+3000 : IDEOGRAPHIC SPACE (commonly "double-width space") - assertTrue(StringUtils.containsOnlyWhitespace("\u2002\u2003\u3000")); - assertFalse(StringUtils.containsOnlyWhitespace(" a ")); - assertFalse(StringUtils.containsOnlyWhitespace(". ")); - assertFalse(StringUtils.containsOnlyWhitespace(".")); - assertTrue(StringUtils.containsOnlyWhitespace("")); - } - public void testJsonUtils() { final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; final List<Object> objArray = Arrays.asList(objs); @@ -372,4 +358,14 @@ public class StringAndJsonUtilsTests extends AndroidTestCase { assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small", exceptionHappened); } + + public void testGetTrailingSingleQuotesCount() { + assertEquals(0, StringUtils.getTrailingSingleQuotesCount("")); + assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'")); + assertEquals(5, StringUtils.getTrailingSingleQuotesCount("'''''")); + assertEquals(0, StringUtils.getTrailingSingleQuotesCount("a")); + assertEquals(0, StringUtils.getTrailingSingleQuotesCount("'this")); + assertEquals(1, StringUtils.getTrailingSingleQuotesCount("'word'")); + assertEquals(0, StringUtils.getTrailingSingleQuotesCount("I'm")); + } } diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index ee345905c..8e409ab99 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -31,7 +31,7 @@ import java.util.Locale; @SmallTest public class SubtypeLocaleUtilsTests extends AndroidTestCase { // All input method subtypes of LatinIME. - private final ArrayList<InputMethodSubtype> mSubtypesList = CollectionUtils.newArrayList(); + private final ArrayList<InputMethodSubtype> mSubtypesList = new ArrayList<>(); private RichInputMethodManager mRichImm; private Resources mRes; diff --git a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java b/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java deleted file mode 100644 index 28a9f3d5c..000000000 --- a/tests/src/com/android/inputmethod/research/MotionEventReaderTests.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.research; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import android.util.JsonReader; - -import com.android.inputmethod.research.MotionEventReader.ReplayData; - -import java.io.IOException; -import java.io.StringReader; - -@SmallTest -public class MotionEventReaderTests extends AndroidTestCase { - private MotionEventReader mMotionEventReader = new MotionEventReader(); - private ReplayData mReplayData; - - @Override - protected void setUp() throws Exception { - super.setUp(); - mReplayData = new ReplayData(); - } - - private JsonReader jsonReaderForString(final String s) { - return new JsonReader(new StringReader(s)); - } - - public void testTopLevelDataVariant() { - final JsonReader jsonReader = jsonReaderForString( - "{" - + "\"_ct\": 1359590400000," - + "\"_ut\": 4381933," - + "\"_ty\": \"MotionEvent\"," - + "\"action\": \"UP\"," - + "\"isLoggingRelated\": false," - + "\"x\": 100.0," - + "\"y\": 200.0" - + "}" - ); - try { - mMotionEventReader.readLogStatement(jsonReader, mReplayData); - } catch (IOException e) { - e.printStackTrace(); - fail("IOException thrown"); - } - assertEquals("x set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100); - assertEquals("y set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200); - assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1); - assertEquals("only one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1); - } - - public void testNestedDataVariant() { - final JsonReader jsonReader = jsonReaderForString( - "{" - + " \"_ct\": 135959040000," - + " \"_ut\": 4382702," - + " \"_ty\": \"MotionEvent\"," - + " \"action\": \"MOVE\"," - + " \"isLoggingRelated\": false," - + " \"motionEvent\": {" - + " \"pointerIds\": [" - + " 0" - + " ]," - + " \"xyt\": [" - + " {" - + " \"t\": 4382551," - + " \"d\": [" - + " {" - + " \"x\": 100.0," - + " \"y\": 200.0," - + " \"toma\": 999.0," - + " \"tomi\": 999.0," - + " \"o\": 0.0" - + " }" - + " ]" - + " }," - + " {" - + " \"t\": 4382559," - + " \"d\": [" - + " {" - + " \"x\": 300.0," - + " \"y\": 400.0," - + " \"toma\": 999.0," - + " \"tomi\": 999.0," - + " \"o\": 0.0" - + " }" - + " ]" - + " }" - + " ]" - + " }" - + "}" - ); - try { - mMotionEventReader.readLogStatement(jsonReader, mReplayData); - } catch (IOException e) { - e.printStackTrace(); - fail("IOException thrown"); - } - assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100); - assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200); - assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].x, 300); - assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(1)[0].y, 400); - assertEquals("only one pointer", mReplayData.mPointerCoordsArrays.get(0).length, 1); - assertEquals("two MotionEvents", mReplayData.mPointerCoordsArrays.size(), 2); - } - - public void testNestedDataVariantMultiPointer() { - final JsonReader jsonReader = jsonReaderForString( - "{" - + " \"_ct\": 135959040000," - + " \"_ut\": 4382702," - + " \"_ty\": \"MotionEvent\"," - + " \"action\": \"MOVE\"," - + " \"isLoggingRelated\": false," - + " \"motionEvent\": {" - + " \"pointerIds\": [" - + " 1" - + " ]," - + " \"xyt\": [" - + " {" - + " \"t\": 4382551," - + " \"d\": [" - + " {" - + " \"x\": 100.0," - + " \"y\": 200.0," - + " \"toma\": 999.0," - + " \"tomi\": 999.0," - + " \"o\": 0.0" - + " }," - + " {" - + " \"x\": 300.0," - + " \"y\": 400.0," - + " \"toma\": 999.0," - + " \"tomi\": 999.0," - + " \"o\": 0.0" - + " }" - + " ]" - + " }" - + " ]" - + " }" - + "}" - ); - try { - mMotionEventReader.readLogStatement(jsonReader, mReplayData); - } catch (IOException e) { - e.printStackTrace(); - fail("IOException thrown"); - } - assertEquals("x1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].x, 100); - assertEquals("y1 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[0].y, 200); - assertEquals("x2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].x, 300); - assertEquals("y2 set correctly", (int) mReplayData.mPointerCoordsArrays.get(0)[1].y, 400); - assertEquals("two pointers", mReplayData.mPointerCoordsArrays.get(0).length, 2); - assertEquals("one MotionEvent", mReplayData.mPointerCoordsArrays.size(), 1); - } -} diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index e12d7e0b5..ce51df1eb 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -15,7 +15,9 @@ # 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 +ifeq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this +$(warning dicttool_aosp is not supported on $(LATINIME_HOST_OSNAME)) +else # TODO: Remove this LATINIME_DICTTOOL_AOSP_LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(LATINIME_DICTTOOL_AOSP_LOCAL_PATH) @@ -44,11 +46,11 @@ LATINIME_SRC_FILES_FOR_DICTTOOL := \ latin/InputPointers.java \ latin/LastComposedWord.java \ latin/LatinImeLogger.java \ + latin/PrevWordsInfo.java \ latin/SuggestedWords.java \ latin/WordComposer.java \ latin/settings/NativeSuggestOptions.java \ latin/utils/BinaryDictionaryUtils.java \ - latin/utils/CollectionUtils.java \ latin/utils/CombinedFormatUtils.java \ latin/utils/CoordinateUtils.java \ latin/utils/FileUtils.java \ @@ -82,7 +84,7 @@ LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \ $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIRECTORY)) LOCAL_JAVA_LIBRARIES := junit -LOCAL_ADDITIONAL_DEPENDENCIES := $(LATINIME_HOST_NATIVE_LIBNAME) +LOCAL_REQUIRED_MODULES := $(LATINIME_HOST_NATIVE_LIBNAME) LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_MODULE := dicttool_aosp diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk index 74034482b..028025d91 100644 --- a/tools/dicttool/NativeLib.mk +++ b/tools/dicttool/NativeLib.mk @@ -30,6 +30,9 @@ ifeq ($(FLAG_DBG), true) endif #FLAG_DBG LOCAL_CFLAGS += -DHOST_TOOL -fPIC -Wno-deprecated -Wno-unused-parameter -Wno-unused-function +ifneq ($(strip $(HOST_JDK_IS_64BIT_VERSION)),) +LOCAL_MULTILIB := 64 +endif #HOST_JDK_IS_64BIT_VERSION LOCAL_CLANG := true # For C++11 diff --git a/tools/dicttool/compat/android/content/Context.java b/tools/dicttool/compat/android/content/Context.java new file mode 100644 index 000000000..afe1322ac --- /dev/null +++ b/tools/dicttool/compat/android/content/Context.java @@ -0,0 +1,20 @@ +/* + * 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 android.content; + +public class Context { +} diff --git a/tools/dicttool/compat/android/test/AndroidTestCase.java b/tools/dicttool/compat/android/test/AndroidTestCase.java index d01b7ad7c..f765ce0fc 100644 --- a/tools/dicttool/compat/android/test/AndroidTestCase.java +++ b/tools/dicttool/compat/android/test/AndroidTestCase.java @@ -16,6 +16,8 @@ package android.test; +import com.android.inputmethod.latin.dicttool.Test; + import junit.framework.TestCase; import java.io.File; @@ -27,7 +29,11 @@ import java.io.File; */ public class AndroidTestCase extends TestCase { public File getCacheDir() { - return new File("."); + final File dir = Test.TEST_TMP_DIR; + if (!dir.isDirectory()) { + dir.mkdirs(); + } + return dir; } public AndroidTestCase getContext() { return this; diff --git a/tools/dicttool/compat/android/util/SparseArray.java b/tools/dicttool/compat/android/util/SparseArray.java index 6c76f19f4..9efbd39c7 100644 --- a/tools/dicttool/compat/android/util/SparseArray.java +++ b/tools/dicttool/compat/android/util/SparseArray.java @@ -16,8 +16,6 @@ package android.util; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.ArrayList; import java.util.Collections; @@ -30,8 +28,8 @@ public class SparseArray<E> { } public SparseArray(final int initialCapacity) { - mKeys = CollectionUtils.newArrayList(initialCapacity); - mValues = CollectionUtils.newArrayList(initialCapacity); + mKeys = new ArrayList<>(initialCapacity); + mValues = new ArrayList<>(initialCapacity); } public int size() { diff --git a/tools/dicttool/compat/android/util/SparseIntArray.java b/tools/dicttool/compat/android/util/SparseIntArray.java index ac8a04ceb..e4d3dfd07 100644 --- a/tools/dicttool/compat/android/util/SparseIntArray.java +++ b/tools/dicttool/compat/android/util/SparseIntArray.java @@ -24,7 +24,7 @@ public class SparseIntArray { } public SparseIntArray(final int initialCapacity) { - mArray = new SparseArray<Integer>(initialCapacity); + mArray = new SparseArray<>(initialCapacity); } public int size() { diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java index b68df1ce7..458f22c45 100644 --- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java +++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java @@ -16,13 +16,20 @@ package com.android.inputmethod.event; -import com.android.inputmethod.latin.utils.CollectionUtils; - 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 = new StringBuilder(); - public CombinerChain(final Combiner... combinerList) {} + private StringBuilder mComposingWord; + public CombinerChain(final String initialText, final Combiner... combinerList) { + mComposingWord = new StringBuilder(initialText); + } public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { mComposingWord.append(newEvent.getTextToCommit()); @@ -35,4 +42,9 @@ public class CombinerChain { 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 f9771c8dd..2cbc04154 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java @@ -19,15 +19,14 @@ 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.FormatSpec; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import org.xml.sax.SAXException; -import java.io.File; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -54,7 +53,7 @@ public final class BinaryDictOffdeviceUtils { private final static int MAX_DECODE_DEPTH = 8; public static class DecoderChainSpec { - ArrayList<String> mDecoderSpec = new ArrayList<String>(); + ArrayList<String> mDecoderSpec = new ArrayList<>(); File mFile; public DecoderChainSpec addStep(final String stepDescription) { mDecoderSpec.add(stepDescription); 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 391328fda..6a0e1b7f0 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java @@ -98,7 +98,7 @@ public class CombinedInputOutput { headerLine = reader.readLine(); } final String header[] = headerLine.split(","); - final HashMap<String, String> attributes = new HashMap<String, String>(); + final HashMap<String, String> attributes = new HashMap<>(); for (String item : header) { final String keyValue[] = item.split("="); if (2 != keyValue.length) { @@ -115,8 +115,8 @@ public class CombinedInputOutput { String word = null; ProbabilityInfo probabilityInfo = new ProbabilityInfo(0); boolean isNotAWord = false; - ArrayList<WeightedString> bigrams = new ArrayList<WeightedString>(); - ArrayList<WeightedString> shortcuts = new ArrayList<WeightedString>(); + ArrayList<WeightedString> bigrams = new ArrayList<>(); + ArrayList<WeightedString> shortcuts = new ArrayList<>(); while (null != (line = reader.readLine())) { if (line.startsWith(COMMENT_LINE_STARTER)) continue; final String args[] = line.trim().split(","); @@ -128,8 +128,8 @@ public class CombinedInputOutput { dict.setBigram(word, s.mWord, s.mProbabilityInfo); } } - if (!shortcuts.isEmpty()) shortcuts = new ArrayList<WeightedString>(); - if (!bigrams.isEmpty()) bigrams = new ArrayList<WeightedString>(); + if (!shortcuts.isEmpty()) shortcuts = new ArrayList<>(); + if (!bigrams.isEmpty()) bigrams = new ArrayList<>(); isNotAWord = false; for (String param : args) { final String params[] = param.split("=", 2); @@ -223,7 +223,7 @@ public class CombinedInputOutput { */ public static void writeDictionaryCombined( final Writer destination, final FusionDictionary dict) throws IOException { - final TreeSet<WordProperty> wordPropertiesInDict = new TreeSet<WordProperty>(); + final TreeSet<WordProperty> wordPropertiesInDict = new TreeSet<>(); for (final WordProperty wordProperty : dict) { // This for ordering by frequency, then by asciibetic order wordPropertiesInDict.add(wordProperty); 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 8e8ab19e0..37c8d4184 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -138,7 +138,7 @@ public class DictionaryMaker { } public Arguments(String[] argsArray) throws IOException { - final LinkedList<String> args = new LinkedList<String>(Arrays.asList(argsArray)); + final LinkedList<String> args = new LinkedList<>(Arrays.asList(argsArray)); if (args.isEmpty()) { displayHelp(); } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java index cacee5268..8ae035f64 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java @@ -29,8 +29,7 @@ public class Dicttool { abstract public String getHelp(); abstract public void run() throws Exception; } - static HashMap<String, Class<? extends Command>> sCommands = - new HashMap<String, Class<? extends Command>>(); + static HashMap<String, Class<? extends Command>> sCommands = new HashMap<>(); static { CommandList.populate(); } diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java index cd3d4d393..94d1ae8bb 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java @@ -85,8 +85,7 @@ public class Diff extends Dicttool.Command { private static void diffHeaders(final FusionDictionary dict0, final FusionDictionary dict1) { boolean hasDifferences = false; - final HashMap<String, String> options1 = - new HashMap<String, String>(dict1.mOptions.mAttributes); + final HashMap<String, String> options1 = new HashMap<>(dict1.mOptions.mAttributes); for (final String optionKey : dict0.mOptions.mAttributes.keySet()) { if (!dict0.mOptions.mAttributes.get(optionKey).equals( dict1.mOptions.mAttributes.get(optionKey))) { 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 48817b1b1..b6383d788 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java @@ -19,16 +19,29 @@ package com.android.inputmethod.latin.dicttool; 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; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.file.Files; import java.util.ArrayList; /** * Dicttool command implementing self-tests. */ public class Test extends Dicttool.Command { + private static final String getTmpDir() { + try { + return Files.createTempDirectory("dicttool").toString(); + } catch (IOException e) { + throw new RuntimeException("Can't get temporary directory", e); + } + } + private static final String TEST_TMP_DIR_BASE = getTmpDir(); + public static final File TEST_TMP_DIR = new File(TEST_TMP_DIR_BASE); public static final String COMMAND = "test"; private static final int DEFAULT_MAX_UNIGRAMS = 1500; private long mSeed = System.currentTimeMillis(); @@ -40,8 +53,8 @@ public class Test extends Dicttool.Command { BinaryDictDecoderEncoderTests.class, BinaryDictEncoderFlattenTreeTests.class, }; - private ArrayList<Method> mAllTestMethods = new ArrayList<Method>(); - private ArrayList<String> mUsedTestMethods = new ArrayList<String>(); + private ArrayList<Method> mAllTestMethods = new ArrayList<>(); + private ArrayList<String> mUsedTestMethods = new ArrayList<>(); public Test() { for (final Class<?> c : sClassesToTest) { @@ -56,8 +69,12 @@ public class Test extends Dicttool.Command { @Override public String getHelp() { - final StringBuilder s = new StringBuilder("test [-s seed] [-m maxUnigrams] [testName...]\n" - + "If seed is not specified, the current time is used.\nTest list is:\n"); + final StringBuilder s = new StringBuilder( + "test [-s seed] [-m maxUnigrams] [-n] [testName...]\n" + + "If seed is not specified, the current time is used.\n" + + "If -n option is provided, do not delete temporary files in " + + TEST_TMP_DIR_BASE + "/*.\n" + + "Test list is:\n"); for (final Method m : mAllTestMethods) { s.append(" "); s.append(m.getName()); @@ -70,17 +87,26 @@ public class Test extends Dicttool.Command { public void run() throws IllegalAccessException, InstantiationException, InvocationTargetException { int i = 0; + boolean deleteTmpDir = true; while (i < mArgs.length) { final String arg = mArgs[i++]; if ("-s".equals(arg)) { mSeed = Long.parseLong(mArgs[i++]); } else if ("-m".equals(arg)) { mMaxUnigrams = Integer.parseInt(mArgs[i++]); + } else if ("-n".equals(arg)) { + deleteTmpDir = false; } else { mUsedTestMethods.add(arg); } } - runChosenTests(); + try { + runChosenTests(); + } finally { + if (deleteTmpDir) { + FileUtils.deleteRecursively(TEST_TMP_DIR); + } + } } private void runChosenTests() throws IllegalAccessException, InstantiationException, diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java index 17e77dca1..7435fa7d6 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java @@ -115,7 +115,7 @@ public class XmlDictInputOutput { } } } else if (ROOT_TAG.equals(localName)) { - final HashMap<String, String> attributes = new HashMap<String, String>(); + final HashMap<String, String> attributes = new HashMap<>(); for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { final String attrName = attrs.getLocalName(attrIndex); attributes.put(attrName, attrs.getValue(attrIndex)); @@ -172,7 +172,7 @@ public class XmlDictInputOutput { DST_ATTRIBUTE = dstAttribute; DST_FREQ = dstFreq; mSrc = null; - mAssocMap = new HashMap<String, ArrayList<WeightedString>>(); + mAssocMap = new HashMap<>(); } @Override @@ -184,7 +184,7 @@ public class XmlDictInputOutput { int freq = getValueFromFreqString(attrs.getValue(uri, DST_FREQ)); WeightedString bigram = new WeightedString(dst, freq / XML_TO_MEMORY_RATIO); ArrayList<WeightedString> bigramList = mAssocMap.get(mSrc); - if (null == bigramList) bigramList = new ArrayList<WeightedString>(); + if (null == bigramList) bigramList = new ArrayList<>(); bigramList.add(bigram); mAssocMap.put(mSrc, bigramList); } @@ -352,7 +352,7 @@ public class XmlDictInputOutput { */ public static void writeDictionaryXml(Writer destination, FusionDictionary dict) throws IOException { - final TreeSet<WordProperty> wordPropertiesInDict = new TreeSet<WordProperty>(); + final TreeSet<WordProperty> wordPropertiesInDict = new TreeSet<>(); for (WordProperty wordProperty : dict) { wordPropertiesInDict.add(wordProperty); } 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 4f1273bdc..fccb654b0 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java @@ -16,12 +16,10 @@ package com.android.inputmethod.latin.dicttool; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictEncoder; import com.android.inputmethod.latin.makedict.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; @@ -32,8 +30,8 @@ import com.android.inputmethod.latin.makedict.Ver2DictEncoder; import junit.framework.TestCase; -import java.io.File; import java.io.BufferedOutputStream; +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java index 6e81c3f3a..71f8ac8d4 100644 --- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java +++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java @@ -32,7 +32,7 @@ import java.util.Random; * Unit tests for FusionDictionary. */ public class FusionDictionaryTest extends TestCase { - private static final ArrayList<String> sWords = new ArrayList<String>(); + private static final ArrayList<String> sWords = new ArrayList<>(); private static final int MAX_UNIGRAMS = 1000; private void prepare(final long seed) { diff --git a/tools/make-keyboard-text/res/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.tmpl b/tools/make-keyboard-text/res/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.tmpl index 2b5494fa5..6a7469ccb 100644 --- a/tools/make-keyboard-text/res/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.tmpl +++ b/tools/make-keyboard-text/res/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.tmpl @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard.internal; -import com.android.inputmethod.latin.utils.CollectionUtils; - import java.util.HashMap; import java.util.Locale; @@ -44,14 +42,12 @@ import java.util.Locale; */ public final class KeyboardTextsTable { // Name to index map. - private static final HashMap<String, Integer> sNameToIndexesMap = CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sNameToIndexesMap = new HashMap<>(); // Locale to texts table map. - private static final HashMap<String, String[]> sLocaleToTextsTableMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String[]> sLocaleToTextsTableMap = new HashMap<>(); // TODO: Remove this variable after debugging. // Texts table to locale maps. - private static final HashMap<String[], String> sTextsTableToLocaleMap = - CollectionUtils.newHashMap(); + private static final HashMap<String[], String> sTextsTableToLocaleMap = new HashMap<>(); public static String getText(final String name, final String[] textsTable) { final Integer indexObj = sNameToIndexesMap.get(name); diff --git a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml index 4ecb10533..ab78f45c6 100644 --- a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml @@ -80,7 +80,7 @@ U+061B: "؛" ARABIC SEMICOLON --> <string name="keyspec_tablet_comma">"،"</string> <string name="keyhintlabel_tablet_comma">"؟"</string> - <string name="morekeys_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,/,\",\'"</string> + <string name="morekeys_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,\",\'"</string> <!-- U+266A: "♪" EIGHTH NOTE --> <string name="morekeys_bullet">♪</string> <!-- U+2605: "★" BLACK STAR diff --git a/tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml index 7998bf161..c22edbaae 100644 --- a/tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml @@ -27,33 +27,33 @@ U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE U+0101: "ā" LATIN SMALL LETTER A WITH MACRON --> <string name="morekeys_a">à,á,â,ä,æ,ã,å,ā</string> - <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE - U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS U+0113: "ē" LATIN SMALL LETTER E WITH MACRON --> - <string name="morekeys_e">è,é,ê,ë,ē</string> - <!-- U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + <string name="morekeys_e">é,è,ê,ë,ē</string> + <!-- U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE U+012B: "ī" LATIN SMALL LETTER I WITH MACRON U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE --> - <string name="morekeys_i">î,ï,í,ī,ì</string> - <!-- U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + <string name="morekeys_i">í,î,ï,ī,ì</string> + <!-- U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE U+0153: "œ" LATIN SMALL LIGATURE OE U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE U+014D: "ō" LATIN SMALL LETTER O WITH MACRON U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE --> - <string name="morekeys_o">ô,ö,ò,ó,œ,ø,ō,õ</string> - <!-- U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + <string name="morekeys_o">ó,ô,ö,ò,œ,ø,ō,õ</string> + <!-- U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE U+016B: "ū" LATIN SMALL LETTER U WITH MACRON --> - <string name="morekeys_u">û,ü,ù,ú,ū</string> + <string name="morekeys_u">ú,û,ü,ù,ū</string> <!-- U+00DF: "ß" LATIN SMALL LETTER SHARP S --> <string name="morekeys_s">ß</string> <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> diff --git a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml index bdbc92afa..58f455504 100644 --- a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml @@ -82,7 +82,7 @@ U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> <string name="keyspec_tablet_comma">"،"</string> <string name="keyhintlabel_tablet_comma">"؟"</string> - <string name="morekeys_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,/,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote"</string> + <string name="morekeys_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,!text/keyspec_left_double_angle_quote,!text/keyspec_right_double_angle_quote"</string> <!-- U+FDFC: "﷼" RIAL SIGN --> <string name="keyspec_currency">﷼</string> <!-- U+266A: "♪" EIGHTH NOTE --> diff --git a/tools/make-keyboard-text/res/values-mr-rIN/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-mr-rIN/donottranslate-more-keys.xml new file mode 100644 index 000000000..19db16da1 --- /dev/null +++ b/tools/make-keyboard-text/res/values-mr-rIN/donottranslate-more-keys.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for "switch to alphabetic" key. + U+0915: "क" DEVANAGARI LETTER KA + U+0916: "ख" DEVANAGARI LETTER KHA + U+0917: "ग" DEVANAGARI LETTER GA --> + <string name="keylabel_to_alpha">कखग</string> + <!-- U+0967: "१" DEVANAGARI DIGIT ONE --> + <string name="keyspec_symbols_1">१</string> + <!-- U+0968: "२" DEVANAGARI DIGIT TWO --> + <string name="keyspec_symbols_2">२</string> + <!-- U+0969: "३" DEVANAGARI DIGIT THREE --> + <string name="keyspec_symbols_3">३</string> + <!-- U+096A: "४" DEVANAGARI DIGIT FOUR --> + <string name="keyspec_symbols_4">४</string> + <!-- U+096B: "५" DEVANAGARI DIGIT FIVE --> + <string name="keyspec_symbols_5">५</string> + <!-- U+096C: "६" DEVANAGARI DIGIT SIX --> + <string name="keyspec_symbols_6">६</string> + <!-- U+096D: "७" DEVANAGARI DIGIT SEVEN --> + <string name="keyspec_symbols_7">७</string> + <!-- U+096E: "८" DEVANAGARI DIGIT EIGHT --> + <string name="keyspec_symbols_8">८</string> + <!-- U+096F: "९" DEVANAGARI DIGIT NINE --> + <string name="keyspec_symbols_9">९</string> + <!-- U+0966: "०" DEVANAGARI DIGIT ZERO --> + <string name="keyspec_symbols_0">०</string> + <!-- Label for "switch to symbols" key. --> + <string name="keylabel_to_symbol">?१२३</string> + <string name="additional_morekeys_symbols_1">1</string> + <string name="additional_morekeys_symbols_2">2</string> + <string name="additional_morekeys_symbols_3">3</string> + <string name="additional_morekeys_symbols_4">4</string> + <string name="additional_morekeys_symbols_5">5</string> + <string name="additional_morekeys_symbols_6">6</string> + <string name="additional_morekeys_symbols_7">7</string> + <string name="additional_morekeys_symbols_8">8</string> + <string name="additional_morekeys_symbols_9">9</string> + <string name="additional_morekeys_symbols_0">0</string> + <!-- U+20B9: "₹" INDIAN RUPEE SIGN --> + <string name="keyspec_currency">₹</string> +</resources> diff --git a/tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml index f9150f366..2c5df0c81 100644 --- a/tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml @@ -28,33 +28,33 @@ U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE U+0101: "ā" LATIN SMALL LETTER A WITH MACRON --> <string name="morekeys_a">à,á,â,ä,æ,ã,å,ā</string> - <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE - U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS U+0113: "ē" LATIN SMALL LETTER E WITH MACRON --> - <string name="morekeys_e">è,é,ê,ë,ē</string> - <!-- U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + <string name="morekeys_e">é,è,ê,ë,ē</string> + <!-- U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS - U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE U+012B: "ī" LATIN SMALL LETTER I WITH MACRON U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE --> - <string name="morekeys_i">î,ï,í,ī,ì</string> - <!-- U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + <string name="morekeys_i">í,î,ï,ī,ì</string> + <!-- U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE - U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE U+0153: "œ" LATIN SMALL LIGATURE OE U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE U+014D: "ō" LATIN SMALL LETTER O WITH MACRON U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE --> - <string name="morekeys_o">ô,ö,ò,ó,œ,ø,ō,õ</string> - <!-- U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + <string name="morekeys_o">ó,ô,ö,ò,œ,ø,ō,õ</string> + <!-- U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE - U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE U+016B: "ū" LATIN SMALL LETTER U WITH MACRON --> - <string name="morekeys_u">û,ü,ù,ú,ū</string> + <string name="morekeys_u">ú,û,ü,ù,ū</string> <!-- U+00DF: "ß" LATIN SMALL LETTER SHARP S --> <string name="morekeys_s">ß</string> <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java index c947a63bf..abb33397b 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java @@ -61,7 +61,7 @@ public final class JarUtils { } public static ArrayList<String> getEntryNameListing(final JarFile jar, final JarFilter filter) { - final ArrayList<String> result = new ArrayList<String>(); + final ArrayList<String> result = new ArrayList<>(); final Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java index 0dfa37667..c1a7ec5eb 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java @@ -35,7 +35,7 @@ public final class LocaleUtils { // Intentional empty constructor for utility class. } - private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); private static final int INDEX_LANGUAGE = 0; private static final int INDEX_SCRIPT = 1; diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java index 36a03f8dc..6c15ce6bf 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java @@ -36,7 +36,7 @@ public class MakeKeyboardText { } public Options(final String[] argsArray) { - final LinkedList<String> args = new LinkedList<String>(Arrays.asList(argsArray)); + final LinkedList<String> args = new LinkedList<>(Arrays.asList(argsArray)); String arg = null; String java = null; try { diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java index c8cb4acec..563acc57e 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java @@ -43,12 +43,11 @@ public class MoreKeysResources { private final JarFile mJar; // String resources maps sorted by its language. The language is determined from the jar entry // name by calling {@link JarUtils#getLocaleFromEntryName(String)}. - private final TreeMap<String, StringResourceMap> mResourcesMap = - new TreeMap<String, StringResourceMap>(); + private final TreeMap<String, StringResourceMap> mResourcesMap = new TreeMap<>(); // Default string resources map. private final StringResourceMap mDefaultResourceMap; // Histogram of string resource names. This is used to sort {@link #mSortedResourceNames}. - private final HashMap<String, Integer> mNameHistogram = new HashMap<String, Integer>(); + private final HashMap<String, Integer> mNameHistogram = new HashMap<>(); // Sorted string resource names array; Descending order of histogram count. // The string resource name is specified as an attribute "name" in string resource files. // The string resource can be accessed by specifying name "!text/<name>" @@ -68,7 +67,7 @@ public class MoreKeysResources { // Initialize name histogram and names list. final HashMap<String, Integer> nameHistogram = mNameHistogram; - final ArrayList<String> resourceNamesList = new ArrayList<String>(); + final ArrayList<String> resourceNamesList = new ArrayList<>(); for (final StringResource res : mDefaultResourceMap.getResources()) { nameHistogram.put(res.mName, 0); // Initialize histogram value. resourceNamesList.add(res.mName); diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java index 6a79268e5..cf44f2cad 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java @@ -73,7 +73,7 @@ public class StringResourceMap { } mResources = Collections.unmodifiableList(handler.mResources); - final HashMap<String,StringResource> map = new HashMap<String,StringResource>(); + final HashMap<String, StringResource> map = new HashMap<>(); for (final StringResource res : mResources) { map.put(res.mName, res); } @@ -105,7 +105,7 @@ public class StringResourceMap { private static final String TAG_STRING = "string"; private static final String ATTR_NAME = "name"; - final ArrayList<StringResource> mResources = new ArrayList<StringResource>(); + final ArrayList<StringResource> mResources = new ArrayList<>(); private String mName; private final StringBuilder mValue = new StringBuilder(); |