diff options
349 files changed, 4958 insertions, 1189 deletions
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png Binary files differindex 9aa8db60e..87211a502 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png Binary files differindex 5e6a9d6a4..fa2cb8542 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.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_dark_normal_off_holo.9.png Binary files differindex a3ba2230d..b1af23b6c 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png Binary files differindex 9f4587b4a..814e40235 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png Binary files differindex 7ec33dd20..90abe3940 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png Binary files differindex 655bc01b1..48eeb3f54 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png Binary files differindex 138e915d9..71e0683cd 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.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_light_normal_holo.9.png Binary files differindex baff85873..6da273b09 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png Binary files differindex 5612c51a1..6768241a7 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png Binary files differindex c2e8b3779..10f8e97e4 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png diff --git a/java/res/drawable-hdpi/ic_emoji_light.png b/java/res/drawable-hdpi/ic_emoji_light.png Binary files differdeleted file mode 100644 index 2e3638bf3..000000000 --- a/java/res/drawable-hdpi/ic_emoji_light.png +++ /dev/null diff --git a/java/res/drawable-hdpi/ic_ime_light.png b/java/res/drawable-hdpi/ic_ime_light.png Binary files differdeleted file mode 100644 index 4fd3ba126..000000000 --- a/java/res/drawable-hdpi/ic_ime_light.png +++ /dev/null diff --git a/java/res/drawable-hdpi/ic_ime_switcher_dark.png b/java/res/drawable-hdpi/ic_ime_switcher_dark.png Binary files differnew file mode 100644 index 000000000..7506af5a3 --- /dev/null +++ b/java/res/drawable-hdpi/ic_ime_switcher_dark.png diff --git a/java/res/drawable-hdpi/ic_subtype_keyboard.png b/java/res/drawable-hdpi/ic_subtype_keyboard.png Binary files differdeleted file mode 100644 index 484305655..000000000 --- a/java/res/drawable-hdpi/ic_subtype_keyboard.png +++ /dev/null diff --git a/java/res/drawable-hdpi/ic_subtype_mic_dark.png b/java/res/drawable-hdpi/ic_subtype_mic_dark.png Binary files differnew file mode 100644 index 000000000..eacbcd255 --- /dev/null +++ b/java/res/drawable-hdpi/ic_subtype_mic_dark.png diff --git a/java/res/drawable-hdpi/keyboard_background_ics.9.png b/java/res/drawable-hdpi/keyboard_background_holo.9.png Binary files differindex 73868751c..73868751c 100644 --- a/java/res/drawable-hdpi/keyboard_background_ics.9.png +++ b/java/res/drawable-hdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png Binary files differindex 28b406a5c..50ed568ff 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png Binary files differindex e42cd88dc..9fa6d0003 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png Binary files differindex 160344073..c73269b7e 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png Binary files differindex a40d4277c..fffd4021e 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png Binary files differindex 1f6807376..61c23c19b 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png Binary files differindex ec53593d9..827d74363 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png Binary files differindex 53d7b6fb3..dc2fc7dfc 100644 --- a/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png Binary files differindex e173beb73..32f426402 100644 --- a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png +++ b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_delete.png b/java/res/drawable-hdpi/sym_keyboard_delete.png Binary files differdeleted file mode 100644 index 0591b82cd..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_delete.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png b/java/res/drawable-hdpi/sym_keyboard_delete_holo.png Binary files differdeleted file mode 100644 index d3e108846..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 000000000..d2d3560a3 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic.png b/java/res/drawable-hdpi/sym_keyboard_label_mic.png Binary files differdeleted file mode 100644 index 4e0a8ed8e..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_label_mic.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png Binary files differdeleted file mode 100644 index f8df44741..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch.png b/java/res/drawable-hdpi/sym_keyboard_language_switch.png Binary files differdeleted file mode 100644 index 7b980a0c8..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_language_switch.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png Binary files differnew file mode 100644 index 000000000..78d3a1fc5 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_mic.png b/java/res/drawable-hdpi/sym_keyboard_mic.png Binary files differdeleted file mode 100644 index 520a40f09..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_mic.png +++ /dev/null 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 differnew file mode 100644 index 000000000..3c5469403 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_return.png b/java/res/drawable-hdpi/sym_keyboard_return.png Binary files differdeleted file mode 100644 index 9743c7f2f..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_return.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_return_holo.png b/java/res/drawable-hdpi/sym_keyboard_return_holo.png Binary files differdeleted file mode 100644 index 8978934b8..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_return_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png Binary files differnew file mode 100644 index 000000000..60d893cf3 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_search.png b/java/res/drawable-hdpi/sym_keyboard_search.png Binary files differdeleted file mode 100644 index 8cd28c64a..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_search.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_search_holo.png b/java/res/drawable-hdpi/sym_keyboard_search_holo.png Binary files differdeleted file mode 100644 index b987a20f1..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_search_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png Binary files differnew file mode 100644 index 000000000..fa0d1bde1 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_settings.png b/java/res/drawable-hdpi/sym_keyboard_settings.png Binary files differdeleted file mode 100644 index 1e5bf939e..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_settings.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png Binary files differdeleted file mode 100644 index 5af09ad8c..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png Binary files differnew file mode 100644 index 000000000..c76008ab3 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_shift.png b/java/res/drawable-hdpi/sym_keyboard_shift.png Binary files differdeleted file mode 100644 index 8e3d0320c..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_shift.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_holo.png Binary files differdeleted file mode 100644 index c58f9ab5c..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png Binary files differnew file mode 100644 index 000000000..544b7e141 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png Binary files differdeleted file mode 100644 index d345634a6..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png Binary files differdeleted file mode 100644 index 7a5c03713..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png Binary files differnew file mode 100644 index 000000000..9b1d6a015 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_space_holo.png b/java/res/drawable-hdpi/sym_keyboard_space_holo.png Binary files differdeleted file mode 100644 index e8bc3902d..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_space_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png Binary files differnew file mode 100644 index 000000000..12e27ade7 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_tab.png b/java/res/drawable-hdpi/sym_keyboard_tab.png Binary files differdeleted file mode 100644 index 3d1c5c0ea..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_tab.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png b/java/res/drawable-hdpi/sym_keyboard_tab_holo.png Binary files differdeleted file mode 100644 index 8d10d057c..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png Binary files differnew file mode 100644 index 000000000..2e5f811f3 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_holo.png Binary files differdeleted file mode 100644 index 8a6336a57..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png Binary files differnew file mode 100644 index 000000000..c1e16a651 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png Binary files differdeleted file mode 100644 index edf1379ab..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png Binary files differnew file mode 100644 index 000000000..26d068490 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png Binary files differdeleted file mode 100644 index 5fa30ceb8..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png Binary files differnew file mode 100644 index 000000000..9f9bc173b --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png Binary files differdeleted file mode 100644 index 91367f3d2..000000000 --- a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png +++ /dev/null diff --git a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png Binary files differnew file mode 100644 index 000000000..f0f832e13 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png Binary files differindex e810c7789..f98653ea1 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png Binary files differindex d449d7600..8e9a34957 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.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_dark_normal_off_holo.9.png Binary files differindex fa24d5987..58a316fba 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png Binary files differindex f3fc64114..b7b2dca43 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png Binary files differindex 8f340d355..4a92b80dd 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png Binary files differindex 53ea5f894..72125a065 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png Binary files differindex 69c84e7ec..82413d4cc 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.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_light_normal_holo.9.png Binary files differindex 976083fdf..2915588bf 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png Binary files differindex c39dd4a94..049385984 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png Binary files differindex 93a6e7921..ee0aae28b 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png diff --git a/java/res/drawable-mdpi/ic_emoji_light.png b/java/res/drawable-mdpi/ic_emoji_light.png Binary files differdeleted file mode 100644 index a3195041a..000000000 --- a/java/res/drawable-mdpi/ic_emoji_light.png +++ /dev/null diff --git a/java/res/drawable-mdpi/ic_ime_light.png b/java/res/drawable-mdpi/ic_ime_light.png Binary files differdeleted file mode 100644 index d94ad6f7e..000000000 --- a/java/res/drawable-mdpi/ic_ime_light.png +++ /dev/null diff --git a/java/res/drawable-mdpi/ic_ime_switcher_dark.png b/java/res/drawable-mdpi/ic_ime_switcher_dark.png Binary files differnew file mode 100644 index 000000000..152f65300 --- /dev/null +++ b/java/res/drawable-mdpi/ic_ime_switcher_dark.png diff --git a/java/res/drawable-mdpi/ic_subtype_keyboard.png b/java/res/drawable-mdpi/ic_subtype_keyboard.png Binary files differdeleted file mode 100644 index d28efc106..000000000 --- a/java/res/drawable-mdpi/ic_subtype_keyboard.png +++ /dev/null diff --git a/java/res/drawable-mdpi/keyboard_background_ics.9.png b/java/res/drawable-mdpi/keyboard_background_holo.9.png Binary files differindex fbe97f7a2..fbe97f7a2 100644 --- a/java/res/drawable-mdpi/keyboard_background_ics.9.png +++ b/java/res/drawable-mdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png Binary files differindex 7a9f640d1..564f5460c 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png Binary files differindex 5b06f09bb..427c87061 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png Binary files differindex fd992d6f4..ea757296d 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png Binary files differindex 128dcd6ad..1911c429f 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png Binary files differindex 0b08d1747..cdef116d2 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png Binary files differindex cf0b33c1d..dea5d076c 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png Binary files differindex 61988a8e1..441edc30b 100644 --- a/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png +++ b/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png diff --git a/java/res/drawable-mdpi/suggestions_strip_divider.png b/java/res/drawable-mdpi/suggestions_strip_divider.png Binary files differindex 2dbe2f94b..21e904939 100644 --- a/java/res/drawable-mdpi/suggestions_strip_divider.png +++ b/java/res/drawable-mdpi/suggestions_strip_divider.png diff --git a/java/res/drawable-mdpi/sym_keyboard_delete.png b/java/res/drawable-mdpi/sym_keyboard_delete.png Binary files differdeleted file mode 100644 index 1b0f3f836..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_delete.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png b/java/res/drawable-mdpi/sym_keyboard_delete_holo.png Binary files differdeleted file mode 100644 index 86be35185..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 000000000..edd9d164e --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic.png b/java/res/drawable-mdpi/sym_keyboard_label_mic.png Binary files differdeleted file mode 100644 index a354d5321..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_label_mic.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png Binary files differdeleted file mode 100644 index 15606e95f..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png Binary files differnew file mode 100644 index 000000000..537f39b02 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch.png b/java/res/drawable-mdpi/sym_keyboard_language_switch.png Binary files differdeleted file mode 100644 index f840a631d..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_language_switch.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png Binary files differnew file mode 100644 index 000000000..828929bc8 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_mic.png b/java/res/drawable-mdpi/sym_keyboard_mic.png Binary files differdeleted file mode 100644 index e926b3fa6..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_mic.png +++ /dev/null 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 differnew file mode 100644 index 000000000..5e58866a7 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png b/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png Binary files differnew file mode 100644 index 000000000..84a63dc7f --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png diff --git a/java/res/drawable-mdpi/sym_keyboard_return.png b/java/res/drawable-mdpi/sym_keyboard_return.png Binary files differdeleted file mode 100644 index 0c10f004a..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_return.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_return_holo.png b/java/res/drawable-mdpi/sym_keyboard_return_holo.png Binary files differdeleted file mode 100644 index bfcb91328..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_return_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png Binary files differnew file mode 100644 index 000000000..e10103caf --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_search.png b/java/res/drawable-mdpi/sym_keyboard_search.png Binary files differdeleted file mode 100644 index 614f85f5e..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_search.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_search_holo.png b/java/res/drawable-mdpi/sym_keyboard_search_holo.png Binary files differdeleted file mode 100644 index dd3c83a84..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_search_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png Binary files differnew file mode 100644 index 000000000..290cde41b --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_settings.png b/java/res/drawable-mdpi/sym_keyboard_settings.png Binary files differdeleted file mode 100644 index ad7618fa0..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_settings.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png Binary files differdeleted file mode 100644 index 36c8c9623..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png Binary files differnew file mode 100644 index 000000000..a76a976c5 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_shift.png b/java/res/drawable-mdpi/sym_keyboard_shift.png Binary files differdeleted file mode 100644 index 5109b0471..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_shift.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_holo.png Binary files differdeleted file mode 100644 index 621946455..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png Binary files differnew file mode 100644 index 000000000..37375d935 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked.png Binary files differdeleted file mode 100644 index 244179c2d..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_shift_locked.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png Binary files differdeleted file mode 100644 index fb3a020d8..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png Binary files differnew file mode 100644 index 000000000..3654868dc --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png Binary files differnew file mode 100644 index 000000000..71272bb88 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_space_holo.png b/java/res/drawable-mdpi/sym_keyboard_space_holo.png Binary files differdeleted file mode 100644 index 1f787d573..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_space_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png Binary files differnew file mode 100644 index 000000000..a38f99496 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_tab.png b/java/res/drawable-mdpi/sym_keyboard_tab.png Binary files differdeleted file mode 100644 index eddb9a592..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_tab.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png b/java/res/drawable-mdpi/sym_keyboard_tab_holo.png Binary files differdeleted file mode 100644 index 8d20153ee..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png Binary files differnew file mode 100644 index 000000000..f883807f2 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_holo.png Binary files differdeleted file mode 100644 index 0795fcc9b..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png Binary files differnew file mode 100644 index 000000000..16be37d05 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png Binary files differdeleted file mode 100644 index f76da5797..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png Binary files differnew file mode 100644 index 000000000..95d718a46 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png Binary files differdeleted file mode 100644 index 70370d83d..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png Binary files differnew file mode 100644 index 000000000..8957e282b --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png Binary files differdeleted file mode 100644 index a69eade17..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png +++ /dev/null diff --git a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png Binary files differnew file mode 100644 index 000000000..5f49e64aa --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png Binary files differindex d990c0258..738316d66 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png Binary files differindex d2cd029bb..a2f6ac0e2 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.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_dark_normal_off_holo.9.png Binary files differindex bca39cf17..2f00fc623 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png Binary files differindex ab8fb2e86..20251a000 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png Binary files differindex 3871689ef..84d173967 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png Binary files differindex 912506368..ee4490eac 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png Binary files differindex 35ce67fdc..e8124776c 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.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_light_normal_holo.9.png Binary files differindex b26f1d27a..0ef4a4b5f 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png Binary files differindex c23a4b225..f770962c3 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png Binary files differindex 0c7bfdace..891d00024 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png diff --git a/java/res/drawable-xhdpi/ic_emoji_light.png b/java/res/drawable-xhdpi/ic_emoji_light.png Binary files differdeleted file mode 100644 index 21bc9090d..000000000 --- a/java/res/drawable-xhdpi/ic_emoji_light.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/ic_ime_light.png b/java/res/drawable-xhdpi/ic_ime_light.png Binary files differdeleted file mode 100644 index 9d2caeda6..000000000 --- a/java/res/drawable-xhdpi/ic_ime_light.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/ic_ime_switcher_dark.png b/java/res/drawable-xhdpi/ic_ime_switcher_dark.png Binary files differnew file mode 100644 index 000000000..c567077e5 --- /dev/null +++ b/java/res/drawable-xhdpi/ic_ime_switcher_dark.png diff --git a/java/res/drawable-xhdpi/ic_subtype_keyboard.png b/java/res/drawable-xhdpi/ic_subtype_keyboard.png Binary files differdeleted file mode 100644 index a79bb3458..000000000 --- a/java/res/drawable-xhdpi/ic_subtype_keyboard.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xhdpi/ic_subtype_mic_dark.png Binary files differnew file mode 100644 index 000000000..17581ba89 --- /dev/null +++ b/java/res/drawable-xhdpi/ic_subtype_mic_dark.png diff --git a/java/res/drawable-xhdpi/keyboard_background_ics.9.png b/java/res/drawable-xhdpi/keyboard_background_holo.9.png Binary files differindex f5c9df3e5..f5c9df3e5 100644 --- a/java/res/drawable-xhdpi/keyboard_background_ics.9.png +++ b/java/res/drawable-xhdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png Binary files differindex d999127f2..e8c65f677 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png Binary files differindex c4d694136..543bc763e 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png Binary files differindex 5429c1785..ec42aadb6 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png Binary files differindex 5135a0869..319e9d7cf 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png Binary files differindex 19a77a29f..052032be7 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png Binary files differindex ae2ffff8e..c7e9d1c9e 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png diff --git a/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png Binary files differindex 1dee699f4..dde1856e3 100644 --- a/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png +++ b/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png diff --git a/java/res/drawable-xhdpi/suggestions_strip_divider.png b/java/res/drawable-xhdpi/suggestions_strip_divider.png Binary files differindex 0d8b98437..4101ebc59 100644 --- a/java/res/drawable-xhdpi/suggestions_strip_divider.png +++ b/java/res/drawable-xhdpi/suggestions_strip_divider.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete.png b/java/res/drawable-xhdpi/sym_keyboard_delete.png Binary files differdeleted file mode 100644 index 3c0b8b186..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_delete.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png Binary files differdeleted file mode 100644 index 354c09ee6..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 000000000..e3e37d5ff --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png Binary files differdeleted file mode 100644 index 49810a02f..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png Binary files differdeleted file mode 100644 index 8eeb179f5..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch.png Binary files differdeleted file mode 100644 index 6c2fb53ec..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_language_switch.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png Binary files differnew file mode 100644 index 000000000..b8687f550 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_mic.png b/java/res/drawable-xhdpi/sym_keyboard_mic.png Binary files differdeleted file mode 100644 index 1323b6d1e..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_mic.png +++ /dev/null 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 differnew file mode 100644 index 000000000..566ba1fcd --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_return.png b/java/res/drawable-xhdpi/sym_keyboard_return.png Binary files differdeleted file mode 100644 index ad061227e..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_return.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xhdpi/sym_keyboard_return_holo.png Binary files differdeleted file mode 100644 index ba424adfd..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_return_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png Binary files differnew file mode 100644 index 000000000..7b7ad1747 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_search.png b/java/res/drawable-xhdpi/sym_keyboard_search.png Binary files differdeleted file mode 100644 index aa785a221..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_search.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_holo.png b/java/res/drawable-xhdpi/sym_keyboard_search_holo.png Binary files differdeleted file mode 100644 index f2fb2a2b5..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_search_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png Binary files differnew file mode 100644 index 000000000..36b1646bb --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings.png b/java/res/drawable-xhdpi/sym_keyboard_settings.png Binary files differdeleted file mode 100644 index 50704255d..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_settings.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png Binary files differdeleted file mode 100644 index 99ee97dbf..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png Binary files differnew file mode 100644 index 000000000..05eaffe2e --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift.png b/java/res/drawable-xhdpi/sym_keyboard_shift.png Binary files differdeleted file mode 100644 index 290170619..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_shift.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png Binary files differdeleted file mode 100644 index 1046b4545..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png Binary files differnew file mode 100644 index 000000000..5ab549114 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png Binary files differdeleted file mode 100644 index a5deb60e9..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png Binary files differdeleted file mode 100644 index 6acb565d9..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png Binary files differnew file mode 100644 index 000000000..b820eaabb --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png Binary files differnew file mode 100644 index 000000000..686831fd3 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xhdpi/sym_keyboard_space_holo.png Binary files differdeleted file mode 100644 index 504a3ed45..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_space_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png Binary files differnew file mode 100644 index 000000000..7114b740f --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab.png b/java/res/drawable-xhdpi/sym_keyboard_tab.png Binary files differdeleted file mode 100644 index 0ef2ab5b9..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_tab.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png Binary files differdeleted file mode 100644 index ff380eeab..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png Binary files differnew file mode 100644 index 000000000..73ebfe525 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png Binary files differdeleted file mode 100644 index b2bb9b803..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png Binary files differnew file mode 100644 index 000000000..944a8524d --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png Binary files differdeleted file mode 100644 index 23e75bfe7..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png Binary files differnew file mode 100644 index 000000000..2016caf40 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png Binary files differdeleted file mode 100644 index 26694274e..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png Binary files differnew file mode 100644 index 000000000..2f9607add --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png Binary files differdeleted file mode 100644 index 75a22b65f..000000000 --- a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png Binary files differnew file mode 100644 index 000000000..ab07f7549 --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png Binary files differindex 680421eaf..b35c29fe6 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.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_dark_normal_holo.9.png Binary files differindex ae2675053..17f0a7a58 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.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_dark_normal_off_holo.9.png Binary files differindex c92a669f9..b0e815eb2 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png Binary files differindex 40f5011c0..97f96258e 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png Binary files differindex 6ff6319d3..dfb16a76b 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png Binary files differindex 818ea70fd..bf1d34686 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png Binary files differindex a476d2a9e..962277165 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.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_light_normal_holo.9.png Binary files differindex 9c280a655..4ddfdcb6c 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png Binary files differindex 3c17c5eec..17144b673 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png Binary files differindex 6d2af5942..0cbb2ec84 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_light.png b/java/res/drawable-xxhdpi/ic_emoji_light.png Binary files differdeleted file mode 100644 index 7480e5294..000000000 --- a/java/res/drawable-xxhdpi/ic_emoji_light.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/ic_ime_light.png b/java/res/drawable-xxhdpi/ic_ime_light.png Binary files differdeleted file mode 100644 index 0309635d2..000000000 --- a/java/res/drawable-xxhdpi/ic_ime_light.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png b/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png Binary files differnew file mode 100644 index 000000000..f99f7d0c7 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png diff --git a/java/res/drawable-xxhdpi/ic_subtype_keyboard.png b/java/res/drawable-xxhdpi/ic_subtype_keyboard.png Binary files differdeleted file mode 100644 index 0bb4283b0..000000000 --- a/java/res/drawable-xxhdpi/ic_subtype_keyboard.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png Binary files differnew file mode 100644 index 000000000..811103a56 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png diff --git a/java/res/drawable-xxhdpi/keyboard_background_ics.9.png b/java/res/drawable-xxhdpi/keyboard_background_holo.9.png Binary files differindex bcef0f839..bcef0f839 100644 --- a/java/res/drawable-xxhdpi/keyboard_background_ics.9.png +++ b/java/res/drawable-xxhdpi/keyboard_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png Binary files differindex bd1ef3cd9..11eee94f3 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png Binary files differindex 65af4b569..2079e0462 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png Binary files differindex ac6750dcb..c4178d9a8 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png Binary files differindex cea7c05f6..121411a06 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png Binary files differindex 520fa7c6b..d3d8733fd 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png Binary files differindex eee221758..d7ec8bcb2 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png Binary files differindex 721c24400..ca576deaf 100644 --- a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png +++ b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png Binary files differdeleted file mode 100644 index be3cb7ce7..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png Binary files differnew file mode 100644 index 000000000..92be79241 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png Binary files differdeleted file mode 100644 index b6d4477bd..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png Binary files differdeleted file mode 100644 index 7cd0684a0..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_dark.png Binary files differnew file mode 100644 index 000000000..88b55bb37 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_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 differnew file mode 100644 index 000000000..f55af308c --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png Binary files differdeleted file mode 100644 index 7d9580796..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png Binary files differnew file mode 100644 index 000000000..46ee50eba --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png Binary files differdeleted file mode 100644 index 6b09d8e57..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png Binary files differnew file mode 100644 index 000000000..f518748ce --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png Binary files differdeleted file mode 100644 index 7041bb6ce..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png Binary files differnew file mode 100644 index 000000000..e4358463b --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png Binary files differdeleted file mode 100644 index 2b4fbbba6..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png Binary files differnew file mode 100644 index 000000000..523286e6e --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png Binary files differdeleted file mode 100644 index 91c8603fd..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png Binary files differnew file mode 100644 index 000000000..87926d9a6 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png Binary files differnew file mode 100644 index 000000000..04b721617 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png Binary files differdeleted file mode 100644 index 65aa5ea9b..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png Binary files differnew file mode 100644 index 000000000..1dab1f431 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png Binary files differdeleted file mode 100644 index 1f4ae3df7..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png Binary files differnew file mode 100644 index 000000000..6eb3eb0b3 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png Binary files differdeleted file mode 100644 index f04cadf6f..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png Binary files differnew file mode 100644 index 000000000..6809f0711 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png Binary files differdeleted file mode 100644 index e74d523bc..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png Binary files differnew file mode 100644 index 000000000..6bd506a11 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png Binary files differdeleted file mode 100644 index 85289b2a3..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png Binary files differnew file mode 100644 index 000000000..5e225b837 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png Binary files differdeleted file mode 100644 index e610678b1..000000000 --- a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png Binary files differnew file mode 100644 index 000000000..cdfc0295b --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_keyboard_view.xml index 5fee419d0..4566a5a1f 100644 --- a/java/res/layout/emoji_keyboard_view.xml +++ b/java/res/layout/emoji_keyboard_view.xml @@ -40,7 +40,7 @@ <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:background="@drawable/tab_selected" android:divider="@null" android:tabStripEnabled="true" @@ -61,17 +61,27 @@ android:visibility="gone" /> </FrameLayout> </TabHost> + <View + android:layout_width="2dip" + android:layout_height="match_parent" + android:background="@drawable/suggestions_strip_divider" /> <ImageButton android:id="@+id/emoji_keyboard_delete" android:layout_width="0dip" android:layout_weight="12.5" android:layout_height="match_parent" - android:src="@drawable/sym_keyboard_delete_holo" /> + android:background="@color/emoji_key_background_color" + android:src="@drawable/sym_keyboard_delete_holo_dark" /> </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 + 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" /> <LinearLayout android:id="@+id/emoji_action_bar" android:orientation="horizontal" @@ -84,7 +94,7 @@ android:layout_width="0dip" android:layout_weight="0.15" android:layout_height="match_parent" - android:src="@drawable/ic_ime_light" /> + android:src="@drawable/ic_ime_switcher_dark" /> <ImageButton android:id="@+id/emoji_keyboard_space" android:layout_width="0dip" @@ -95,6 +105,6 @@ android:layout_width="0dip" android:layout_weight="0.15" android:layout_height="match_parent" - android:src="@drawable/sym_keyboard_return_holo" /> + android:src="@drawable/sym_keyboard_return_holo_dark" /> </LinearLayout> </com.android.inputmethod.keyboard.EmojiKeyboardView> diff --git a/java/res/values-hy/donottranslate.xml b/java/res/values-hy/donottranslate.xml new file mode 100644 index 000000000..4a6d188fb --- /dev/null +++ b/java/res/values-hy/donottranslate.xml @@ -0,0 +1,29 @@ +<?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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Same list as in English, but add armenian period and comma: --> + <!-- U+055D: "՝" ARMENIAN COMMA --> + <!-- U+0589: "։" ARMENIAN FULL STOP --> + <!-- 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 separate words. Adding armenian period and comma. --> + <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> + <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"։՝</string> +</resources> diff --git a/java/res/values-km/donottranslate.xml b/java/res/values-km/donottranslate.xml new file mode 100644 index 000000000..a9893feec --- /dev/null +++ b/java/res/values-km/donottranslate.xml @@ -0,0 +1,23 @@ +<?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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Whether this language uses spaces between words --> + <bool name="current_language_has_spaces">false</bool> +</resources> diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml index ea762f977..3803cb776 100644 --- a/java/res/values/colors.xml +++ b/java/res/values/colors.xml @@ -33,7 +33,7 @@ <color name="spacebar_text_color_gb">#FFC0C0C0</color> <color name="spacebar_text_shadow_color_gb">#80000000</color> <color name="gesture_floating_preview_color_gb">#C0000000</color> - <!-- Color resources for IceCreamSandwich theme. --> + <!-- Color resources for IceCreamSandwich theme. Base color = 33B5E5 --> <!-- android:color/holo_blue_light value is #FF33B5E5 --> <color name="highlight_color_ics">#FF33B5E5</color> <color name="typed_word_color_ics">#D833B5E5</color> @@ -49,10 +49,21 @@ <color name="spacebar_text_color_ics">#FFC0C0C0</color> <color name="spacebar_text_shadow_color_ics">#80000000</color> <color name="gesture_floating_preview_color_ics">#C0000000</color> + <!-- Color resources for KLP theme. Base color = F0F0F0 --> + <color name="highlight_color_holo">#FFF0F0F0</color> + <color name="typed_word_color_holo">#D8F0F0F0</color> + <color name="suggested_word_color_holo">#B2F0F0F0</color> + <color name="highlight_translucent_color_holo">#99E0E0E0</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/dimens.xml b/java/res/values/dimens.xml index 88e327f26..4e3b2f567 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -113,13 +113,14 @@ <dimen name="gesture_floating_preview_text_offset">73dp</dimen> <dimen name="gesture_floating_preview_horizontal_padding">24dp</dimen> <dimen name="gesture_floating_preview_vertical_padding">16dp</dimen> - <dimen name="gesture_floating_preview_round_radius">3dp</dimen> + <dimen name="gesture_floating_preview_round_radius">2dp</dimen> <!-- Emoji keyboard --> <fraction name="emoji_keyboard_key_width">14.2857%p</fraction> <fraction name="emoji_keyboard_row_height">33%p</fraction> <fraction name="emoji_keyboard_key_letter_size">90%p</fraction> <integer name="emoji_keyboard_max_key_count">21</integer> + <dimen name="emoji_category_page_id_height">3dp</dimen> <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. --> <dimen name="accessibility_edge_slop">8dp</dimen> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 82c5ce456..42e692d2f 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -158,47 +158,47 @@ <!-- Compatibility map from subtypeLocale:subtypeExtraValue to keyboardLayoutSet --> <string-array name="locale_and_extra_value_to_keyboard_layout_set_map"> - <item>en_US:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection</item> + <item>en_US:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>en_GB:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection</item> + <item>en_GB:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>ar:SupportTouchPositionCorrection</item> + <item>ar:SupportTouchPositionCorrection,EmojiCapable</item> <item>arabic</item> - <item>cs:AsciiCapable,SupportTouchPositionCorrection</item> + <item>cs:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwertz</item> - <item>da:AsciiCapable,SupportTouchPositionCorrection</item> + <item>da:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>nordic</item> - <item>de:AsciiCapable,SupportTouchPositionCorrection</item> + <item>de:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwertz</item> - <item>es:AsciiCapable,SupportTouchPositionCorrection</item> + <item>es:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>spanish</item> - <item>fi:AsciiCapable,SupportTouchPositionCorrection</item> + <item>fi:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>nordic</item> - <item>fr:AsciiCapable,SupportTouchPositionCorrection</item> + <item>fr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>azerty</item> - <item>fr_CA:AsciiCapable,SupportTouchPositionCorrection</item> + <item>fr_CA:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>hr:AsciiCapable,SupportTouchPositionCorrection</item> + <item>hr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwertz</item> - <item>hu:AsciiCapable,SupportTouchPositionCorrection</item> + <item>hu:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwertz</item> - <item>it:AsciiCapable,SupportTouchPositionCorrection</item> + <item>it:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>iw:SupportTouchPositionCorrection</item> + <item>iw:SupportTouchPositionCorrection,EmojiCapable</item> <item>hebrew</item> - <item>nb:AsciiCapable,SupportTouchPositionCorrection</item> + <item>nb:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>nordic</item> - <item>nl:AsciiCapable,SupportTouchPositionCorrection</item> + <item>nl:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>pl:AsciiCapable,SupportTouchPositionCorrection</item> + <item>pl:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> - <item>ru:SupportTouchPositionCorrection</item> + <item>ru:SupportTouchPositionCorrection,EmojiCapable</item> <item>east_slavic</item> - <item>sr:SupportTouchPositionCorrection</item> + <item>sr:SupportTouchPositionCorrection,EmojiCapable</item> <item>south_slavic</item> - <item>sv:AsciiCapable,SupportTouchPositionCorrection</item> + <item>sv:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>nordic</item> - <item>tr:AsciiCapable,SupportTouchPositionCorrection</item> + <item>tr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item> <item>qwerty</item> </string-array> diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml index 53448c3e1..ee0ac003c 100644 --- a/java/res/values/keypress-vibration-durations.xml +++ b/java/res/values/keypress-vibration-durations.xml @@ -55,8 +55,8 @@ <item>MODEL=HTL22:MANUFACTURER=HTC,15</item> <!-- Motorola Razor M --> <item>MODEL=XT907:MANUFACTURER=motorola,30</item> - <!-- Sony Xperia Z --> - <item>MODEL=C6603:MANUFACTURER=Sony,35</item> + <!-- Sony Xperia Z, Z Ultra --> + <item>MODEL=C6603|C6806:MANUFACTURER=Sony,35</item> <!-- Default value for unknown device. The negative value means system default. --> <item>,-1</item> </string-array> diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml index 7f8dd235b..d9ac4acb2 100644 --- a/java/res/values/themes-gb.xml +++ b/java/res/values/themes-gb.xml @@ -32,25 +32,25 @@ </style> <style name="KeyboardIcons.GB"> <!-- Keyboard icons --> - <item name="iconShiftKey">@drawable/sym_keyboard_shift</item> - <item name="iconDeleteKey">@drawable/sym_keyboard_delete</item> - <item name="iconSettingsKey">@drawable/sym_keyboard_settings</item> - <item name="iconSpaceKey">@drawable/sym_keyboard_space</item> - <item name="iconEnterKey">@drawable/sym_keyboard_return</item> - <item name="iconSearchKey">@drawable/sym_keyboard_search</item> - <item name="iconTabKey">@drawable/sym_keyboard_tab</item> - <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item> - <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic</item> + <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> + <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo_dark</item> + <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item> + <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item> + <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item> + <item name="iconShortcutKey">@drawable/sym_keyboard_mic_holo_dark</item> + <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo_dark</item> <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space</item> - <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked</item> + <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item> <!-- TODO: Needs non-holo disabled shortcut icon drawable --> - <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo</item> + <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item> <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item> - <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch</item> + <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item> <!-- TODO: Needs dedicated black theme ZWNJ and ZWJ icons --> - <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo</item> - <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo</item> - <item name="iconEmojiKey">@drawable/ic_emoji_light</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_holo_dark</item> </style> <style name="Keyboard.GB" diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 19fb4fd27..33dd50c2c 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -35,23 +35,23 @@ <!-- 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</item> - <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item> - <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item> + <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> <item name="iconSpaceKey">@null</item> - <item name="iconEnterKey">@drawable/sym_keyboard_return_holo</item> - <item name="iconSearchKey">@drawable/sym_keyboard_search_holo</item> - <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item> - <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item> - <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo</item> - <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo</item> - <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo</item> - <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo</item> + <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item> + <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item> + <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item> + <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo_dark</item> + <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo_dark</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item> + <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item> + <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item> <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item> - <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch</item> - <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo</item> - <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo</item> - <item name="iconEmojiKey">@drawable/ic_emoji_light</item> + <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_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_holo_dark</item> </style> <style name="Keyboard.ICS" @@ -69,7 +69,7 @@ name="KeyboardView.ICS" parent="KeyboardView" > - <item name="android:background">@drawable/keyboard_background_ics</item> + <item name="android:background">@drawable/keyboard_background_holo</item> <item name="keyBackground">@drawable/btn_keyboard_key_ics</item> <item name="keyTypeface">bold</item> <item name="keyTextColor">@color/key_text_color_ics</item> @@ -88,10 +88,10 @@ > <item name="keyPreviewLayout">@layout/key_preview_ics</item> <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item> - <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item> + <item name="gestureFloatingPreviewTextColor">@color/highlight_color_holo</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_ics</item> - <item name="gestureTrailColor">@color/highlight_color_ics</item> - <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item> + <item name="gestureTrailColor">@color/highlight_color_holo</item> + <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_holo</item> <item name="autoCorrectionSpacebarLedEnabled">false</item> <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> <item name="spacebarTextColor">@color/spacebar_text_color_ics</item> @@ -135,10 +135,10 @@ > <item name="android:background">@drawable/keyboard_suggest_strip_holo</item> <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item> - <item name="colorValidTypedWord">@color/typed_word_color_ics</item> - <item name="colorTypedWord">@color/typed_word_color_ics</item> - <item name="colorAutoCorrect">@color/highlight_color_ics</item> - <item name="colorSuggested">@color/suggested_word_color_ics</item> + <item name="colorValidTypedWord">@color/typed_word_color_holo</item> + <item name="colorTypedWord">@color/typed_word_color_holo</item> + <item name="colorAutoCorrect">@color/highlight_color_holo</item> + <item name="colorSuggested">@color/suggested_word_color_holo</item> <item name="alphaObsoleted">70%</item> </style> <style name="SuggestionWord.ICS"> diff --git a/java/res/xml-sw600dp/row_pcqwerty5.xml b/java/res/xml-sw600dp/row_pcqwerty5.xml index a79d2a87f..b854f1051 100644 --- a/java/res/xml-sw600dp/row_pcqwerty5.xml +++ b/java/res/xml-sw600dp/row_pcqwerty5.xml @@ -53,7 +53,7 @@ latin:keyXPos="-9.0%p" latin:keyWidth="9.0%p" latin:backgroundType="functional" - latin:keyboardLayout="@xml/key_symbols_period" /> + latin:keyboardLayout="@xml/key_f2" /> </default> </switch> </Row> diff --git a/java/res/xml-sw600dp/rows_khmer.xml b/java/res/xml-sw600dp/rows_khmer.xml new file mode 100644 index 000000000..2824a5c2f --- /dev/null +++ b/java/res/xml-sw600dp/rows_khmer.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer2" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer3" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="7.5%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_khmer4" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/rows_lao.xml b/java/res/xml-sw600dp/rows_lao.xml index cfe8db98e..446d9bd5a 100644 --- a/java/res/xml-sw600dp/rows_lao.xml +++ b/java/res/xml-sw600dp/rows_lao.xml @@ -55,8 +55,17 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/rowkeys_lao4" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw600dp/rows_thai.xml b/java/res/xml-sw600dp/rows_thai.xml index cfcaf6815..7738c7f04 100644 --- a/java/res/xml-sw600dp/rows_thai.xml +++ b/java/res/xml-sw600dp/rows_thai.xml @@ -59,8 +59,17 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/rowkeys_thai4" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <Spacer /> + </case> + <default> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + </default> + </switch> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml/kbd_khmer.xml b/java/res/xml/kbd_khmer.xml new file mode 100644 index 000000000..7a2337a48 --- /dev/null +++ b/java/res/xml/kbd_khmer.xml @@ -0,0 +1,31 @@ +<?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. +*/ +--> + +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + latin:rowHeight="20%p" + latin:verticalGap="@fraction/key_bottom_gap_5row" + latin:keyLetterSize="@fraction/key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row" + latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" +> + <include + latin:keyboardLayout="@xml/rows_khmer" /> +</Keyboard> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index 6b3dc9a0d..67ed9620d 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -184,4 +184,11 @@ latin:keyLabelFlags="hasPopupHint" latin:moreKeys="!text/more_keys_for_punctuation" latin:backgroundType="functional" /> + <key-style + latin:styleName="comKeyStyle" + latin:keyLabel="!text/keylabel_for_popular_domain" + latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase" + latin:keyOutputText="!text/keylabel_for_popular_domain" + latin:moreKeys="!text/more_keys_for_popular_domain" + latin:backgroundType="functional" /> </merge> diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml index b7677a20d..84c2abc08 100644 --- a/java/res/xml/key_styles_currency.xml +++ b/java/res/xml/key_styles_currency.xml @@ -103,6 +103,8 @@ vi: Vietnamese (Dong) --> <!-- TODO: The currency sign of Turkish Lira was created in 2012 and assigned U+20BA for its unicode, although there is no font glyph for it as of November 2012. --> + <!-- TODO: The currency sign of Armenian Dram was created in 2012 and assigned U+058F for + its unicode, although there is no font glyph for it as of September 2013. --> <case latin:languageCode="fa|hi|iw|lo|mn|ne|th|uk|vi" > diff --git a/java/res/xml/keyboard_layout_set_khmer.xml b/java/res/xml/keyboard_layout_set_khmer.xml new file mode 100644 index 000000000..181f98b3d --- /dev/null +++ b/java/res/xml/keyboard_layout_set_khmer.xml @@ -0,0 +1,58 @@ +<?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. +*/ +--> + +<KeyboardLayoutSet + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Element + latin:elementName="alphabet" + latin:elementKeyboard="@xml/kbd_khmer" + latin:enableProximityCharsCorrection="true" /> + <Element + latin:elementName="alphabetAutomaticShifted" + latin:elementKeyboard="@xml/kbd_khmer" + latin:enableProximityCharsCorrection="true" /> + <!-- On these shifted alphabet layouts the proximity characters correction should be disabled + because the letters on these layouts aren't the ones in different case of the above + unshifted layouts. --> + <Element + latin:elementName="alphabetManualShifted" + latin:elementKeyboard="@xml/kbd_khmer" /> + <Element + latin:elementName="alphabetShiftLocked" + latin:elementKeyboard="@xml/kbd_khmer" /> + <Element + latin:elementName="alphabetShiftLockShifted" + latin:elementKeyboard="@xml/kbd_khmer" /> + <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/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml index 7e7c7282e..02b46c23a 100644 --- a/java/res/xml/keys_comma_period.xml +++ b/java/res/xml/keys_comma_period.xml @@ -73,6 +73,20 @@ latin:backgroundType="functional" latin:keyStyle="hasShiftedLetterHintStyle" /> </case> + <case + latin:languageCode="hy" + > + <!-- U+0589: "։" ARMENIAN FULL STOP --> + <Key + latin:keyLabel="։" + latin:keyLabelFlags="hasPopupHint" + latin:backgroundType="functional" + latin:moreKeys="!text/more_keys_for_punctuation" /> + <!-- U+055D: "՝" ARMENIAN COMMA --> + <Key + latin:keyLabel="՝" + latin:backgroundType="functional" /> + </case> <default> <Key latin:keyLabel="." diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 6014646bb..f0e04c220 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -54,6 +54,7 @@ iw: Hebrew/hebrew # "he" is official language code of Hebrew. ka: Georgian/georgian (kk: Kazakh/east_slavic) # disabled temporarily. waiting for strnig resources. + km: Khmer/khmer ky: Kyrgyz/east_slavic lo: Lao/lao lt: Lithuanian/qwerty @@ -62,8 +63,8 @@ mn: Mongolian/mongolian ms: Malay/qwerty nb: Norwegian Bokmål/nordic - ne: Nepali Romanized/nepali_romanized - ne: Nepali Traditional/nepali_traditional + ne: Nepali Romanized/nepali_romanized # disabled temporarily + ne: Nepali Traditional/nepali_traditional # disabled temporarily nl: Dutch/qwerty nl_BE: Dutch Belgium/azerty pl: Polish/qwerty @@ -94,464 +95,474 @@ <input-method xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity" android:isDefault="@bool/im_is_default"> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_en_US" android:subtypeId="0xc9194f98" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_en_GB" android:subtypeId="0xb045e755" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x6f972360" android:imeSubtypeLocale="af" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x590dde40" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x70b0f974" android:imeSubtypeLocale="az" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x1dc3a859" android:imeSubtypeLocale="be" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x0ba9c0e8" android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian" + android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_bulgarian_bds" android:subtypeId="0x5f51ba9a" android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds" + android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xd2e520d5" android:imeSubtypeLocale="ca" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x2d3d2ed0" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x2df4605d" android:imeSubtypeLocale="da" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x2e2cbe61" android:imeSubtypeLocale="de" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x0e7802d3" android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=greek" + android:imeSubtypeExtraValue="KeyboardLayoutSet=greek,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x4090554a" android:imeSubtypeLocale="eo" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x30a6e00e" android:imeSubtypeLocale="es" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_es_US" android:subtypeId="0x84d2efc6" android:imeSubtypeLocale="es_US" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" /> <!-- - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x623f9286" android:imeSubtypeLocale="es_419" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" /> --> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xec2d3955" android:imeSubtypeLocale="et" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xbe66c254" android:imeSubtypeLocale="fa" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi" + android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x31cecda3" android:imeSubtypeLocale="fi" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x324da12c" android:imeSubtypeLocale="fr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xeadbb691" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x39753b7f" android:imeSubtypeLocale="hi" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi" + android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x35b7526a" android:imeSubtypeLocale="hr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x35e198ed" android:imeSubtypeLocale="hu" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xe39ac3ca" android:imeSubtypeLocale="hy" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic,EmojiCapable" /> <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. --> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x7daea460" android:imeSubtypeLocale="in" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x7df519e5" android:imeSubtypeLocale="is" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x37885a0b" android:imeSubtypeLocale="it" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x66fb18bd" android:imeSubtypeLocale="iw" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x6e119e6a" android:imeSubtypeLocale="ka" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian" + android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable" /> <!-- - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x2d73d2f6" android:imeSubtypeLocale="kk" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" /> --> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0x1365683a" + android:imeSubtypeLocale="km" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable" + /> + <!-- android:subtypeId="Need this for km" --> + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x2e391c04" android:imeSubtypeLocale="ky" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8315772c" android:imeSubtypeLocale="lo" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=lao" + android:imeSubtypeExtraValue="KeyboardLayoutSet=lao,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8321bb43" android:imeSubtypeLocale="lt" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x833dea45" android:imeSubtypeLocale="lv" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xaf50ab7c" android:imeSubtypeLocale="mk" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xcdcfc3ab" android:imeSubtypeLocale="mn" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian" + android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x84c87c61" android:imeSubtypeLocale="ms" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3f12ee14" android:imeSubtypeLocale="nb" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <!-- + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xd80a4cee" android:imeSubtypeLocale="ne" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized" + android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_nepali_traditional" android:subtypeId="0x5fafea88" android:imeSubtypeLocale="ne" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional" + android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + --> + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3f9fd91e" android:imeSubtypeLocale="nl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x500ca92c" android:imeSubtypeLocale="nl_BE" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x43098a5c" android:imeSubtypeLocale="pl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xcafff4a6" android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xe2fffc5a" android:imeSubtypeLocale="pt_PT" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8d185978" android:imeSubtypeLocale="ro" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x763a8752" android:imeSubtypeLocale="ru" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8e94d413" android:imeSubtypeLocale="sk" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8ea2eb94" android:imeSubtypeLocale="sl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x77c5196e" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" /> <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_serbian_cyrillic" android:subtypeId="0xXXXXXXXX" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_serbian_latin" android:subtypeId="0xXXXXXXXX" android:imeSubtypeLocale="sr-Latn" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> --> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x48b4ff43" android:imeSubtypeLocale="sv" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8f3dee1f" android:imeSubtypeLocale="sw" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x1f94d5d4" android:imeSubtypeLocale="th" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=thai" + android:imeSubtypeExtraValue="KeyboardLayoutSet=thai,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xf08285ef" android:imeSubtypeLocale="tl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x4a3179de" android:imeSubtypeLocale="tr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3e84492c" android:imeSubtypeLocale="uk" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic" + android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x93972eee" android:imeSubtypeLocale="vi" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x9b13ab76" android:imeSubtypeLocale="zu" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" /> - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_no_language_qwerty" android:subtypeId="0xa239ebad" android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable" + android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable,EmojiCapable" /> <!-- Emoji subtype has to be an addtional subtype added at boot time because ICS doesn't support Emoji. --> <!-- - <subtype android:icon="@drawable/ic_subtype_keyboard" + <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_emoji" android:subtypeId="0xc14d88b2" android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji" + android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji,EmojiCapable" /> --> </input-method> diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml index 0e618059e..4ec908ba1 100644 --- a/java/res/xml/row_pcqwerty5.xml +++ b/java/res/xml/row_pcqwerty5.xml @@ -51,13 +51,13 @@ latin:keyWidth="11.538%p" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="42.310%p" /> + latin:keyWidth="38.464%p" /> </case> <!-- languageSwitchKeyEnabled="false" --> <default> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="53.848%p" /> + latin:keyWidth="50.002%p" /> </default> </switch> <Key @@ -71,9 +71,9 @@ </case> <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" --> <default> - <include - latin:keyboardLayout="@xml/key_symbols_period" - latin:backgroundType="functional" /> + <Key + latin:keyStyle="emojiKeyStyle" + latin:keyWidth="fillRight" /> </default> </switch> </Row> diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml index 340beb99b..578bc1234 100644 --- a/java/res/xml/row_qwerty4.xml +++ b/java/res/xml/row_qwerty4.xml @@ -49,6 +49,14 @@ <include latin:keyboardLayout="@xml/key_nepali_traditional_period" /> </case> + <case + latin:languageCode="hy" + > + <!-- U+0589: "։" ARMENIAN FULL STOP --> + <Key + latin:keyLabel="։" + latin:keyStyle="punctuationKeyStyle" /> + </case> <default> <Key latin:keyStyle="punctuationKeyStyle" /> diff --git a/java/res/xml/rowkeys_khmer1.xml b/java/res/xml/rowkeys_khmer1.xml new file mode 100644 index 000000000..25da66400 --- /dev/null +++ b/java/res/xml/rowkeys_khmer1.xml @@ -0,0 +1,194 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+200D: ZERO WIDTH JOINER --> + <Key + latin:keyLabel="!" + latin:moreKeys="!icon/zwj_key|‍" /> + <!-- U+17D7: "ៗ" KHMER SIGN LEK TOO + U+200C: ZERO WIDTH NON-JOINER --> + <Key + latin:keyLabel="ៗ" + latin:moreKeys="!icon/zwnj_key|‌" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D1: "៑" KHMER SIGN VIRIAM --> + <Key + latin:keyLabel=""" + latin:keyHintLabel="៑" + latin:moreKeys="៑" /> + <!-- U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL + U+20AC: "€" EURO SIGN --> + <Key + latin:keyLabel="៛" + latin:keyHintLabel="$" + latin:moreKeys="$,€" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D6: "៖" KHMER SIGN CAMNUC PII KUUH --> + <Key + latin:keyLabel="%" + latin:keyHintLabel="៖" + latin:moreKeys="៖" /> + <!-- U+17CD: "៍" KHMER SIGN TOANDAKHIAT + U+17D9: "៙" KHMER SIGN PHNAEK MUAN --> + <Key + latin:keyLabel="៍" + latin:keyHintLabel="៙" + latin:moreKeys="៙" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D0: "័" KHMER SIGN SAMYOK SANNYA + U+17DA: "៚" KHMER SIGN KOOMUUT --> + <Key + latin:keyLabel="័" + latin:keyHintLabel="៚" + latin:moreKeys="៚" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CF: "៏" KHMER SIGN AHSDA --> + <Key + latin:keyLabel="៏" + latin:keyHintLabel="*" + latin:moreKeys="*" + latin:keyLabelFlags="fontNormal" /> + <!-- U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <Key + latin:keyLabel="(" + latin:keyHintLabel="{" + latin:moreKeys="{,«" /> + <!-- U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <Key + latin:keyLabel=")" + latin:keyHintLabel="}" + latin:moreKeys="},»" /> + <!-- U+17CC: "៌" KHMER SIGN ROBAT + U+00D7: "×" MULTIPLICATION SIGN --> + <Key + latin:keyLabel="៌" + latin:keyHintLabel="×" + latin:moreKeys="×" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CE: "៎" KHMER SIGN KAKABAT --> + <Key + latin:keyLabel="៎" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+17E1: "១" KHMER DIGIT ONE + U+17F1: "៱" KHMER SYMBOL LEK ATTAK MUOY --> + <Key + latin:keyLabel="១" + latin:keyHintLabel="1" + latin:additionalMoreKeys="1" + latin:moreKeys="៱" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E2: "២" KHMER DIGIT TWO + U+17F2: "៲" KHMER SYMBOL LEK ATTAK PII --> + <Key + latin:keyLabel="២" + latin:keyHintLabel="2" + latin:additionalMoreKeys="2" + latin:moreKeys="៲" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E3: "៣" KHMER DIGIT THREE + U+17F3: "៳" KHMER SYMBOL LEK ATTAK BEI --> + <Key + latin:keyLabel="៣" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3" + latin:moreKeys="៳" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E4: "៤" KHMER DIGIT FOUR + U+17F4: "៴" KHMER SYMBOL LEK ATTAK BUON --> + <Key + latin:keyLabel="៤" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4" + latin:moreKeys="៴" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E5: "៥" KHMER DIGIT FIVE + U+17F5: "៵" KHMER SYMBOL LEK ATTAK PRAM --> + <Key + latin:keyLabel="៥" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5" + latin:moreKeys="៵" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E6: "៦" KHMER DIGIT SIX + U+17F6: "៶" KHMER SYMBOL LEK ATTAK PRAM-MUOY --> + <Key + latin:keyLabel="៦" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6" + latin:moreKeys="៶" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E7: "៧" KHMER DIGIT SEVEN + U+17F7: "៷" KHMER SYMBOL LEK ATTAK PRAM-PII --> + <Key + latin:keyLabel="៧" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7" + latin:moreKeys="៷" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E8: "៨" KHMER DIGIT EIGHT + U+17F8: "៸" KHMER SYMBOL LEK ATTAK PRAM-BEI --> + <Key + latin:keyLabel="៨" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8" + latin:moreKeys="៸" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E9: "៩" KHMER DIGIT NINE + U+17F9: "៹" KHMER SYMBOL LEK ATTAK PRAM-BUON --> + <Key + latin:keyLabel="៩" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9" + latin:moreKeys="៹" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17E0: "០" KHMER DIGIT ZERO + U+17F0: "៰" KHMER SYMBOL LEK ATTAK SON --> + <Key + latin:keyLabel="០" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0" + latin:moreKeys="៰" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A5: "ឥ" KHMER INDEPENDENT VOWEL QI + U+17A6: "ឦ" KHMER INDEPENDENT VOWEL QII --> + <Key + latin:keyLabel="ឥ" + latin:keyHintLabel="ឦ" + latin:moreKeys=",ឦ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B2: "ឲ" KHMER INDEPENDENT VOWEL QOO TYPE TWO + U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE --> + <Key + latin:keyLabel="ឲ" + latin:keyHintLabel="ឱ" + latin:moreKeys="ឱ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer2.xml b/java/res/xml/rowkeys_khmer2.xml new file mode 100644 index 000000000..cba2d3b90 --- /dev/null +++ b/java/res/xml/rowkeys_khmer2.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+1788: "ឈ" KHMER LETTER CHO + U+17DC: "ៜ" KHMER SIGN AVAKRAHASANYA --> + <Key + latin:keyLabel="ឈ" + latin:keyHintLabel="ៜ" + latin:moreKeys="ៜ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BA: "ឺ" KHMER VOWEL SIGN YY + U+17DD: "៝" KHMER SIGN ATTHACAN --> + <Key + latin:keyLabel="ឺ" + latin:keyHintLabel="៝" + latin:moreKeys="៝" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C2: "ែ" KHMER VOWEL SIGN AE --> + <Key + latin:keyLabel="ែ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AC: "ឬ" KHMER INDEPENDENT VOWEL RYY + U+17AB: "ឫ" KHMER INDEPENDENT VOWEL RY --> + <Key + latin:keyLabel="ឬ" + latin:keyHintLabel="ឫ" + latin:moreKeys="ឫ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1791: "ទ" KHMER LETTER TO --> + <Key + latin:keyLabel="ទ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BD: "ួ" KHMER VOWEL SIGN UA --> + <Key + latin:keyLabel="ួ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BC: "ូ" KHMER VOWEL SIGN UU --> + <Key + latin:keyLabel="ូ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B8: "ី" KHMER VOWEL SIGN II --> + <Key + latin:keyLabel="ី" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C5: "ៅ" KHMER VOWEL SIGN AU --> + <Key + latin:keyLabel="ៅ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1797: "ភ" KHMER LETTER PHO --> + <Key + latin:keyLabel="ភ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BF: "ឿ" KHMER VOWEL SIGN YA --> + <Key + latin:keyLabel="ឿ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI --> + <Key + latin:keyLabel="ឰ" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+1786: "ឆ" KHMER LETTER CHA --> + <Key + latin:keyLabel="ឆ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B9: "ឹ" KHMER VOWEL SIGN Y --> + <Key + latin:keyLabel="ឹ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C1: "េ" KHMER VOWEL SIGN E --> + <Key + latin:keyLabel="េ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179A: "រ" KHMER LETTER RO --> + <Key + latin:keyLabel="រ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178F: "ត" KHMER LETTER TA --> + <Key + latin:keyLabel="ត" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1799: "យ" KHMER LETTER YO --> + <Key + latin:keyLabel="យ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB: "ុ" KHMER VOWEL SIGN U --> + <Key + latin:keyLabel="ុ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17B7: "ិ" KHMER VOWEL SIGN I --> + <Key + latin:keyLabel="ិ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C4: "ោ" KHMER VOWEL SIGN OO --> + <Key + latin:keyLabel="ោ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1795: "ផ" KHMER LETTER PHA --> + <Key + latin:keyLabel="ផ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C0: "ៀ" KHMER VOWEL SIGN IE --> + <Key + latin:keyLabel="ៀ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AA: "ឪ" KHMER INDEPENDENT VOWEL QUUV + U+17A7: "ឧ" KHMER INDEPENDENT VOWEL QU + U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE + U+17B3: "ឳ" KHMER INDEPENDENT VOWEL QAU + U+17A9: "ឩ" KHMER INDEPENDENT VOWEL QUU + U+17A8: "ឨ" KHMER INDEPENDENT VOWEL QUK --> + <Key + latin:keyLabel="ឪ" + latin:keyHintLabel="ឧ" + latin:moreKeys="ឧ,ឱ,ឳ,ឩ,ឨ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer3.xml b/java/res/xml/rowkeys_khmer3.xml new file mode 100644 index 000000000..ff6c9ca51 --- /dev/null +++ b/java/res/xml/rowkeys_khmer3.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+17B6/U+17C6: "ាំ" KHMER VOWEL SIGN AA/KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ាំ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17C3: "ៃ" KHMER VOWEL SIGN AI --> + <Key + latin:keyLabel="ៃ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178C: "ឌ" KHMER LETTER DO --> + <Key + latin:keyLabel="ឌ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1792: "ធ" KHMER LETTER THO --> + <Key + latin:keyLabel="ធ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A2: "អ" KHMER LETTER QA --> + <Key + latin:keyLabel="អ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C7: "ះ" KHMER SIGN REAHMUK + U+17C8: "ៈ" KHMER SIGN YUUKALEAPINTU;--> + <Key + latin:keyLabel="ះ" + latin:keyHintLabel="ៈ" + latin:moreKeys="ៈ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1789: "ញ" KHMER LETTER NYO --> + <Key + latin:keyLabel="ញ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1782: "គ" KHMER LETTER KO + U+179D: "ឝ" KHMER LETTER SHA --> + <Key + latin:keyLabel="គ" + latin:keyHintLabel="ឝ" + latin:moreKeys="ឝ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A1: "ឡ" KHMER LETTER LA --> + <Key + latin:keyLabel="ឡ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C4/U+17C7: "ោះ" KHMER VOWEL SIGN OO/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="ោះ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C9: "៉" KHMER SIGN MUUSIKATOAN --> + <Key + latin:keyLabel="៉" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AF: "ឯ" KHMER INDEPENDENT VOWEL QE --> + <Key + latin:keyLabel="ឯ" + latin:keyLabelFlags="fontNormal" /> + </case> + <default> + <!-- U+17B6: "ា" KHMER VOWEL SIGN AA --> + <Key + latin:keyLabel="ា" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179F: "ស" KHMER LETTER SA --> + <Key + latin:keyLabel="ស" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178A: "ដ" KHMER LETTER DA --> + <Key + latin:keyLabel="ដ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1790: "ថ" KHMER LETTER THA --> + <Key + latin:keyLabel="ថ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1784: "ង" KHMER LETTER NGO --> + <Key + latin:keyLabel="ង" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17A0: "ហ" KHMER LETTER HA --> + <Key + latin:keyLabel="ហ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17D2: "្" KHMER SIGN COENG --> + <Key + latin:keyLabel="្" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1780: "ក" KHMER LETTER KA --> + <Key + latin:keyLabel="ក" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179B: "ល" KHMER LETTER LO --> + <Key + latin:keyLabel="ល" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BE: "ើ" KHMER VOWEL SIGN OE --> + <Key + latin:keyLabel="ើ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CB: "់" KHMER SIGN BANTOC --> + <Key + latin:keyLabel="់" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17AE: "ឮ" KHMER INDEPENDENT VOWEL LYY + U+17AD: "ឭ" KHMER INDEPENDENT VOWEL LY + U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI --> + <Key + latin:keyLabel="ឮ" + latin:keyHintLabel="ឭ" + latin:moreKeys="ឭ,ឰ" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rowkeys_khmer4.xml b/java/res/xml/rowkeys_khmer4.xml new file mode 100644 index 000000000..fe6c59125 --- /dev/null +++ b/java/res/xml/rowkeys_khmer4.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + > + <!-- U+178D: "ឍ" KHMER LETTER TTHO --> + <Key + latin:keyLabel="ឍ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1783: "ឃ" KHMER LETTER KHO --> + <Key + latin:keyLabel="ឃ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1787: "ជ" KHMER LETTER CO --> + <Key + latin:keyLabel="ជ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C1/U+17C7: "េះ" KHMER VOWEL SIGN E/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="េះ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1796: "ព" KHMER LETTER PO + U+179E: "ឞ" KHMER LETTER SSO --> + <Key + latin:keyLabel="ព" + latin:keyHintLabel="ឞ" + latin:moreKeys="ឞ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+178E: "ណ" KHMER LETTER NNO --> + <Key + latin:keyLabel="ណ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17C6: "ំ" KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ំ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB/U+17C7: "ុះ" KHMER VOWEL SIGN U/KHMER SIGN REAHMUK --> + <Key + latin:keyLabel="ុះ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17D5: "៕" KHMER SIGN BARIYOOSAN --> + <Key + latin:keyLabel="៕" + latin:keyLabelFlags="fontNormal" /> + <Key + latin:keyLabel="\?" /> + </case> + <default> + <!-- U+178B: "ឋ" KHMER LETTER TTHA --> + <Key + latin:keyLabel="ឋ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1781: "ខ" KHMER LETTER KHA --> + <Key + latin:keyLabel="ខ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1785: "ច" KHMER LETTER CA --> + <Key + latin:keyLabel="ច" + latin:keyLabelFlags="fontNormal" /> + <!-- U+179C: "វ" KHMER LETTER VO --> + <Key + latin:keyLabel="វ" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1794: "ប" KHMER LETTER BA --> + <Key + latin:keyLabel="ប" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1793: "ន" KHMER LETTER NO --> + <Key + latin:keyLabel="ន" + latin:keyLabelFlags="fontNormal" /> + <!-- U+1798: "ម" KHMER LETTER MO --> + <Key + latin:keyLabel="ម" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17BB/U+17C6: "ុំ" KHMER VOWEL SIGN U/KHMER SIGN NIKAHIT --> + <Key + latin:keyLabel="ុំ" + latin:keyLabelFlags="fontNormal|followKeyLetterRatio" /> + <!-- U+17D4: "។" KHMER SIGN KHAN --> + <Key + latin:keyLabel="។" + latin:keyLabelFlags="fontNormal" /> + <!-- U+17CA: "៊" KHMER SIGN TRIISAP --> + <Key + latin:keyLabel="៊" + latin:keyLabelFlags="fontNormal" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/rows_khmer.xml b/java/res/xml/rows_khmer.xml new file mode 100644 index 000000000..e3993871b --- /dev/null +++ b/java/res/xml/rows_khmer.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer1" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer2" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_khmer3" /> + </Row> + <Row + latin:keyWidth="8.3333%p" + > + <Key + latin:keyStyle="shiftKeyStyle" /> + <include + latin:keyboardLayout="@xml/rowkeys_khmer4" /> + <Key + latin:keyStyle="deleteKeyStyle" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java new file mode 100644 index 000000000..385e3e025 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java @@ -0,0 +1,46 @@ +/* + * 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.compat; + +import android.app.ActivityManager; +import android.content.Context; + +import java.lang.reflect.Method; + +public class ActivityManagerCompatUtils { + private static final Object LOCK = new Object(); + private static volatile Boolean sBoolean = null; + private static final Method METHOD_isLowRamDevice = CompatUtils.getMethod( + ActivityManager.class, "isLowRamDevice"); + + private ActivityManagerCompatUtils() { + // Do not instantiate this class. + } + + public static boolean isLowRamDevice(Context context) { + if (sBoolean == null) { + synchronized(LOCK) { + if (sBoolean == null) { + final ActivityManager am = + (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + sBoolean = (Boolean)CompatUtils.invoke(am, false, METHOD_isLowRamDevice); + } + } + } + return sBoolean; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java new file mode 100644 index 000000000..fed134eb9 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java @@ -0,0 +1,67 @@ +/* + * 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 com.android.inputmethod.latin.R; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +public class EmojiCategoryPageIndicatorView extends LinearLayout { + private static final float BOTTOM_MARGIN_RATIO = 0.66f; + private final Paint mPaint = new Paint(); + private int mCategoryPageSize = 0; + private int mCurrentCategoryPageId = 0; + private float mOffset = 0.0f; + + public EmojiCategoryPageIndicatorView(Context context) { + this(context, null /* attrs */); + } + + public EmojiCategoryPageIndicatorView(Context context, AttributeSet attrs) { + super(context, attrs); + mPaint.setColor(context.getResources().getColor( + R.color.emoji_category_page_id_view_foreground)); + } + + public void setCategoryPageId(int size, int id, float offset) { + mCategoryPageSize = size; + mCurrentCategoryPageId = id; + mOffset = offset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + if (mCategoryPageSize == 0) { + // If the category is not set yet, just clear and return. + canvas.drawColor(0); + return; + } + final float height = getHeight(); + final float width = getWidth(); + final float unitWidth = width / mCategoryPageSize; + final float left = unitWidth * mCurrentCategoryPageId + mOffset * unitWidth; + final float top = 0.0f; + final float right = left + unitWidth; + final float bottom = height * BOTTOM_MARGIN_RATIO; + canvas.drawRect(left, top, right, bottom, mPaint); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java index 702ed2075..4e61edac2 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java @@ -28,11 +28,13 @@ import android.os.Build; import android.preference.PreferenceManager; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; +import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -76,10 +78,12 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange private final int mEmojiFunctionalKeyBackgroundId; private final KeyboardLayoutSet mLayoutSet; private final ColorStateList mTabLabelColor; + private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener; private EmojiKeyboardAdapter mEmojiKeyboardAdapter; private TabHost mTabHost; private ViewPager mEmojiPager; + private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView; private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; @@ -197,6 +201,21 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange return mCurrentCategoryId; } + public int getCurrentCategoryPageSize() { + return getCategoryPageSize(mCurrentCategoryId); + } + + public int getCategoryPageSize(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(int categoryId) { mCurrentCategoryId = categoryId; } @@ -205,6 +224,10 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mCurrentCategoryPageId = id; } + public int getCurrentCategoryPageId() { + return mCurrentCategoryPageId; + } + public void saveLastTypedCategoryPage() { Settings.writeEmojiCategoryLastTypedId( mPrefs, mCurrentCategoryId, mCurrentCategoryPageId); @@ -382,6 +405,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mLayoutSet = builder.build(); mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context), context.getResources(), builder.build()); + mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context); } @Override @@ -435,18 +459,20 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mEmojiPager.setOffscreenPageLimit(0); final Resources res = getResources(); final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); - emojiLp.setPagerProps(mEmojiPager); + emojiLp.setPagerProperties(mEmojiPager); + + mEmojiCategoryPageIndicatorView = + (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view); + emojiLp.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); - emojiLp.setActionBarProps(actionBar); + emojiLp.setActionBarProperties(actionBar); - // TODO: Implement auto repeat, using View.OnTouchListener? final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete); - deleteKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); deleteKey.setTag(Constants.CODE_DELETE); - deleteKey.setOnClickListener(this); + deleteKey.setOnTouchListener(mDeleteKeyOnTouchListener); final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet); alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL); @@ -455,7 +481,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange spaceKey.setBackgroundResource(mKeyBackgroundId); spaceKey.setTag(Constants.CODE_SPACE); spaceKey.setOnClickListener(this); - emojiLp.setKeyProps(spaceKey); + emojiLp.setKeyProperties(spaceKey); final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send); sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); sendKey.setTag(Constants.CODE_ENTER); @@ -466,6 +492,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange public void onTabChanged(final String tabId) { final int categoryId = mEmojiCategory.getCategoryId(tabId); setCurrentCategoryId(categoryId, false /* force */); + updateEmojiCategoryPageIdView(); } @@ -475,6 +502,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position); setCurrentCategoryId(newPos.first /* categoryId */, false /* force */); mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */); + updateEmojiCategoryPageIdView(); } @Override @@ -485,7 +513,23 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { - // Ignore this message. Only want the actual page selected. + 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); + } } @Override @@ -505,10 +549,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange @Override public void onKeyClick(final Key key) { - // TODO: Save emoticons to recents - if (mEmojiCategory.getCurrentCategoryId() != CATEGORY_ID_EMOTICONS) { - mEmojiKeyboardAdapter.addRecentKey(key); - } + mEmojiKeyboardAdapter.addRecentKey(key); mEmojiCategory.saveLastTypedCategoryPage(); final int code = key.getCode(); if (code == Constants.CODE_OUTPUT_TEXT) { @@ -524,6 +565,16 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange 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) { @@ -624,4 +675,92 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange container.removeView(keyboardView); } } + + // TODO: Do the same things done in PointerTracker + private static class DeleteKeyOnTouchListener implements OnTouchListener { + private static final long MAX_REPEAT_COUNT_TIME = 30 * DateUtils.SECOND_IN_MILLIS; + 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); + } + + private KeyboardActionListener mKeyboardActionListener = + KeyboardActionListener.EMPTY_LISTENER; + private DummyRepeatKeyRepeatTimer mTimer; + + private synchronized void startRepeat() { + if (mTimer != null) { + abortRepeat(); + } + mTimer = new DummyRepeatKeyRepeatTimer(); + mTimer.start(); + } + + private synchronized void abortRepeat() { + mTimer.abort(); + mTimer = null; + } + + // TODO: Remove + // This function is mimicking the repeat code in PointerTracker. + // Specifically referring to PointerTracker#startRepeatKey and PointerTracker#onKeyRepeat. + private class DummyRepeatKeyRepeatTimer extends Thread { + public boolean mAborted = false; + + @Override + public void run() { + int timeCount = 0; + while (timeCount < MAX_REPEAT_COUNT_TIME && !mAborted) { + if (timeCount > mKeyRepeatStartTimeout) { + pressDelete(); + } + timeCount += mKeyRepeatInterval; + try { + Thread.sleep(mKeyRepeatInterval); + } catch (InterruptedException e) { + } + } + } + + public void abort() { + mAborted = true; + } + } + + public void pressDelete() { + mKeyboardActionListener.onPressKey( + Constants.CODE_DELETE, 0 /* repeatCount */, true /* isSinglePointer */); + mKeyboardActionListener.onCodeInput( + Constants.CODE_DELETE, NOT_A_COORDINATE, NOT_A_COORDINATE); + mKeyboardActionListener.onReleaseKey( + Constants.CODE_DELETE, false /* withSliding */); + } + + public void setKeyboardActionListener(KeyboardActionListener listener) { + mKeyboardActionListener = listener; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch(event.getAction()) { + case MotionEvent.ACTION_DOWN: + v.setBackgroundColor(mDeleteKeyPressedBackgroundColor); + pressDelete(); + startRepeat(); + return true; + case MotionEvent.ACTION_UP: + v.setBackgroundColor(0); + abortRepeat(); + return true; + } + return false; + } + } } diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java index 5570d594d..267fad5cd 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java @@ -30,6 +30,7 @@ public class EmojiLayoutParams { public final int mEmojiPagerHeight; private final int mEmojiPagerBottomMargin; public final int mEmojiKeyboardHeight; + private final int mEmojiCategoryPageIdViewHeight; public final int mEmojiActionBarHeight; public final int mKeyVerticalGap; private final int mKeyHorizontalGap; @@ -47,23 +48,32 @@ public class EmojiLayoutParams { (int) defaultKeyboardHeight, (int) defaultKeyboardHeight); mKeyHorizontalGap = (int) (res.getFraction(R.fraction.key_horizontal_gap_ics, defaultKeyboardWidth, defaultKeyboardWidth)); + mEmojiCategoryPageIdViewHeight = + (int) (res.getDimension(R.dimen.emoji_category_page_id_height)); final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding + mKeyVerticalGap; mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS - (mKeyVerticalGap - mBottomPadding) / 2; - mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight; + mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight + - mEmojiCategoryPageIdViewHeight; mEmojiPagerBottomMargin = mKeyVerticalGap / 2; mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1; } - public void setPagerProps(ViewPager vp) { + public void setPagerProperties(ViewPager vp) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams(); - lp.height = mEmojiPagerHeight - mEmojiPagerBottomMargin; + lp.height = mEmojiKeyboardHeight; lp.bottomMargin = mEmojiPagerBottomMargin; vp.setLayoutParams(lp); } - public void setActionBarProps(LinearLayout ll) { + public void setCategoryPageIdViewProperties(LinearLayout ll) { + final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); + lp.height = mEmojiCategoryPageIdViewHeight; + ll.setLayoutParams(lp); + } + + public void setActionBarProperties(LinearLayout ll) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.height = mEmojiActionBarHeight; lp.topMargin = 0; @@ -71,7 +81,7 @@ public class EmojiLayoutParams { ll.setLayoutParams(lp); } - public void setKeyProps(ImageView ib) { + public void setKeyProperties(ImageView ib) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ib.getLayoutParams(); lp.leftMargin = mKeyHorizontalGap / 2; lp.rightMargin = mKeyHorizontalGap / 2; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 23f037fbd..bc1383aff 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -155,6 +155,15 @@ public class Keyboard { return mKeys; } + public Key getKeyFromOutputText(final String outputText) { + for (final Key key : getKeys()) { + if (outputText.equals(key.getOutputText())) { + return key; + } + } + return null; + } + public Key getKey(final int code) { if (code == Constants.CODE_UNSPECIFIED) { return null; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 0ef6802ca..aeb9e67b2 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -26,6 +26,7 @@ import android.graphics.Paint.Align; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; @@ -445,6 +446,8 @@ public class KeyboardView extends View { if (hintLabel != null) { paint.setTextSize(key.selectHintTextSize(params)); paint.setColor(key.selectHintTextColor(params)); + // TODO: Should add a way to specify type face for hint letters + paint.setTypeface(Typeface.DEFAULT_BOLD); blendAlpha(paint, params.mAnimAlpha); final float hintX, hintY; if (key.hasHintLabel()) { @@ -465,9 +468,13 @@ public class KeyboardView extends View { paint.setTextAlign(Align.CENTER); } else { // key.hasHintLetter() // The hint letter is placed at top-right corner of the key. Used mainly on phone. + final float keyNumericHintLabelReferenceCharWidth = + TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint); + final float keyHintLabelStringWidth = + TypefaceUtils.getStringWidth(hintLabel, paint); hintX = keyWidth - mKeyHintLetterPadding - - TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint) - / 2.0f; + - Math.max(keyNumericHintLabelReferenceCharWidth, keyHintLabelStringWidth) + / 2.0f; hintY = -paint.ascent(); paint.setTextAlign(Align.CENTER); } diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index f8ad43e74..13db47004 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -155,7 +155,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final SlidingKeyInputPreview mSlidingKeyInputPreview; // Key preview - private static final int PREVIEW_ALPHA = 240; private final int mKeyPreviewLayoutId; private final int mKeyPreviewOffset; private final int mKeyPreviewHeight; @@ -816,7 +815,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (background != null) { final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL; background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); - background.setAlpha(PREVIEW_ALPHA); } ViewLayoutUtils.placeViewAt( previewText, previewX, previewY, previewWidth, previewHeight); diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index cd127c760..a0316696c 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -245,8 +245,8 @@ public class ProximityInfo { final int threshold = (int) (defaultWidth * SEARCH_DISTANCE); final int thresholdSquared = threshold * threshold; // Round-up so we don't have any pixels outside the grid - final int fullGridWidth = mGridWidth * mCellWidth; - final int fullGridHeight = mGridHeight * mCellHeight; + final int lastPixelXCoordinate = mGridWidth * mCellWidth - 1; + final int lastPixelYCoordinate = mGridHeight * mCellHeight - 1; // For large layouts, 'neighborsFlatBuffer' is about 80k of memory: gridSize is usually 512, // keycount is about 40 and a pointer to a Key is 4 bytes. This contains, for each cell, @@ -329,22 +329,20 @@ y |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get final int yMiddleOfTopCell = topPixelWithinThreshold - yDeltaToGrid + halfCellHeight; final int yStart = Math.max(halfCellHeight, yMiddleOfTopCell + (yDeltaToGrid <= halfCellHeight ? 0 : mCellHeight)); - final int yEnd = Math.min(fullGridHeight, keyY + key.getHeight() + threshold); + final int yEnd = Math.min(lastPixelYCoordinate, keyY + key.getHeight() + threshold); final int leftPixelWithinThreshold = keyX - threshold; final int xDeltaToGrid = leftPixelWithinThreshold % mCellWidth; final int xMiddleOfLeftCell = leftPixelWithinThreshold - xDeltaToGrid + halfCellWidth; final int xStart = Math.max(halfCellWidth, xMiddleOfLeftCell + (xDeltaToGrid <= halfCellWidth ? 0 : mCellWidth)); - final int xEnd = Math.min(fullGridWidth, keyX + key.getWidth() + threshold); + final int xEnd = Math.min(lastPixelXCoordinate, keyX + key.getWidth() + threshold); int baseIndexOfCurrentRow = (yStart / mCellHeight) * mGridWidth + (xStart / mCellWidth); for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) { int index = baseIndexOfCurrentRow; for (int centerX = xStart; centerX <= xEnd; centerX += mCellWidth) { - // TODO: Remove "index < neighborCountPerCell.length" below. - if (index < neighborCountPerCell.length - && key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { + if (key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { neighborsFlatBuffer[index * keyCount + neighborCountPerCell[index]] = key; ++neighborCountPerCell[index]; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java index f203eb7d7..0dd71e2ec 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java @@ -18,25 +18,27 @@ package com.android.inputmethod.keyboard.internal; import android.content.SharedPreferences; import android.text.TextUtils; +import android.util.Log; import com.android.inputmethod.keyboard.EmojiKeyboardView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.Collection; +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 { + 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; - // Recent codes are saved as an integer array, so we use comma as a separater. - private static final String RECENT_KEY_SEPARATOR = Constants.STRING_COMMA; private final SharedPreferences mPrefs; private final int mLeftPadding; @@ -84,6 +86,9 @@ public class DynamicGridKeyboard extends Keyboard { } private void addKey(final Key usedKey, final boolean addFirst) { + if (usedKey == null) { + return; + } synchronized (mGridKeys) { mCachedGridKeys = null; final GridKey key = new GridKey(usedKey); @@ -109,28 +114,45 @@ public class DynamicGridKeyboard extends Keyboard { } private void saveRecentKeys() { - final StringBuilder sb = new StringBuilder(); + final ArrayList<Object> keys = CollectionUtils.newArrayList(); for (final Key key : mGridKeys) { - sb.append(key.getCode()).append(RECENT_KEY_SEPARATOR); + if (key.getOutputText() != null) { + keys.add(key.getOutputText()); + } else { + keys.add(key.getCode()); + } } - Settings.writeEmojiRecentKeys(mPrefs, sb.toString()); + final String jsonStr = StringUtils.listToJsonStr(keys); + Settings.writeEmojiRecentKeys(mPrefs, jsonStr); } - public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) { - final String str = Settings.readEmojiRecentKeys(mPrefs); - for (String s : str.split(RECENT_KEY_SEPARATOR)) { - if (TextUtils.isEmpty(s)) { - continue; - } - final int code = Integer.valueOf(s); - for (DynamicGridKeyboard kbd : keyboards) { + private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) { + for (final DynamicGridKeyboard kbd : keyboards) { + if (o instanceof Integer) { + final int code = (Integer) o; final Key key = kbd.getKey(code); if (key != null) { - addKeyLast(key); - break; + return key; + } + } else if (o instanceof String) { + final String outputText = (String) o; + final Key key = kbd.getKeyFromOutputText(outputText); + if (key != null) { + return key; } + } else { + Log.w(TAG, "Invalid object: " + o); } } + return null; + } + + public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) { + final String str = Settings.readEmojiRecentKeys(mPrefs); + final List<Object> keys = StringUtils.jsonStrToList(str); + for (final Object o : keys) { + addKeyLast(getKey(keyboards, o)); + } } private int getKeyX(final int index) { @@ -171,7 +193,7 @@ public class DynamicGridKeyboard extends Keyboard { public void updateCorrdinates(final int x, final int y) { mCurrentX = x; mCurrentY = y; - getHitBox().offsetTo(x, y); + getHitBox().set(x, y, x + getWidth(), y + getHeight()); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index a72595f7c..67553fb75 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -1769,15 +1769,27 @@ public final class KeyboardTextsSet { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~52 */ + // U+058A: "֊" ARMENIAN HYPHEN + // U+055C: "՜" ARMENIAN EXCLAMATION MARK + // U+055D: "՝" ARMENIAN COMMA // U+055E: "՞" ARMENIAN QUESTION MARK - /* 53 */ "!fixedColumnOrder!4,\u055E,!,\\,,?,:,;,@", + // U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING + // U+055A: "՚" ARMENIAN APOSTROPHE + // U+055B: "՛" ARMENIAN EMPHASIS MARK + // U+055F: "՟" ARMENIAN ABBREVIATION MARK + /* 53 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F", /* 54~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, - /* ~107 */ - /* 108 */ "\u055E,?", + null, + /* ~99 */ + // U+055C: "՜" ARMENIAN EXCLAMATION MARK + // U+00A1: "¡" INVERTED EXCLAMATION MARK + /* 100 */ "\u055C,\u00A1", + // U+055E: "՞" ARMENIAN QUESTION MARK + // U+00BF: "¿" INVERTED QUESTION MARK + /* 101 */ "\u055E,\u00BF", }; /* Language is: Icelandic */ @@ -2015,6 +2027,25 @@ public final class KeyboardTextsSet { /* 45 */ "\u0410\u0411\u0412", }; + /* Language km: Khmer */ + private static final String[] LANGUAGE_km = { + /* 0~ */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~44 */ + // Label for "switch to alphabetic" key. + // U+1780: "ក" KHMER LETTER KA + // U+1781: "ខ" KHMER LETTER KHA + // U+1782: "គ" KHMER LETTER KO + /* 45 */ "\u1780\u1781\u1782", + /* 46~ */ + null, null, null, null, + /* ~49 */ + // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL + /* 50 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + }; + /* Language ky: Kirghiz */ private static final String[] LANGUAGE_ky = { /* 0~ */ @@ -3407,6 +3438,7 @@ public final class KeyboardTextsSet { "iw", LANGUAGE_iw, /* Hebrew */ "ka", LANGUAGE_ka, /* Georgian */ "kk", LANGUAGE_kk, /* Kazakh */ + "km", LANGUAGE_km, /* Khmer */ "ky", LANGUAGE_ky, /* Kirghiz */ "lo", LANGUAGE_lo, /* Lao */ "lt", LANGUAGE_lt, /* Lithuanian */ diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java index 55df263fe..845a9b987 100644 --- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java @@ -58,7 +58,7 @@ abstract public class AbstractDictionaryWriter extends Dictionary { final File file = new File(mContext.getFilesDir(), fileName); final File tempFile = new File(mContext.getFilesDir(), tempFileName); try { - final DictEncoder dictEncoder = new Ver3DictEncoder(file); + final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile); writeDictionary(dictEncoder); tempFile.renameTo(file); } catch (IOException e) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b49cd80ab..61ccfcfad 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -27,6 +27,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.StringUtils; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Locale; @@ -109,7 +110,7 @@ public final class BinaryDictionary extends Dictionary { private static native void flushWithGCNative(long dict, String filePath); private static native void closeNative(long dict); private static native int getProbabilityNative(long dict, int[] word); - private static native boolean isValidBigramNative(long dict, int[] word0, int[] word1); + private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1); private static native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint, @@ -122,6 +123,8 @@ public final class BinaryDictionary extends Dictionary { private static native void addBigramWordsNative(long dict, int[] word0, int[] word1, int probability); private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1); + private static native int calculateProbabilityNative(long dict, int unigramProbability, + int bigramProbability); // TODO: Move native dict into session private final void loadDictionary(final String path, final long startOffset, @@ -219,12 +222,12 @@ public final class BinaryDictionary extends Dictionary { @Override public boolean isValidWord(final String word) { - return getFrequency(word) >= 0; + return getFrequency(word) != NOT_A_PROBABILITY; } @Override public int getFrequency(final String word) { - if (word == null) return -1; + if (word == null) return NOT_A_PROBABILITY; int[] codePoints = StringUtils.toCodePointArray(word); return getProbabilityNative(mNativeDict, codePoints); } @@ -232,10 +235,20 @@ public final class BinaryDictionary extends Dictionary { // 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) { - if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return false; + return getBigramProbability(word0, word1) != NOT_A_PROBABILITY; + } + + 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 isValidBigramNative(mNativeDict, codePoints0, codePoints1); + return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1); + } + + private void runGCIfRequired() { + if (needsToRunGCNative(mNativeDict)) { + flushWithGC(); + } } // Add a unigram entry to binary dictionary in native code. @@ -243,6 +256,7 @@ public final class BinaryDictionary extends Dictionary { if (TextUtils.isEmpty(word)) { return; } + runGCIfRequired(); final int[] codePoints = StringUtils.toCodePointArray(word); addUnigramWordNative(mNativeDict, codePoints, probability); } @@ -252,6 +266,7 @@ public final class BinaryDictionary extends Dictionary { if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { return; } + runGCIfRequired(); final int[] codePoints0 = StringUtils.toCodePointArray(word0); final int[] codePoints1 = StringUtils.toCodePointArray(word1); addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability); @@ -262,29 +277,41 @@ public final class BinaryDictionary extends Dictionary { if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { return; } + runGCIfRequired(); final int[] codePoints0 = StringUtils.toCodePointArray(word0); final int[] codePoints1 = StringUtils.toCodePointArray(word1); removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); } - @UsedForTesting public void flush() { if (!isValidDictionary()) return; flushNative(mNativeDict, mDictFilePath); + closeNative(mNativeDict); + final File dictFile = new File(mDictFilePath); + mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */, + dictFile.length(), true /* isUpdatable */); } - @UsedForTesting public void flushWithGC() { if (!isValidDictionary()) return; flushWithGCNative(mNativeDict, mDictFilePath); + closeNative(mNativeDict); + final File dictFile = new File(mDictFilePath); + mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */, + dictFile.length(), true /* isUpdatable */); } - @UsedForTesting public boolean needsToRunGC() { if (!isValidDictionary()) return false; return needsToRunGCNative(mNativeDict); } + @UsedForTesting + public int calculateProbability(final int unigramProbability, final int bigramProbability) { + if (!isValidDictionary()) return NOT_A_PROBABILITY; + return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability); + } + @Override public boolean shouldAutoCommit(final SuggestedWordInfo candidate) { // TODO: actually use the confidence rather than use this completely broken heuristic diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 566184244..181ad17ea 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -286,7 +286,8 @@ final public class BinaryDictionaryGetter { } if (!dictPackSettings.isWordListActive(wordListId)) continue; if (canUse) { - fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); + final AssetFileAddress afa = AssetFileAddress.makeFromFileName(f.getPath()); + if (null != afa) fileList.add(afa); } else { Log.e(TAG, "Found a cached dictionary file but cannot read or use it"); } diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 029ba02ed..c4f96016c 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -76,6 +76,11 @@ public final class Constants { public static final String ASCII_CAPABLE = "AsciiCapable"; /** + * The subtype extra value used to indicate that the subtype keyboard layout is capable + * for typing EMOJI characters. + */ + public static final String EMOJI_CAPABLE = "EmojiCapable"; + /** * The subtype extra value used to indicate that the subtype require network connection * to work. */ @@ -133,6 +138,9 @@ public final class Constants { public static final int SPELL_CHECKER_COORDINATE = -3; public static final int EXTERNAL_KEYBOARD_COORDINATE = -4; + // 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; // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h public static final int DICTIONARY_MAX_WORD_LENGTH = 48; @@ -157,6 +165,7 @@ public final class Constants { public static final int CODE_TAB = '\t'; public static final int CODE_SPACE = ' '; public static final int CODE_PERIOD = '.'; + 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 = '"'; @@ -164,9 +173,7 @@ public final class Constants { public static final int CODE_EXCLAMATION_MARK = '!'; public static final int CODE_SLASH = '/'; public static final int CODE_COMMERCIAL_AT = '@'; - // TODO: Check how this should work for right-to-left languages. It seems to stand - // that for rtl languages, a closing parenthesis is a left parenthesis. Is this - // managed by the font? Or is it a different char? + public static final int CODE_PLUS = '+'; public static final int CODE_CLOSING_PARENTHESIS = ')'; public static final int CODE_CLOSING_SQUARE_BRACKET = ']'; public static final int CODE_CLOSING_CURLY_BRACKET = '}'; @@ -221,7 +228,6 @@ public final class Constants { } public static final int MAX_INT_BIT_COUNT = 32; - public static final String STRING_COMMA = ","; private Constants() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 67eb7f3dd..ffeb92784 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -22,6 +22,7 @@ import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.SystemClock; import android.provider.BaseColumns; @@ -145,8 +146,10 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { cursor.close(); } } - } catch (IllegalStateException e) { - Log.e(TAG, "Contacts DB is having problems"); + } catch (final SQLiteException e) { + Log.e(TAG, "SQLiteException in the remote Contacts process.", e); + } catch (final IllegalStateException e) { + Log.e(TAG, "Contacts DB is having problems", e); } } @@ -173,14 +176,18 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private int getContactCount() { // TODO: consider switching to a rawQuery("select count(*)...") on the database if // performance is a bottleneck. - final Cursor cursor = mContext.getContentResolver().query( - Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null); - if (cursor != null) { - try { - return cursor.getCount(); - } finally { - cursor.close(); + try { + final Cursor cursor = mContext.getContentResolver().query( + Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null); + if (cursor != null) { + try { + return cursor.getCount(); + } finally { + cursor.close(); + } } + } catch (final SQLiteException e) { + Log.e(TAG, "SQLiteException in the remote Contacts process.", e); } return 0; } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 2a9076436..0774ce203 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -22,14 +22,22 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.makedict.DictEncoder; +import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FusionDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.makedict.Ver3DictEncoder; import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; import java.io.File; +import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; @@ -49,9 +57,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Whether to print debug output to log */ private static boolean DEBUG = false; - // TODO: Remove and enable dynamic update in native code. + // TODO: Remove. /** Whether to call binary dictionary dynamically updating methods. */ - private static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = false; + public static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = true; private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; @@ -60,6 +68,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; + private static final FormatSpec.FormatOptions FORMAT_OPTIONS = + new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */); + /** * A static map of time recorders, each of which records the time of accesses to a single binary * dictionary file. The key for this map is the filename and the value is the shared dictionary @@ -154,7 +165,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private static AbstractDictionaryWriter getDictionaryWriter(final Context context, final String dictType, final boolean isDynamicPersonalizationDictionary) { if (isDynamicPersonalizationDictionary) { - return new DynamicPersonalizationDictionaryWriter(context, dictType); + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + return null; + } else { + return new DynamicPersonalizationDictionaryWriter(context, dictType); + } } else { return new DictionaryWriter(context, dictType); } @@ -198,7 +213,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mBinaryDictionary.close(); mBinaryDictionary = null; } - mDictionaryWriter.close(); + if (mDictionaryWriter != null) { + mDictionaryWriter.close(); + } } }); } @@ -220,7 +237,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { getExecutor(mFilename).execute(new Runnable() { @Override public void run() { - mDictionaryWriter.clear(); + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) { + mBinaryDictionary.close(); + final File file = new File(mContext.getFilesDir(), mFilename); + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), + false, false)); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + try { + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + } catch (IOException e) { + Log.e(TAG, "Exception in creating new dictionary file.", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Exception in creating new dictionary file.", e); + } + } else { + mDictionaryWriter.clear(); + } } }); } @@ -257,9 +290,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { mBinaryDictionary.addUnigramWord(word, frequency); + } else { + // TODO: Remove. + mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); } - // TODO: Remove. - mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); } }); } @@ -280,10 +314,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { mBinaryDictionary.addBigramWords(word0, word1, frequency); + } else { + // TODO: Remove. + mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid, + 0 /* lastTouchedTime */); } - // TODO: Remove. - mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid, - 0 /* lastTouchedTime */); } }); } @@ -303,17 +338,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void run() { if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { mBinaryDictionary.removeBigramWords(word0, word1); + } else { + // TODO: Remove. + mDictionaryWriter.removeBigramWords(word0, word1); } - // TODO: Remove. - mDictionaryWriter.removeBigramWords(word0, word1); } }); } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, + final int sessionId) { reloadDictionaryIfRequired(); final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = @@ -321,32 +358,54 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { getExecutor(mFilename).executePrioritized(new Runnable() { @Override public void run() { - final ArrayList<SuggestedWordInfo> inMemDictSuggestion = - mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions); - // TODO: Remove checking mIsUpdatable and use native suggestion. - if (mBinaryDictionary != null && !mIsUpdatable) { + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + if (mBinaryDictionary == null) { + holder.set(null); + return; + } final ArrayList<SuggestedWordInfo> binarySuggestion = - mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions); - if (inMemDictSuggestion == null) { - holder.set(binarySuggestion); - } else if (binarySuggestion == null) { - holder.set(inMemDictSuggestion); + mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, + proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId); + holder.set(binarySuggestion); + } else { + final ArrayList<SuggestedWordInfo> inMemDictSuggestion = + composer.isBatchMode() ? null : + mDictionaryWriter.getSuggestionsWithSessionId(composer, + prevWord, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, sessionId); + // TODO: Remove checking mIsUpdatable and use native suggestion. + if (mBinaryDictionary != null && !mIsUpdatable) { + final ArrayList<SuggestedWordInfo> binarySuggestion = + mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, + proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, sessionId); + if (inMemDictSuggestion == null) { + holder.set(binarySuggestion); + } else if (binarySuggestion == null) { + holder.set(inMemDictSuggestion); + } else { + binarySuggestion.addAll(inMemDictSuggestion); + holder.set(binarySuggestion); + } } else { - binarySuggestion.addAll(inMemDictSuggestion); - holder.set(binarySuggestion); + holder.set(inMemDictSuggestion); } - } else { - holder.set(inMemDictSuggestion); } } }); - return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); } @Override + public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, + final String prevWord, final ProximityInfo proximityInfo, + final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { + return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, + additionalFeaturesOptions, 0 /* sessionId */); + } + + @Override public boolean isValidWord(final String word) { reloadDictionaryIfRequired(); return isValidWordInner(word); @@ -401,8 +460,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable); - // Ensure all threads accessing the current dictionary have finished before swapping in - // the new one. + // Ensure all threads accessing the current dictionary have finished before + // swapping in the new one. + // TODO: Ensure multi-thread assignment of mBinaryDictionary. final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; getExecutor(mFilename).executePrioritized(new Runnable() { @Override @@ -433,8 +493,33 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { if (needsToReloadBeforeWriting()) { mDictionaryWriter.clear(); loadDictionaryAsync(); + mDictionaryWriter.write(mFilename); + } else { + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) { + final File file = new File(mContext.getFilesDir(), mFilename); + final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), + new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), + false, false)); + final DictEncoder dictEncoder = new Ver3DictEncoder(file); + try { + dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + } catch (IOException e) { + Log.e(TAG, "Exception in creating new dictionary file.", e); + } catch (UnsupportedFormatException e) { + Log.e(TAG, "Exception in creating new dictionary file.", e); + } + } else { + if (mBinaryDictionary.needsToRunGC()) { + mBinaryDictionary.flushWithGC(); + } else { + mBinaryDictionary.flush(); + } + } + } else { + mDictionaryWriter.write(mFilename); + } } - mDictionaryWriter.write(mFilename); } /** @@ -529,7 +614,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { getExecutor(mFilename).executePrioritized(new Runnable() { @Override public void run() { - loadDictionaryAsync(); + if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + loadDictionaryAsync(); + } } }); } @@ -537,7 +624,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Generate binary dictionary using DictionaryWriter. */ - protected void asyncWriteBinaryDictionary() { + protected void asyncFlashAllBinaryDictionary() { final Runnable newTask = new Runnable() { @Override public void run() { @@ -610,8 +697,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Override public void run() { if (mDictType == Dictionary.TYPE_USER_HISTORY) { - holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter) - .isInDictionaryForTests(word)); + if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + holder.set(mBinaryDictionary.isValidWord(word)); + } else { + holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter) + .isInDictionaryForTests(word)); + } } } }); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d3a18d410..270dc4c06 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -46,6 +46,7 @@ import android.text.InputType; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.Log; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.KeyCharacterMap; @@ -232,10 +233,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_RESUME_SUGGESTIONS = 4; private static final int MSG_REOPEN_DICTIONARIES = 5; private static final int MSG_ON_END_BATCH_INPUT = 6; + private static final int MSG_RESET_CACHES = 7; private static final int ARG1_NOT_GESTURE_INPUT = 0; 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_WITHOUT_TYPED_WORD = 0; + private static final int ARG2_WITH_TYPED_WORD = 1; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -269,7 +273,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) { - latinIme.showSuggestionStrip((SuggestedWords) msg.obj); + if (msg.arg2 == ARG2_WITH_TYPED_WORD) { + final Pair<SuggestedWords, String> p = + (Pair<SuggestedWords, String>) msg.obj; + latinIme.showSuggestionStripWithTypedWord(p.first, p.second); + } else { + latinIme.showSuggestionStrip((SuggestedWords) msg.obj); + } } else { latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj, msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); @@ -288,6 +298,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_ON_END_BATCH_INPUT: latinIme.onEndBatchInputAsyncInternal((SuggestedWords) msg.obj); break; + case MSG_RESET_CACHES: + latinIme.retryResetCaches(msg.arg1 == 1 /* tryResumeSuggestions */, + msg.arg2 /* remainingTries */); + break; } } @@ -304,6 +318,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); } + public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { + removeMessages(MSG_RESET_CACHES); + sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0, + remainingTries, null)); + } + public void cancelUpdateSuggestionStrip() { removeMessages(MSG_UPDATE_SUGGESTION_STRIP); } @@ -331,14 +351,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int arg1 = dismissGestureFloatingPreviewText ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT; - obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords) - .sendToTarget(); + obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, + ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); } public void showSuggestionStrip(final SuggestedWords suggestedWords) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, - ARG1_NOT_GESTURE_INPUT, 0, suggestedWords).sendToTarget(); + ARG1_NOT_GESTURE_INPUT, ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); + } + + // TODO: Remove this method. + public void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords, + final String typedWord) { + removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); + obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, ARG1_NOT_GESTURE_INPUT, + ARG2_WITH_TYPED_WORD, + new Pair<SuggestedWords, String>(suggestedWords, typedWord)).sendToTarget(); } public void onEndBatchInput(final SuggestedWords suggestedWords) { @@ -834,7 +863,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // span, so we should reset our state unconditionally, even if restarting is true. mEnteredText = null; resetComposingState(true /* alsoResetLastComposedWord */); - if (isDifferentTextField) mHandler.postResumeSuggestions(); mDeleteCount = 0; mSpaceState = SPACE_STATE_NONE; mRecapitalizeStatus.deactivate(); @@ -853,8 +881,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mSuggestedWords = SuggestedWords.EMPTY; - mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart, - false /* shouldFinishComposition */); + // Sometimes, while rotating, for some reason the framework tells the app we are not + // connected to it and that means we can't refresh the cache. In this case, schedule a + // refresh later. + if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart, + false /* shouldFinishComposition */)) { + // We try resetting the caches up to 5 times before giving up. + mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */); + } else { + if (isDifferentTextField) mHandler.postResumeSuggestions(); + } if (isDifferentTextField) { mainKeyboardView.closing(); @@ -881,6 +917,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastSelectionStart = editorInfo.initialSelStart; mLastSelectionEnd = editorInfo.initialSelEnd; + // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying + // so we try using some heuristics to find out about these and fix them. + tryFixLyingCursorPosition(); mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacePeriodTimer(); @@ -900,6 +939,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } + /** + * Try to get the text from the editor to expose lies the framework may have been + * telling us. Concretely, when the device rotates, the frameworks tells us about where the + * cursor used to be initially in the editor at the time it first received the focus; this + * may be completely different from the place it is upon rotation. Since we don't have any + * means to get the real value, try at least to ask the text view for some characters and + * detect the most damaging cases: when the cursor position is declared to be much smaller + * than it really is. + */ + private void tryFixLyingCursorPosition() { + final CharSequence textBeforeCursor = + mConnection.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); + if (null == textBeforeCursor) { + mLastSelectionStart = mLastSelectionEnd = NOT_A_CURSOR_POSITION; + } else { + final int textLength = textBeforeCursor.length(); + if (textLength > mLastSelectionStart + || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE + && mLastSelectionStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { + mLastSelectionStart = textLength; + // We can't figure out the value of mLastSelectionEnd :( + // But at least if it's smaller than mLastSelectionStart something is wrong + if (mLastSelectionStart > mLastSelectionEnd) { + mLastSelectionEnd = mLastSelectionStart; + } + } + } + } + // Initialization of personalization debug settings. This must be called inside // onStartInputView. private void initPersonalizationDebugSettings(SettingsValues currentSettingsValues) { @@ -1054,7 +1122,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // argument as true. But in all cases where we don't reset the entire input state, // we still want to tell the rich input connection about the new cursor position so // that it can update its caches. - mConnection.resetCachesUponCursorMove(newSelStart, + mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, false /* shouldFinishComposition */); } @@ -1290,7 +1358,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { setSuggestedWords(settingsValues.mSuggestPuncList, false); } - mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition); + mConnection.resetCachesUponCursorMoveAndReturnSuccess(newCursorPosition, + shouldFinishComposition); } private void resetComposingState(final boolean alsoResetLastComposedWord) { @@ -1394,7 +1463,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || codePoint == Constants.CODE_CLOSING_PARENTHESIS || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET - || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET; + || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET + || codePoint == Constants.CODE_PLUS; } // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is @@ -2467,27 +2537,39 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen false /* isPrediction */); } - private void setAutoCorrection(final SuggestedWords suggestedWords) { + private void setAutoCorrection(final SuggestedWords suggestedWords, final String typedWord) { if (suggestedWords.isEmpty()) return; final String autoCorrection; if (suggestedWords.mWillAutoCorrect) { autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); } else { - autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD); + // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) + // because it may differ from mWordComposer.mTypedWord. + autoCorrection = typedWord; } mWordComposer.setAutoCorrection(autoCorrection); } + private void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords, + final String typedWord) { + if (suggestedWords.isEmpty()) { + clearSuggestionStrip(); + return; + } + setAutoCorrection(suggestedWords, typedWord); + final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); + setSuggestedWords(suggestedWords, isAutoCorrection); + setAutoCorrectionIndicator(isAutoCorrection); + setSuggestionStripShown(isSuggestionsStripVisible()); + } + private void showSuggestionStrip(final SuggestedWords suggestedWords) { if (suggestedWords.isEmpty()) { clearSuggestionStrip(); return; } - setAutoCorrection(suggestedWords); - final boolean isAutoCorrection = suggestedWords.willAutoCorrect(); - setSuggestedWords(suggestedWords, isAutoCorrection); - setAutoCorrectionIndicator(isAutoCorrection); - setSuggestionStripShown(isSuggestionsStripVisible()); + showSuggestionStripWithTypedWord(suggestedWords, + suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)); } private void commitCurrentAutoCorrection(final String separator) { @@ -2685,6 +2767,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return prevWord; } + private boolean isResumableWord(final String word, final SettingsValues settings) { + final int firstCodePoint = word.codePointAt(0); + return settings.isWordCodePoint(firstCodePoint) + && Constants.CODE_SINGLE_QUOTE != firstCodePoint + && Constants.CODE_DASH != firstCodePoint; + } + /** * Check if the cursor is touching a word. If so, restart suggestions on this word, else * do nothing. @@ -2708,12 +2797,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final TextRange range = mConnection.getWordRangeAtCursor(currentSettings.mWordSeparators, 0 /* additionalPrecedingWordsCount */); if (null == range) return; // Happens if we don't have an input connection at all + if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out. // If for some strange reason (editor bug or so) we measure the text before the cursor as // longer than what the entire text is supposed to be, the safe thing to do is bail out. final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor(); if (numberOfCharsInWordBeforeCursor > mLastSelectionStart) return; final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final String typedWord = range.mWord.toString(); + if (!isResumableWord(typedWord, currentSettings)) return; int i = 0; for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) { for (final String s : span.getSuggestions()) { @@ -2757,7 +2848,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Since there is only one word, willAutoCorrect is false. suggestedWords = suggestedWordsIncludingTypedWord; } - unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); + // We need to pass typedWord because mWordComposer.mTypedWord may differ from + // typedWord. + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, + typedWord); }}); } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of @@ -2766,12 +2860,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */); - unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords); + // We need to pass typedWord because mWordComposer.mTypedWord may differ from typedWord. + unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, typedWord); } } public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( - final SuggestedWords suggestedWords) { + final SuggestedWords suggestedWords, final String typedWord) { // Note that it's very important here that suggestedWords.mWillAutoCorrect is false. // We never want to auto-correct on a resumed suggestion. Please refer to the three places // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected. @@ -2779,7 +2874,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // the text to adapt it. // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition) mIsAutoCorrectionIndicatorOn = false; - mHandler.showSuggestionStrip(suggestedWords); + mHandler.showSuggestionStripWithTypedWord(suggestedWords, typedWord); } /** @@ -2809,6 +2904,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestionStrip(); } + /** + * Retry resetting caches in the rich input connection. + * + * When the editor can't be accessed we can't reset the caches, so we schedule a retry. + * This method handles the retry, and re-schedules a new retry if we still can't access. + * We only retry up to 5 times before giving up. + * + * @param tryResumeSuggestions Whether we should resume suggestions or not. + * @param remainingTries How many times we may try again before giving up. + */ + private void retryResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { + if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) { + if (0 < remainingTries) { + mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1); + } + return; + } + tryFixLyingCursorPosition(); + if (tryResumeSuggestions) mHandler.postResumeSuggestions(); + } + private void revertCommit() { final String previousWord = mLastComposedWord.mPrevWord; final String originallyTypedWord = mLastComposedWord.mTypedWord; diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index a031bb3be..925381b50 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -73,9 +73,6 @@ public final class RichInputConnection { * This contains the currently composing text, as LatinIME thinks the TextView is seeing it. */ private final StringBuilder mComposingText = new StringBuilder(); - // 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. - private static final int DEFAULT_TEXT_CACHE_SIZE = 100; private final InputMethodService mParent; InputConnection mIC; @@ -93,7 +90,8 @@ public final class RichInputConnection { r.token = 1; r.flags = 0; final ExtractedText et = mIC.getExtractedText(r, 0); - final CharSequence beforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0); + final CharSequence beforeCursor = getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, + 0); final StringBuilder internal = new StringBuilder().append(mCommittedTextBeforeComposingText) .append(mComposingText); if (null == et || null == beforeCursor) return; @@ -142,19 +140,56 @@ public final class RichInputConnection { if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } - public void resetCachesUponCursorMove(final int newCursorPosition, + /** + * Reset the cached text and retrieve it again from the editor. + * + * This should be called when the cursor moved. It's possible that we can't connect to + * the application when doing this; notably, this happens sometimes during rotation, probably + * because of a race condition in the framework. In this case, we just can't retrieve the + * data, so we empty the cache and note that we don't know the new cursor position, and we + * return false so that the caller knows about this and can retry later. + * + * @param newCursorPosition The new position of the cursor, as received from the system. + * @param shouldFinishComposition Whether we should finish the composition in progress. + * @return true if we were able to connect to the editor successfully, false otherwise. When + * this method returns false, the caches could not be correctly refreshed so they were only + * reset: the caller should try again later to return to normal operation. + */ + public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newCursorPosition, final boolean shouldFinishComposition) { mExpectedCursorPosition = newCursorPosition; mComposingText.setLength(0); mCommittedTextBeforeComposingText.setLength(0); - final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0); - if (null != textBeforeCursor) mCommittedTextBeforeComposingText.append(textBeforeCursor); + mIC = mParent.getCurrentInputConnection(); + // Call upon the inputconnection directly since our own method is using the cache, and + // we want to refresh it. + final CharSequence textBeforeCursor = null == mIC ? null : + mIC.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); + if (null == textBeforeCursor) { + // For some reason the app thinks we are not connected to it. This looks like a + // framework bug... Fall back to ground state and return false. + mExpectedCursorPosition = INVALID_CURSOR_POSITION; + Log.e(TAG, "Unable to connect to the editor to retrieve text... will retry later"); + return false; + } + mCommittedTextBeforeComposingText.append(textBeforeCursor); + final int lengthOfTextBeforeCursor = textBeforeCursor.length(); + if (lengthOfTextBeforeCursor > newCursorPosition + || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE + && newCursorPosition < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { + // newCursorPosition may be lying -- when rotating the device (probably a framework + // bug). If we have less chars than we asked for, then we know how many chars we have, + // and if we got more than newCursorPosition says, then we know it was lying. In both + // cases the length is more reliable + mExpectedCursorPosition = lengthOfTextBeforeCursor; + } if (null != mIC && shouldFinishComposition) { mIC.finishComposingText(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.richInputConnection_finishComposingText(); } } + return true; } private void checkBatchEdit() { @@ -233,8 +268,11 @@ public final class RichInputConnection { // getCapsMode should be updated to be able to return a "not enough info" result so that // we can get more context only when needed. if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedCursorPosition) { - mCommittedTextBeforeComposingText.append( - getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0)); + final CharSequence textBeforeCursor = getTextBeforeCursor( + Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); + if (!TextUtils.isEmpty(textBeforeCursor)) { + mCommittedTextBeforeComposingText.append(textBeforeCursor); + } } // This never calls InputConnection#getCapsMode - in fact, it's a static method that // never blocks or initiates IPC. @@ -362,7 +400,7 @@ public final class RichInputConnection { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); final CharSequence textBeforeCursor = - getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE + (end - start), 0); + getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE + (end - start), 0); mCommittedTextBeforeComposingText.setLength(0); if (!TextUtils.isEmpty(textBeforeCursor)) { final int indexOfStartOfComposingText = @@ -404,7 +442,8 @@ public final class RichInputConnection { } mExpectedCursorPosition = start; mCommittedTextBeforeComposingText.setLength(0); - mCommittedTextBeforeComposingText.append(getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0)); + mCommittedTextBeforeComposingText.append( + getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0)); } public void commitCorrection(final CorrectionInfo correctionInfo) { @@ -523,9 +562,9 @@ public final class RichInputConnection { if (mIC == null || sep == null) { return null; } - final CharSequence before = mIC.getTextBeforeCursor(1000, + final CharSequence before = mIC.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, InputConnection.GET_TEXT_WITH_STYLES); - final CharSequence after = mIC.getTextAfterCursor(1000, + final CharSequence after = mIC.getTextAfterCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, InputConnection.GET_TEXT_WITH_STYLES); if (before == null || after == null) { return null; @@ -568,8 +607,11 @@ public final class RichInputConnection { } } - return new TextRange(TextUtils.concat(before, after), startIndexInBefore, - before.length() + endIndexInAfter, before.length()); + // We don't use TextUtils#concat because it copies all spans without respect to their + // nature. If the text includes a PARAGRAPH span and it has been split, then + // TextUtils#concat will crash when it tries to concat both sides of it. + return new TextRange(StringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after), + startIndexInBefore, before.length() + endIndexInAfter, before.length()); } public boolean isCursorTouchingWord(final SettingsValues settingsValues) { diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 0889f22ca..cd9c89f04 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -58,17 +58,20 @@ public final class SubtypeSwitcher { // Dummy no language QWERTY subtype. See {@link R.xml.method}. private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype( - R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard, - SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", - "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY - + ",AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable", + R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet=" + + SubtypeLocaleUtils.QWERTY + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + ",EnabledWhenDefaultIsNotAsciiCapable," + + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */); // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}. // Dummy Emoji subtype. See {@link R.xml.method}. private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype( - R.string.subtype_emoji, R.drawable.ic_subtype_keyboard, - SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", - "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI, + R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet=" + + SubtypeLocaleUtils.EMOJI + "," + + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */); static final class NeedsToDisplayLanguage { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 7815f4d41..6c18c948f 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -361,12 +361,6 @@ public final class Suggest { // At second character typed, search the unigrams (scores being affected by bigrams) for (final String key : mDictionaries.keySet()) { - // Skip User history dictionary for lookup - // TODO: The user history dictionary should just override getSuggestionsWithSessionId - // to make sure it doesn't return anything and we should remove this test - if (key.equals(Dictionary.TYPE_USER_HISTORY)) { - continue; - } final Dictionary dictionary = mDictionaries.get(key); suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, @@ -460,7 +454,7 @@ public final class Suggest { private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = new SuggestedWordInfoComparator(); - private static SuggestedWordInfo getTransformedSuggestedWordInfo( + /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo( final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); @@ -471,7 +465,12 @@ public final class Suggest { } else { sb.append(wordInfo.mWord); } - for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { + // Appending quotes is here to help people quote words. However, it's not helpful + // when they type words with quotes toward the end like "it's" or "didn't", where + // it's more likely the user missed the last character (or didn't type it yet). + final int quotesToAppend = trailingSingleQuotesCount + - (-1 == wordInfo.mWord.indexOf(Constants.CODE_SINGLE_QUOTE) ? 0 : 1); + for (int i = quotesToAppend - 1; i >= 0; --i) { sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); } return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind, diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index a241b5505..864a17375 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -22,10 +22,12 @@ import android.content.ContentUris; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; +import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.Build; import android.provider.UserDictionary.Words; import android.text.TextUtils; +import android.util.Log; import com.android.inputmethod.compat.UserDictionaryCompatUtils; import com.android.inputmethod.latin.utils.LocaleUtils; @@ -39,6 +41,7 @@ import java.util.Locale; * dictionary file to use it from native code. */ public class UserBinaryDictionary extends ExpandableBinaryDictionary { + private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName(); // The user dictionary provider uses an empty string to mean "all languages". private static final String USER_DICTIONARY_ALL_LANGUAGES = ""; @@ -168,12 +171,19 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { } else { requestArguments = localeElements; } - final Cursor cursor = mContext.getContentResolver().query( - Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); + Cursor cursor = null; try { + cursor = mContext.getContentResolver().query( + Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null); addWords(cursor); + } catch (final SQLiteException e) { + Log.e(TAG, "SQLiteException in the remote User dictionary process.", e); } finally { - if (null != cursor) cursor.close(); + try { + if (null != cursor) cursor.close(); + } catch (final SQLiteException e) { + Log.e(TAG, "SQLiteException in the remote User dictionary process.", e); + } } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 5b319ad90..665c7a27c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -498,7 +498,7 @@ public final class BinaryDictDecoderUtils { // reach the end of the array. if (options.mSupportsDynamicUpdate) { - final boolean hasValidForwardLink = dictDecoder.readForwardLinkAndAdvancePosition(); + final boolean hasValidForwardLink = dictDecoder.readAndFollowForwardLink(); if (!hasValidForwardLink) break; } } while (options.mSupportsDynamicUpdate && dictDecoder.hasNextPtNodeArray()); @@ -550,7 +550,7 @@ public final class BinaryDictDecoderUtils { * @return the created (or merged) dictionary. */ @UsedForTesting - /* package */ static FusionDictionary readDictionaryBinary(final Ver3DictDecoder dictDecoder, + /* package */ static FusionDictionary readDictionaryBinary(final DictDecoder dictDecoder, final FusionDictionary dict) throws IOException, UnsupportedFormatException { // Read header final FileHeader fileHeader = dictDecoder.readHeader(); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index f333b0d86..4dba8e5cf 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -368,9 +368,9 @@ public class BinaryDictEncoderUtils { if (null != ptNode.mBigrams) { for (WeightedString bigram : ptNode.mBigrams) { final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray, - nodeSize + size + FormatSpec.PTNODE_FLAGS_SIZE, + nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE, FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord)); - nodeSize += getByteSize(offset) + FormatSpec.PTNODE_FLAGS_SIZE; + nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE; } } ptNode.mCachedSize = nodeSize; @@ -758,8 +758,15 @@ public class BinaryDictEncoderUtils { final FormatOptions formatOptions) { int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate + getNodeHeaderSize(ptNode, formatOptions); - if (ptNode.mFrequency >= 0) { - positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE; + if (ptNode.isTerminal()) { + // A terminal node has either the terminal id or the frequency. + // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children + // position. + if (formatOptions.mHasTerminalId) { + positionOfChildrenPosField += FormatSpec.PTNODE_TERMINAL_ID_SIZE; + } else { + positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE; + } } return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS : ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField; diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 2c5e93e5c..a282f595c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -114,7 +114,7 @@ public final class BinaryDictIOUtils { if (p.mPosition == p.mNumOfPtNode) { if (formatOptions.mSupportsDynamicUpdate) { final boolean hasValidForwardLinkAddress = - dictDecoder.readForwardLinkAndAdvancePosition(); + dictDecoder.readAndFollowForwardLink(); if (hasValidForwardLinkAddress && dictDecoder.hasNextPtNodeArray()) { // The node array has a forward link. p.mNumOfPtNode = Position.NOT_READ_PTNODE_COUNT; @@ -233,7 +233,7 @@ public final class BinaryDictIOUtils { } final boolean hasValidForwardLinkAddress = - dictDecoder.readForwardLinkAndAdvancePosition(); + dictDecoder.readAndFollowForwardLink(); if (!hasValidForwardLinkAddress || !dictDecoder.hasNextPtNodeArray()) { return FormatSpec.NOT_VALID_WORD; } diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java index 40e852423..3796a466c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java @@ -17,9 +17,11 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; import java.io.File; @@ -30,13 +32,50 @@ import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; +import java.util.HashMap; import java.util.TreeMap; /** - * An interface of binary dictionary decoder. + * The base class of binary dictionary decoders. */ -public interface DictDecoder { - public FileHeader readHeader() throws IOException, UnsupportedFormatException; +public abstract class DictDecoder { + + protected FileHeader readHeader(final DictBuffer dictBuffer) + throws IOException, UnsupportedFormatException { + if (dictBuffer == null) { + openDictBuffer(); + } + + final int version = HeaderReader.readVersion(dictBuffer); + if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION + || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) { + throw new UnsupportedFormatException("Unsupported version : " + version); + } + // TODO: Remove this field. + final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer); + + final int headerSize = HeaderReader.readHeaderSize(dictBuffer); + + if (headerSize < 0) { + throw new UnsupportedFormatException("header size can't be negative."); + } + + final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer, + headerSize); + + final FileHeader header = new FileHeader(headerSize, + new FusionDictionary.DictionaryOptions(attributes, + 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), + 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), + new FormatOptions(version, + 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); + return header; + } + + /** + * Reads and returns the file header. + */ + public abstract FileHeader readHeader() throws IOException, UnsupportedFormatException; /** * Reads PtNode from nodeAddress. @@ -44,7 +83,7 @@ public interface DictDecoder { * @param formatOptions the format options. * @return PtNodeInfo. */ - public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions); + public abstract PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions); /** * Reads a buffer and returns the memory representation of the dictionary. @@ -59,9 +98,9 @@ public interface DictDecoder { * @return the created (or merged) dictionary. */ @UsedForTesting - public FusionDictionary readDictionaryBinary(final FusionDictionary dict, + public abstract FusionDictionary readDictionaryBinary(final FusionDictionary dict, final boolean deleteDictIfBroken) - throws FileNotFoundException, IOException, UnsupportedFormatException; + throws FileNotFoundException, IOException, UnsupportedFormatException; /** * Gets the address of the last PtNode of the exact matching word in the dictionary. @@ -74,7 +113,12 @@ public interface DictDecoder { */ @UsedForTesting public int getTerminalPosition(final String word) - throws IOException, UnsupportedFormatException; + throws IOException, UnsupportedFormatException { + if (!isDictBufferOpen()) { + openDictBuffer(); + } + return BinaryDictIOUtils.getTerminalPosition(this, word); + } /** * Reads unigrams and bigrams from the binary file. @@ -86,50 +130,56 @@ public interface DictDecoder { * @throws IOException if the file can't be read. * @throws UnsupportedFormatException if the format of the file is not recognized. */ + @UsedForTesting public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words, final TreeMap<Integer, Integer> frequencies, final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams) - throws IOException, UnsupportedFormatException; + throws IOException, UnsupportedFormatException { + if (!isDictBufferOpen()) { + openDictBuffer(); + } + BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams); + } /** * Sets the position of the buffer to the given value. * * @param newPos the new position */ - public void setPosition(final int newPos); + public abstract void setPosition(final int newPos); /** * Gets the position of the buffer. * * @return the position */ - public int getPosition(); + public abstract int getPosition(); /** * Reads and returns the PtNode count out of a buffer and forwards the pointer. */ - public int readPtNodeCount(); + public abstract int readPtNodeCount(); /** * Reads the forward link and advances the position. * - * @return if this method advances the position then true else false. + * @return true if this method moves the file pointer, false otherwise. */ - public boolean readForwardLinkAndAdvancePosition(); - public boolean hasNextPtNodeArray(); + public abstract boolean readAndFollowForwardLink(); + public abstract boolean hasNextPtNodeArray(); /** * Opens the dictionary file and makes DictBuffer. */ @UsedForTesting - public void openDictBuffer() throws FileNotFoundException, IOException; + public abstract void openDictBuffer() throws FileNotFoundException, IOException; @UsedForTesting - public boolean isOpenedDictBuffer(); + public abstract boolean isDictBufferOpen(); - // Flags for DictionaryBufferFactory. + // Constants for DictionaryBufferFactory. public static final int USE_READONLY_BYTEBUFFER = 0x01000000; public static final int USE_BYTEARRAY = 0x02000000; - public static final int USE_WRITABLE_BYTEBUFFER = 0x04000000; + public static final int USE_WRITABLE_BYTEBUFFER = 0x03000000; public static final int MASK_DICTBUFFER = 0x0F000000; public interface DictionaryBufferFactory { @@ -221,4 +271,124 @@ public interface DictDecoder { return null; } } + + /** + * A utility class for reading a file header. + */ + protected static class HeaderReader { + protected static int readVersion(final DictBuffer dictBuffer) + throws IOException, UnsupportedFormatException { + return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer); + } + + protected static int readOptionFlags(final DictBuffer dictBuffer) { + return dictBuffer.readUnsignedShort(); + } + + protected static int readHeaderSize(final DictBuffer dictBuffer) { + return dictBuffer.readInt(); + } + + protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer, + final int headerSize) { + final HashMap<String, String> attributes = new HashMap<String, String>(); + while (dictBuffer.position() < headerSize) { + // We can avoid an infinite loop here since dictBuffer.position() is always + // increased by calling CharEncoding.readString. + final String key = CharEncoding.readString(dictBuffer); + final String value = CharEncoding.readString(dictBuffer); + attributes.put(key, value); + } + dictBuffer.position(headerSize); + return attributes; + } + } + + /** + * A utility class for reading a PtNode. + */ + protected static class PtNodeReader { + protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { + return dictBuffer.readUnsignedByte(); + } + + protected static int readParentAddress(final DictBuffer dictBuffer, + final FormatOptions formatOptions) { + if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { + return BinaryDictDecoderUtils.readSInt24(dictBuffer); + } else { + return FormatSpec.NO_PARENT_ADDRESS; + } + } + + protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags, + final FormatOptions formatOptions) { + if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { + final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer); + if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; + return address; + } else { + switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: + return dictBuffer.readUnsignedByte(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: + return dictBuffer.readUnsignedShort(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: + return dictBuffer.readUnsignedInt24(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: + default: + return FormatSpec.NO_CHILDREN_ADDRESS; + } + } + } + + // Reads shortcuts and returns the read length. + protected static int readShortcut(final DictBuffer dictBuffer, + final ArrayList<WeightedString> shortcutTargets) { + final int pointerBefore = dictBuffer.position(); + dictBuffer.readUnsignedShort(); // skip the size + while (true) { + final int targetFlags = dictBuffer.readUnsignedByte(); + final String word = CharEncoding.readString(dictBuffer); + shortcutTargets.add(new WeightedString(word, + targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); + if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return dictBuffer.position() - pointerBefore; + } + + protected static int readBigramAddresses(final DictBuffer dictBuffer, + final ArrayList<PendingAttribute> bigrams, final int baseAddress) { + int readLength = 0; + int bigramCount = 0; + while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + final int bigramFlags = dictBuffer.readUnsignedByte(); + ++readLength; + final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) + ? 1 : -1; + int bigramAddress = baseAddress + readLength; + switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: + bigramAddress += sign * dictBuffer.readUnsignedByte(); + readLength += 1; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: + bigramAddress += sign * dictBuffer.readUnsignedShort(); + readLength += 2; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: + bigramAddress += sign * dictBuffer.readUnsignedInt24(); + readLength += 3; + break; + default: + throw new RuntimeException("Has bigrams with no address"); + } + bigrams.add(new PendingAttribute( + bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, + bigramAddress)); + if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return readLength; + } + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 96ccd8e49..51b89a02a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -360,18 +360,26 @@ public final class FormatSpec { * Returns new dictionary decoder. * * @param dictFile the dictionary file. - * @param bufferType the flag indicating buffer type which is used by the dictionary decoder. + * @param bufferType The type of buffer, as one of USE_* in DictDecoder. * @return new dictionary decoder if the dictionary file exists, otherwise null. */ public static DictDecoder getDictDecoder(final File dictFile, final int bufferType) { - if (!dictFile.isFile()) return null; - return new Ver3DictDecoder(dictFile, bufferType); + if (dictFile.isDirectory()) { + return new Ver4DictDecoder(dictFile, bufferType); + } else if (dictFile.isFile()) { + return new Ver3DictDecoder(dictFile, bufferType); + } + return null; } public static DictDecoder getDictDecoder(final File dictFile, final DictionaryBufferFactory factory) { - if (!dictFile.isFile()) return null; - return new Ver3DictDecoder(dictFile, factory); + if (dictFile.isDirectory()) { + return new Ver4DictDecoder(dictFile, factory); + } else if (dictFile.isFile()) { + return new Ver3DictDecoder(dictFile, factory); + } + return null; } public static DictDecoder getDictDecoder(final File dictFile) { diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java index 1a90a4b98..848277cd4 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java @@ -32,14 +32,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; -import java.util.TreeMap; /** * An implementation of DictDecoder for version 3 binary dictionary. */ @UsedForTesting -public class Ver3DictDecoder implements DictDecoder { +public class Ver3DictDecoder extends DictDecoder { private static final String TAG = Ver3DictDecoder.class.getSimpleName(); static { @@ -49,124 +47,10 @@ public class Ver3DictDecoder implements DictDecoder { // TODO: implement something sensical instead of just a phony method private static native int doNothing(); - private final static class HeaderReader { - protected static int readVersion(final DictBuffer dictBuffer) - throws IOException, UnsupportedFormatException { - return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer); - } - - protected static int readOptionFlags(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedShort(); - } - - protected static int readHeaderSize(final DictBuffer dictBuffer) { - return dictBuffer.readInt(); - } - - protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer, - final int headerSize) { - final HashMap<String, String> attributes = new HashMap<String, String>(); - while (dictBuffer.position() < headerSize) { - // We can avoid an infinite loop here since dictBuffer.position() is always - // increased by calling CharEncoding.readString. - final String key = CharEncoding.readString(dictBuffer); - final String value = CharEncoding.readString(dictBuffer); - attributes.put(key, value); - } - dictBuffer.position(headerSize); - return attributes; - } - } - - private final static class PtNodeReader { - protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { + protected static class PtNodeReader extends DictDecoder.PtNodeReader { + private static int readFrequency(final DictBuffer dictBuffer) { return dictBuffer.readUnsignedByte(); } - - protected static int readParentAddress(final DictBuffer dictBuffer, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - return BinaryDictDecoderUtils.readSInt24(dictBuffer); - } else { - return FormatSpec.NO_PARENT_ADDRESS; - } - } - - protected static int readFrequency(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedByte(); - } - - protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer); - if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; - return address; - } else { - switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: - return dictBuffer.readUnsignedByte(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: - return dictBuffer.readUnsignedShort(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: - return dictBuffer.readUnsignedInt24(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: - default: - return FormatSpec.NO_CHILDREN_ADDRESS; - } - } - } - - // Reads shortcuts and returns the read length. - protected static int readShortcut(final DictBuffer dictBuffer, - final ArrayList<WeightedString> shortcutTargets) { - final int pointerBefore = dictBuffer.position(); - dictBuffer.readUnsignedShort(); // skip the size - while (true) { - final int targetFlags = dictBuffer.readUnsignedByte(); - final String word = CharEncoding.readString(dictBuffer); - shortcutTargets.add(new WeightedString(word, - targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return dictBuffer.position() - pointerBefore; - } - - protected static int readBigrams(final DictBuffer dictBuffer, - final ArrayList<PendingAttribute> bigrams, final int baseAddress) { - int readLength = 0; - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = dictBuffer.readUnsignedByte(); - ++readLength; - final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) - ? 1 : -1; - int bigramAddress = baseAddress + readLength; - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - bigramAddress += sign * dictBuffer.readUnsignedByte(); - readLength += 1; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - bigramAddress += sign * dictBuffer.readUnsignedShort(); - readLength += 2; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - final int offset = (dictBuffer.readUnsignedByte() << 16) - + dictBuffer.readUnsignedShort(); - bigramAddress += sign * offset; - readLength += 3; - break; - default: - throw new RuntimeException("Has bigrams with no address"); - } - bigrams.add(new PendingAttribute( - bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - bigramAddress)); - if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return readLength; - } } private final File mDictionaryBinaryFile; @@ -199,7 +83,7 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public boolean isOpenedDictBuffer() { + public boolean isDictBufferOpen() { return mDictBuffer != null; } @@ -218,25 +102,11 @@ public class Ver3DictDecoder implements DictDecoder { if (mDictBuffer == null) { openDictBuffer(); } - - final int version = HeaderReader.readVersion(mDictBuffer); - final int optionsFlags = HeaderReader.readOptionFlags(mDictBuffer); - - final int headerSize = HeaderReader.readHeaderSize(mDictBuffer); - - if (headerSize < 0) { - throw new UnsupportedFormatException("header size can't be negative."); + final FileHeader header = super.readHeader(mDictBuffer); + final int version = header.mFormatOptions.mVersion; + if (!(version >= 2 && version <= 3)) { + throw new UnsupportedFormatException("File header has a wrong version : " + version); } - - final HashMap<String, String> attributes = HeaderReader.readAttributes(mDictBuffer, - headerSize); - - final FileHeader header = new FileHeader(headerSize, - new FusionDictionary.DictionaryOptions(attributes, - 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG), - 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)), - new FormatOptions(version, - 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE))); return header; } @@ -246,11 +116,11 @@ public class Ver3DictDecoder implements DictDecoder { public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) { int addressPointer = ptNodePos; final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - ++addressPointer; + addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { - addressPointer += 3; + addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; } final int characters[]; @@ -258,7 +128,7 @@ public class Ver3DictDecoder implements DictDecoder { int index = 0; int character = CharEncoding.readChar(mDictBuffer); addressPointer += CharEncoding.getCharSize(character); - while (-1 != character) { + while (FormatSpec.INVALID_CHARACTER != character) { // FusionDictionary is making sure that the length of the word is smaller than // MAX_WORD_LENGTH. // So we'll never write past the end of mCharacterBuffer. @@ -274,8 +144,8 @@ public class Ver3DictDecoder implements DictDecoder { } final int frequency; if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - ++addressPointer; frequency = PtNodeReader.readFrequency(mDictBuffer); + addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE; } else { frequency = PtNode.NOT_A_TERMINAL; } @@ -296,7 +166,8 @@ public class Ver3DictDecoder implements DictDecoder { final ArrayList<PendingAttribute> bigrams; if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { bigrams = new ArrayList<PendingAttribute>(); - addressPointer += PtNodeReader.readBigrams(mDictBuffer, bigrams, addressPointer); + addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, + addressPointer); if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { MakedictLog.d("too many bigrams in a PtNode."); } @@ -332,25 +203,6 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public int getTerminalPosition(String word) throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) { - openDictBuffer(); - } - return BinaryDictIOUtils.getTerminalPosition(this, word); - } - - @Override - public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words, - final TreeMap<Integer, Integer> frequencies, - final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams) - throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) { - openDictBuffer(); - } - BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams); - } - - @Override public void setPosition(int newPos) { mDictBuffer.position(newPos); } @@ -366,7 +218,7 @@ public class Ver3DictDecoder implements DictDecoder { } @Override - public boolean readForwardLinkAndAdvancePosition() { + public boolean readAndFollowForwardLink() { final int nextAddress = mDictBuffer.readUnsignedInt24(); if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) { mDictBuffer.position(nextAddress); diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java index 222a0f474..76f0f4052 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java @@ -167,7 +167,7 @@ public class Ver3DictEncoder implements DictEncoder { } } - public void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) { + private void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) { final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions); if (formatOptions.mSupportsDynamicUpdate) { mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition, diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java new file mode 100644 index 000000000..36c5a2720 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + +import android.util.Log; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * An implementation of binary dictionary decoder for version 4 binary dictionary. + */ +@UsedForTesting +public class Ver4DictDecoder extends DictDecoder { + private static final String TAG = Ver4DictDecoder.class.getSimpleName(); + + private static final int FILETYPE_TRIE = 1; + private static final int FILETYPE_FREQUENCY = 2; + + private final File mDictDirectory; + private final DictionaryBufferFactory mBufferFactory; + private DictBuffer mDictBuffer; + private DictBuffer mFrequencyBuffer; + + @UsedForTesting + /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { + mDictDirectory = dictDirectory; + mDictBuffer = mFrequencyBuffer = null; + + if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) { + mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); + } else if ((factoryFlag & MASK_DICTBUFFER) == USE_BYTEARRAY) { + mBufferFactory = new DictionaryBufferFromByteArrayFactory(); + } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) { + mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory(); + } else { + mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); + } + } + + @UsedForTesting + /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { + mDictDirectory = dictDirectory; + mBufferFactory = factory; + mDictBuffer = mFrequencyBuffer = null; + } + + private File getFile(final int fileType) { + if (fileType == FILETYPE_TRIE) { + return new File(mDictDirectory, + mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION); + } else if (fileType == FILETYPE_FREQUENCY) { + return new File(mDictDirectory, + mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION); + } else { + throw new RuntimeException("Unsupported kind of file : " + fileType); + } + } + + @Override + public void openDictBuffer() throws FileNotFoundException, IOException { + final String filename = mDictDirectory.getName(); + mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE)); + mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY)); + } + + @Override + public boolean isDictBufferOpen() { + return mDictBuffer != null; + } + + /* package */ DictBuffer getDictBuffer() { + return mDictBuffer; + } + + @Override + public FileHeader readHeader() throws IOException, UnsupportedFormatException { + if (mDictBuffer == null) { + openDictBuffer(); + } + final FileHeader header = super.readHeader(mDictBuffer); + final int version = header.mFormatOptions.mVersion; + if (version != 4) { + throw new UnsupportedFormatException("File header has a wrong version : " + version); + } + return header; + } + + protected static class PtNodeReader extends DictDecoder.PtNodeReader { + protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId) { + frequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + 1); + return frequencyBuffer.readUnsignedByte(); + } + + protected static int readTerminalId(final DictBuffer dictBuffer) { + return dictBuffer.readInt(); + } + } + + // TODO: Make this buffer thread safe. + // TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH. + private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH]; + @Override + public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) { + int addressPointer = ptNodePos; + final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); + addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; + + final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); + if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { + addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; + } + + final int characters[]; + if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { + int index = 0; + int character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + while (FormatSpec.INVALID_CHARACTER != character + && index < FormatSpec.MAX_WORD_LENGTH) { + mCharacterBuffer[index++] = character; + character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + } + characters = Arrays.copyOfRange(mCharacterBuffer, 0, index); + } else { + final int character = CharEncoding.readChar(mDictBuffer); + addressPointer += CharEncoding.getCharSize(character); + characters = new int[] { character }; + } + final int terminalId; + if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { + terminalId = PtNodeReader.readTerminalId(mDictBuffer); + addressPointer += FormatSpec.PTNODE_TERMINAL_ID_SIZE; + } else { + terminalId = PtNode.NOT_A_TERMINAL; + } + + final int frequency; + if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { + frequency = PtNodeReader.readFrequency(mFrequencyBuffer, terminalId); + } else { + frequency = PtNode.NOT_A_TERMINAL; + } + int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options); + if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { + childrenAddress += addressPointer; + } + addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options); + final ArrayList<WeightedString> shortcutTargets; + if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) { + // readShortcut will add shortcuts to shortcutTargets. + shortcutTargets = new ArrayList<WeightedString>(); + addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets); + } else { + shortcutTargets = null; + } + + final ArrayList<PendingAttribute> bigrams; + if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { + bigrams = new ArrayList<PendingAttribute>(); + addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, + addressPointer); + if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + MakedictLog.d("too many bigrams in a node."); + } + } else { + bigrams = null; + } + return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency, + parentAddress, childrenAddress, shortcutTargets, bigrams); + } + + private void deleteDictFiles() { + final File[] files = mDictDirectory.listFiles(); + for (int i = 0; i < files.length; ++i) { + files[i].delete(); + } + } + + @Override + public FusionDictionary readDictionaryBinary(final FusionDictionary dict, + final boolean deleteDictIfBroken) + throws FileNotFoundException, IOException, UnsupportedFormatException { + if (mDictBuffer == null) { + openDictBuffer(); + } + try { + return BinaryDictDecoderUtils.readDictionaryBinary(this, dict); + } catch (IOException e) { + Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); + if (deleteDictIfBroken) { + deleteDictFiles(); + } + throw e; + } catch (UnsupportedFormatException e) { + Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); + if (deleteDictIfBroken) { + deleteDictFiles(); + } + throw e; + } + } + + @Override + public void setPosition(int newPos) { + mDictBuffer.position(newPos); + } + + @Override + public int getPosition() { + return mDictBuffer.position(); + } + + @Override + public int readPtNodeCount() { + return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer); + } + + @Override + public boolean readAndFollowForwardLink() { + final int nextAddress = mDictBuffer.readUnsignedInt24(); + if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) { + mDictBuffer.position(nextAddress); + return true; + } + return false; + } + + @Override + public boolean hasNextPtNodeArray() { + return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS; + } +} diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java index e43e74d87..0af028a9e 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.ActivityManagerCompatUtils; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.AbstractDictionaryWriter; import com.android.inputmethod.latin.ExpandableDictionary; @@ -41,7 +42,8 @@ import java.util.ArrayList; public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter { private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName(); /** Maximum number of pairs. Pruning will start when databases goes above this number. */ - public static final int MAX_HISTORY_BIGRAMS = 10000; + public static final int DEFAULT_MAX_HISTORY_BIGRAMS = 10000; + public static final int LOW_MEMORY_MAX_HISTORY_BIGRAMS = 2000; /** Any pair being typed or picked */ private static final int FREQUENCY_FOR_TYPED = 2; @@ -53,10 +55,14 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr private final UserHistoryDictionaryBigramList mBigramList = new UserHistoryDictionaryBigramList(); private final ExpandableDictionary mExpandableDictionary; + private final int mMaxHistoryBigrams; public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) { super(context, dictType); mExpandableDictionary = new ExpandableDictionary(dictType); + final boolean isLowRamDevice = ActivityManagerCompatUtils.isLowRamDevice(context); + mMaxHistoryBigrams = isLowRamDevice ? + LOW_MEMORY_MAX_HISTORY_BIGRAMS : DEFAULT_MAX_HISTORY_BIGRAMS; } @Override @@ -72,6 +78,10 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr @Override public void addUnigramWord(final String word, final String shortcutTarget, final int frequency, final boolean isNotAWord) { + if (mBigramList.size() > mMaxHistoryBigrams * 2) { + // Too many entries: just stop adding new vocabrary and wait next refresh. + return; + } mExpandableDictionary.addWord(word, shortcutTarget, frequency); mBigramList.addBigram(null, word, (byte)frequency); } @@ -79,6 +89,10 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr @Override public void addBigramWords(final String word0, final String word1, final int frequency, final boolean isValid, final long lastModifiedTime) { + if (mBigramList.size() > mMaxHistoryBigrams * 2) { + // Too many entries: just stop adding new vocabrary and wait next refresh. + return; + } if (lastModifiedTime > 0) { mExpandableDictionary.setBigramAndGetFrequency(word0, word1, new ForgettingCurveParams(frequency, System.currentTimeMillis(), @@ -102,19 +116,22 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr protected void writeDictionary(final DictEncoder dictEncoder) throws IOException, UnsupportedFormatException { UserHistoryDictIOUtils.writeDictionary(dictEncoder, - new FrequencyProvider(mBigramList, mExpandableDictionary), mBigramList, - FORMAT_OPTIONS); + new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams), + mBigramList, FORMAT_OPTIONS); } private static class FrequencyProvider implements BigramDictionaryInterface { - final private UserHistoryDictionaryBigramList mBigramList; - final private ExpandableDictionary mExpandableDictionary; + private final UserHistoryDictionaryBigramList mBigramList; + private final ExpandableDictionary mExpandableDictionary; + private final int mMaxHistoryBigrams; public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList, - final ExpandableDictionary expandableDictionary) { + final ExpandableDictionary expandableDictionary, final int maxHistoryBigrams) { mBigramList = bigramList; mExpandableDictionary = expandableDictionary; + mMaxHistoryBigrams = maxHistoryBigrams; } + @Override public int getFrequency(final String word0, final String word1) { final int freq; @@ -130,7 +147,7 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr if (prevFc > 0 && prevFc == fc) { freq = fc & 0xFF; } else if (UserHistoryForgettingCurveUtils. - needsToSave(fc, isValid, mBigramList.size() <= MAX_HISTORY_BIGRAMS)) { + needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) { freq = fc & 0xFF; } else { // Delete this entry diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java index 9364fb034..075d7e3c3 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java @@ -74,12 +74,12 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDi @Override public void close() { - // Close only binary dictionary to reuse this dictionary. - // super.close(); - closeBinaryDictionary(); + if (!ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { + closeBinaryDictionary(); + } // Flush pending writes. // TODO: Remove after this class become to use a dynamic binary dictionary. - asyncWriteBinaryDictionary(); + asyncFlashAllBinaryDictionary(); Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale); } @@ -212,6 +212,6 @@ public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDi // Clear the node structure on memory clear(); // Then flush the cleared state of the dictionary on disk. - asyncWriteBinaryDictionary(); + asyncFlashAllBinaryDictionary(); } } diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 215faa0c7..44b201642 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -25,6 +25,7 @@ import android.os.Build; import android.text.TextUtils; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import java.util.ArrayList; @@ -60,9 +61,10 @@ public final class AdditionalSubtypeUtils { StringUtils.appendToCommaSplittableTextIfNotExists( IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue); final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName); - return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard, - localeString, KEYBOARD_MODE, - layoutExtraValue + "," + additionalSubtypeExtraValue, false, false); + return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, + localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false); } public static String getPrefSubtype(final InputMethodSubtype subtype) { diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index 2f91c5743..60b24d5d5 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -60,6 +60,11 @@ public final class CapsModeUtils { || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode; } + private static boolean isPeriod(final int codePoint) { + // TODO: make this a resource. + return codePoint == Constants.CODE_PERIOD || codePoint == Constants.CODE_ARMENIAN_PERIOD; + } + /** * Determine what caps mode should be in effect at the current offset in * the text. Only the mode bits set in <var>reqModes</var> will be @@ -190,7 +195,7 @@ public final class CapsModeUtils { if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes; } - if (c != Constants.CODE_PERIOD || j <= 0) { + if (!isPeriod(c) || j <= 0) { return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes; } @@ -240,7 +245,7 @@ public final class CapsModeUtils { case WORD: if (Character.isLetter(c)) { state = WORD; - } else if (c == Constants.CODE_PERIOD) { + } else if (isPeriod(c)) { state = PERIOD; } else { return caps; @@ -256,7 +261,7 @@ public final class CapsModeUtils { case LETTER: if (Character.isLetter(c)) { state = LETTER; - } else if (c == Constants.CODE_PERIOD) { + } else if (isPeriod(c)) { state = PERIOD; } else { return noCaps; diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index be4184093..327780ad0 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -16,16 +16,30 @@ package com.android.inputmethod.latin.utils; -import android.text.TextUtils; - import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.settings.SettingsValues; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.SpannedString; +import android.text.TextUtils; +import android.text.style.SuggestionSpan; +import android.util.JsonReader; +import android.util.JsonWriter; +import android.util.Log; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; 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 @@ -390,4 +404,151 @@ public final class StringUtils { } return bytes; } + + public static List<Object> jsonStrToList(String s) { + final ArrayList<Object> retval = CollectionUtils.newArrayList(); + final JsonReader reader = new JsonReader(new StringReader(s)); + try { + reader.beginArray(); + while(reader.hasNext()) { + reader.beginObject(); + while (reader.hasNext()) { + final String name = reader.nextName(); + if (name.equals(Integer.class.getSimpleName())) { + retval.add(reader.nextInt()); + } else if (name.equals(String.class.getSimpleName())) { + retval.add(reader.nextString()); + } else { + Log.w(TAG, "Invalid name: " + name); + reader.skipValue(); + } + } + reader.endObject(); + } + reader.endArray(); + return retval; + } catch (IOException e) { + } finally { + try { + reader.close(); + } catch (IOException e) { + } + } + return Collections.<Object>emptyList(); + } + + public static String listToJsonStr(List<Object> list) { + if (list == null || list.isEmpty()) { + return ""; + } + final StringWriter sw = new StringWriter(); + final JsonWriter writer = new JsonWriter(sw); + try { + writer.beginArray(); + for (final Object o : list) { + writer.beginObject(); + if (o instanceof Integer) { + writer.name(Integer.class.getSimpleName()).value((Integer)o); + } else if (o instanceof String) { + writer.name(String.class.getSimpleName()).value((String)o); + } + writer.endObject(); + } + writer.endArray(); + return sw.toString(); + } catch (IOException e) { + } finally { + try { + if (writer != null) { + writer.close(); + } + } catch (IOException e) { + } + } + return ""; + } + + /** + * Copies the spans from the region <code>start...end</code> in + * <code>source</code> to the region + * <code>destoff...destoff+end-start</code> in <code>dest</code>. + * Spans in <code>source</code> that begin before <code>start</code> + * or end after <code>end</code> but overlap this range are trimmed + * as if they began at <code>start</code> or ended at <code>end</code>. + * Only SuggestionSpans that don't have the SPAN_PARAGRAPH span are copied. + * + * This code is almost entirely taken from {@link TextUtils#copySpansFrom}, except for the + * kind of span that is copied. + * + * @throws IndexOutOfBoundsException if any of the copied spans + * are out of range in <code>dest</code>. + */ + public static void copyNonParagraphSuggestionSpansFrom(Spanned source, int start, int end, + Spannable dest, int destoff) { + Object[] spans = source.getSpans(start, end, SuggestionSpan.class); + + for (int i = 0; i < spans.length; i++) { + int fl = source.getSpanFlags(spans[i]); + if (0 != (fl & Spannable.SPAN_PARAGRAPH)) continue; + + int st = source.getSpanStart(spans[i]); + int en = source.getSpanEnd(spans[i]); + + if (st < start) + st = start; + if (en > end) + en = end; + + dest.setSpan(spans[i], st - start + destoff, en - start + destoff, + fl); + } + } + + /** + * Returns a CharSequence concatenating the specified CharSequences, retaining their + * SuggestionSpans that don't have the PARAGRAPH flag, but not other spans. + * + * This code is almost entirely taken from {@link TextUtils#concat(CharSequence...)}, except + * it calls copyNonParagraphSuggestionSpansFrom instead of {@link TextUtils#copySpansFrom}. + */ + public static CharSequence concatWithNonParagraphSuggestionSpansOnly(CharSequence... text) { + if (text.length == 0) { + return ""; + } + + if (text.length == 1) { + return text[0]; + } + + boolean spanned = false; + for (int i = 0; i < text.length; i++) { + if (text[i] instanceof Spanned) { + spanned = true; + break; + } + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < text.length; i++) { + sb.append(text[i]); + } + + if (!spanned) { + return sb.toString(); + } + + SpannableString ss = new SpannableString(sb); + int off = 0; + for (int i = 0; i < text.length; i++) { + int len = text[i].length(); + + if (text[i] instanceof Spanned) { + copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off); + } + + off += len; + } + + return new SpannedString(ss); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java index 5793e4170..48b443ddd 100644 --- a/java/src/com/android/inputmethod/latin/utils/TextRange.java +++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java @@ -40,6 +40,10 @@ public final class TextRange { return mWordAtCursorEndIndex - mCursorIndex; } + public int length() { + return mWord.length(); + } + /** * Gets the suggestion spans that are put squarely on the word, with the exact start * and end of the span matching the boundaries of the word. diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index 544e4d201..47ea1ea75 100644 --- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -66,6 +66,11 @@ public final class TypefaceUtils { } } + public static float getStringWidth(final String string, final Paint paint) { + paint.getTextBounds(string, 0, string.length(), sTextWidthBounds); + return sTextWidthBounds.width(); + } + private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) { final int labelSize = (int)paint.getTextSize(); final Typeface face = paint.getTypeface(); diff --git a/native/jni/Android.mk b/native/jni/Android.mk index d83bdadfa..c2070327e 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -70,9 +70,10 @@ LATIN_IME_CORE_SRC_FILES := \ bigram/bigram_list_read_write_utils.cpp \ bigram/dynamic_bigram_list_policy.cpp \ header/header_policy.cpp \ - header/header_reading_utils.cpp \ + header/header_read_write_utils.cpp \ shortcut/shortcut_list_reading_utils.cpp \ dictionary_structure_with_buffer_policy_factory.cpp \ + dynamic_patricia_trie_gc_event_listeners.cpp \ dynamic_patricia_trie_node_reader.cpp \ dynamic_patricia_trie_policy.cpp \ dynamic_patricia_trie_reading_helper.cpp \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index a63fab6dc..7f47493b2 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -188,8 +188,8 @@ static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, return dictionary->getProbability(codePoints, wordLength); } -static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass clazz, jlong dict, - jintArray word0, jintArray word1) { +static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz, + jlong dict, jintArray word0, jintArray word1) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) return JNI_FALSE; const jsize word0Length = env->GetArrayLength(word0); @@ -198,7 +198,8 @@ static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass claz int word1CodePoints[word1Length]; env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); - return dictionary->isValidBigram(word0CodePoints, word0Length, word1CodePoints, word1Length); + return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints, + word1Length); } static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz, @@ -269,6 +270,16 @@ static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass claz word1Length); } +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 const JNINativeMethod sMethods[] = { { const_cast<char *>("openNative"), @@ -306,9 +317,9 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability) }, { - const_cast<char *>("isValidBigramNative"), - const_cast<char *>("(J[I[I)Z"), - reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram) + const_cast<char *>("getBigramProbabilityNative"), + const_cast<char *>("(J[I[I)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability) }, { const_cast<char *>("calcNormalizedScoreNative"), @@ -334,6 +345,11 @@ static const JNINativeMethod sMethods[] = { const_cast<char *>("removeBigramWordsNative"), const_cast<char *>("(J[I[I)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords) + }, + { + const_cast<char *>("calculateProbabilityNative"), + const_cast<char *>("(JII)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative) } }; diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index 425b07624..71f4ef6ea 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -147,27 +147,29 @@ int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const in int pos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(prevWord, prevWordLength, forceLowerCaseSearch); if (NOT_A_DICT_POS == pos) return NOT_A_DICT_POS; - return mDictionaryStructurePolicy->getBigramsPositionOfNode(pos); + return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos); } -bool BigramDictionary::isValidBigram(const int *word0, int length0, const int *word1, +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 false; + if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY; int nextWordPos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(word1, length1, false /* forceLowerCaseSearch */); - if (NOT_A_DICT_POS == nextWordPos) return false; + if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY; BinaryDictionaryBigramsIterator bigramsIt( mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos); while (bigramsIt.hasNext()) { bigramsIt.next(); if (bigramsIt.getBigramPos() == nextWordPos) { - return true; + return mDictionaryStructurePolicy->getProbability( + mDictionaryStructurePolicy->getUnigramProbabilityOfPtNode(nextWordPos), + bigramsIt.getProbability()); } } - return false; + return NOT_A_PROBABILITY; } // TODO: Move functions related to bigram to here diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h index 99b964c49..8af7ee75d 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h @@ -29,7 +29,7 @@ class BigramDictionary { int getPredictions(const int *word, int length, int *outBigramCodePoints, int *outBigramProbability, int *outputTypes) const; - bool isValidBigram(const int *word1, int length1, const int *word2, int length2) const; + int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const; ~BigramDictionary(); private: diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 033572201..ec1b63a12 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -93,8 +93,9 @@ int Dictionary::getProbability(const int *word, int length) const { return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos); } -bool Dictionary::isValidBigram(const int *word0, int length0, const int *word1, int length1) const { - return mBigramDictionary->isValidBigram(word0, length0, word1, length1); +int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1, + int length1) const { + return mBigramDictionary->getBigramProbability(word0, length0, word1, length1); } void Dictionary::addUnigramWord(const int *const word, const int length, const int probability) { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 06e84bbfe..974447468 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -67,7 +67,7 @@ class Dictionary { int getProbability(const int *word, int length) const; - bool isValidBigram(const int *word0, int length0, const int *word1, int length1) const; + int getBigramProbability(const int *word0, int length0, const int *word1, int length1) const; void addUnigramWord(const int *const word, const int length, const int probability); diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h index aecf41386..4633c07b0 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h @@ -68,7 +68,7 @@ class MultiBigramMap { void init(const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos) { - const int bigramsListPos = structurePolicy->getBigramsPositionOfNode(nodePos); + const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), bigramsListPos); while (bigramsIt.hasNext()) { @@ -112,7 +112,7 @@ class MultiBigramMap { const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, const int nextWordPosition, const int unigramProbability) { int bigramProbability = NOT_A_PROBABILITY; - const int bigramsListPos = structurePolicy->getBigramsPositionOfNode(nodePos); + const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), bigramsListPos); while (bigramsIt.hasNext()) { 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 24d1b8ba1..b95488ebd 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 @@ -52,9 +52,9 @@ class DictionaryStructureWithBufferPolicy { virtual int getUnigramProbabilityOfPtNode(const int nodePos) const = 0; - virtual int getShortcutPositionOfNode(const int nodePos) const = 0; + virtual int getShortcutPositionOfPtNode(const int nodePos) const = 0; - virtual int getBigramsPositionOfNode(const int nodePos) const = 0; + virtual int getBigramsPositionOfPtNode(const int nodePos) const = 0; virtual const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const = 0; diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 0c925be25..b1340e12f 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -223,7 +223,7 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen BinaryDictionaryShortcutIterator shortcutIt( traverseSession->getDictionaryStructurePolicy()->getShortcutsStructurePolicy(), traverseSession->getDictionaryStructurePolicy() - ->getShortcutPositionOfNode(terminalDicNode->getPos())); + ->getShortcutPositionOfPtNode(terminalDicNode->getPos())); // Shortcut is not supported for multiple words suggestions. // TODO: Check shortcuts during traversal for multiple words suggestions. const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode); diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h index 26015d512..6ff95cac4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h @@ -33,10 +33,9 @@ class BigramListPolicy : public DictionaryBigramsStructurePolicy { void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const pos) const { - const BigramListReadWriteUtils::BigramFlags flags = - BigramListReadWriteUtils::getFlagsAndForwardPointer(mBigramsBuf, pos); - *outBigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( - mBigramsBuf, flags, pos); + BigramListReadWriteUtils::BigramFlags flags; + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBigramsBuf, &flags, + outBigramPos, pos); *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(flags); *outHasNext = BigramListReadWriteUtils::hasNext(flags); } diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp index 09e832f07..1926b9831 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp @@ -16,7 +16,9 @@ #include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" namespace latinime { @@ -38,23 +40,31 @@ const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F; const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4; -/* static */ BigramListReadWriteUtils::BigramFlags - BigramListReadWriteUtils::getFlagsAndForwardPointer(const uint8_t *const bigramsBuf, - int *const pos) { - return ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, pos); +/* static */ void BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + const uint8_t *const bigramsBuf, BigramFlags *const outBigramFlags, + int *const outTargetPtNodePos, int *const bigramEntryPos) { + const BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, + bigramEntryPos); + if (outBigramFlags) { + *outBigramFlags = bigramFlags; + } + const int targetPos = getBigramAddressAndAdvancePosition(bigramsBuf, bigramFlags, + bigramEntryPos); + if (outTargetPtNodePos) { + *outTargetPtNodePos = targetPos; + } } /* static */ void BigramListReadWriteUtils::skipExistingBigrams(const uint8_t *const bigramsBuf, - int *const pos) { - BigramFlags flags = getFlagsAndForwardPointer(bigramsBuf, pos); - while (hasNext(flags)) { - *pos += attributeAddressSize(flags); - flags = getFlagsAndForwardPointer(bigramsBuf, pos); - } - *pos += attributeAddressSize(flags); + int *const bigramListPos) { + BigramFlags flags; + do { + getBigramEntryPropertiesAndAdvancePosition(bigramsBuf, &flags, 0 /* outTargetPtNodePos */, + bigramListPos); + } while(hasNext(flags)); } -/* static */ int BigramListReadWriteUtils::getBigramAddressAndForwardPointer( +/* static */ int BigramListReadWriteUtils::getBigramAddressAndAdvancePosition( const uint8_t *const bigramsBuf, const BigramFlags flags, int *const pos) { int offset = 0; const int origin = *pos; @@ -69,8 +79,10 @@ const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4; offset = ByteArrayUtils::readUint24AndAdvancePosition(bigramsBuf, pos); break; } - if (offset == 0) { + if (offset == DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID) { return NOT_A_DICT_POS; + } else if (offset == DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET) { + return origin; } if (isOffsetNegative(flags)) { return origin - offset; @@ -79,4 +91,92 @@ const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4; } } +/* static */ bool BigramListReadWriteUtils::setHasNextFlag( + BufferWithExtendableBuffer *const buffer, const bool hasNext, const int entryPos) { + const bool usesAdditionalBuffer = buffer->isInAdditionalBuffer(entryPos); + int readingPos = entryPos; + if (usesAdditionalBuffer) { + readingPos -= buffer->getOriginalBufferSize(); + } + BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition( + buffer->getBuffer(usesAdditionalBuffer), &readingPos); + if (hasNext) { + bigramFlags = bigramFlags | FLAG_ATTRIBUTE_HAS_NEXT; + } else { + bigramFlags = bigramFlags & (~FLAG_ATTRIBUTE_HAS_NEXT); + } + int writingPos = entryPos; + return buffer->writeUintAndAdvancePosition(bigramFlags, 1 /* size */, &writingPos); +} + +/* static */ bool BigramListReadWriteUtils::createAndWriteBigramEntry( + BufferWithExtendableBuffer *const buffer, const int targetPos, const int probability, + const bool hasNext, int *const writingPos) { + BigramFlags flags; + if (!createAndGetBigramFlags(*writingPos, targetPos, probability, hasNext, &flags)) { + return false; + } + return writeBigramEntry(buffer, flags, targetPos, writingPos); +} + +/* static */ bool BigramListReadWriteUtils::writeBigramEntry( + BufferWithExtendableBuffer *const bufferToWrite, const BigramFlags flags, + const int targetPtNodePos, int *const writingPos) { + const int offset = getBigramTargetOffset(targetPtNodePos, *writingPos); + const BigramFlags flagsToWrite = (offset < 0) ? + (flags | FLAG_ATTRIBUTE_OFFSET_NEGATIVE) : (flags & ~FLAG_ATTRIBUTE_OFFSET_NEGATIVE); + if (!bufferToWrite->writeUintAndAdvancePosition(flagsToWrite, 1 /* size */, writingPos)) { + return false; + } + const uint32_t absOffest = abs(offset); + const int bigramTargetFieldSize = attributeAddressSize(flags); + return bufferToWrite->writeUintAndAdvancePosition(absOffest, bigramTargetFieldSize, + writingPos); +} + +// Returns true if the bigram entry is valid and put entry flags into out*. +/* static */ bool BigramListReadWriteUtils::createAndGetBigramFlags(const int entryPos, + const int targetPtNodePos, const int probability, const bool hasNext, + BigramFlags *const outBigramFlags) { + BigramFlags flags = probability & MASK_ATTRIBUTE_PROBABILITY; + if (hasNext) { + flags |= FLAG_ATTRIBUTE_HAS_NEXT; + } + const int offset = getBigramTargetOffset(targetPtNodePos, entryPos); + if (offset < 0) { + flags |= FLAG_ATTRIBUTE_OFFSET_NEGATIVE; + } + const uint32_t absOffest = abs(offset); + if ((absOffest >> 24) != 0) { + // Offset is too large. + return false; + } else if ((absOffest >> 16) != 0) { + flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES; + } else if ((absOffest >> 8) != 0) { + flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES; + } else { + flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE; + } + // Currently, all newly written bigram position fields are 3 bytes to simplify dictionary + // writing. + // TODO: Remove following 2 lines and optimize memory space. + flags = (flags & (~MASK_ATTRIBUTE_ADDRESS_TYPE)) | FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES; + *outBigramFlags = flags; + return true; +} + +/* static */ int BigramListReadWriteUtils::getBigramTargetOffset(const int targetPtNodePos, + const int entryPos) { + if (targetPtNodePos == NOT_A_DICT_POS) { + return DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID; + } else { + const int offset = targetPtNodePos - (entryPos + 1 /* bigramFlagsField */); + if (offset == 0) { + return DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET; + } else { + return offset; + } + } +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h index 884bcd7a9..eabe4e099 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h @@ -24,11 +24,15 @@ namespace latinime { +class BufferWithExtendableBuffer; + class BigramListReadWriteUtils { public: typedef uint8_t BigramFlags; - static BigramFlags getFlagsAndForwardPointer(const uint8_t *const bigramsBuf, int *const pos); + static void getBigramEntryPropertiesAndAdvancePosition(const uint8_t *const bigramsBuf, + BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, + int *const bigramEntryPos); static AK_FORCE_INLINE int getProbabilityFromFlags(const BigramFlags flags) { return flags & MASK_ATTRIBUTE_PROBABILITY; @@ -39,10 +43,7 @@ public: } // Bigrams reading methods - static void skipExistingBigrams(const uint8_t *const bigramsBuf, int *const pos); - - static int getBigramAddressAndForwardPointer(const uint8_t *const bigramsBuf, - const BigramFlags flags, int *const pos); + static void skipExistingBigrams(const uint8_t *const bigramsBuf, int *const bigramListPos); // Returns the size of the bigram position field that is stored in bigram flags. static AK_FORCE_INLINE int attributeAddressSize(const BigramFlags flags) { @@ -58,50 +59,19 @@ public: */ } - static AK_FORCE_INLINE BigramFlags setHasNextFlag(const BigramFlags flags) { - return flags | FLAG_ATTRIBUTE_HAS_NEXT; - } + static bool setHasNextFlag(BufferWithExtendableBuffer *const buffer, + const bool hasNext, const int entryPos); static AK_FORCE_INLINE BigramFlags setProbabilityInFlags(const BigramFlags flags, const int probability) { return (flags & (~MASK_ATTRIBUTE_PROBABILITY)) | (probability & MASK_ATTRIBUTE_PROBABILITY); } - // Returns true if the bigram entry is valid and put entry values into out*. - static AK_FORCE_INLINE bool createBigramEntryAndGetFlagsAndOffsetAndOffsetFieldSize( - const int entryPos, const int targetPos, const int probability, const bool hasNext, - BigramFlags *const outBigramFlags, uint32_t *const outOffset, - int *const outOffsetFieldSize) { - if (targetPos == NOT_A_DICT_POS) { - return false; - } - BigramFlags flags = probability & MASK_ATTRIBUTE_PROBABILITY; - if (hasNext) { - flags |= FLAG_ATTRIBUTE_HAS_NEXT; - } - const int targetFieldPos = entryPos + 1; - const int offset = targetPos - targetFieldPos; - if (offset < 0) { - flags |= FLAG_ATTRIBUTE_OFFSET_NEGATIVE; - } - const uint32_t absOffest = abs(offset); - if ((absOffest >> 24) != 0) { - // Offset is too large. - return false; - } else if ((absOffest >> 16) != 0) { - flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES; - *outOffsetFieldSize = 3; - } else if ((absOffest >> 8) != 0) { - flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES; - *outOffsetFieldSize = 2; - } else { - flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE; - *outOffsetFieldSize = 1; - } - *outBigramFlags = flags; - *outOffset = absOffest; - return true; - } + static bool createAndWriteBigramEntry(BufferWithExtendableBuffer *const buffer, + const int targetPos, const int probability, const bool hasNext, int *const writingPos); + + static bool writeBigramEntry(BufferWithExtendableBuffer *const buffer, const BigramFlags flags, + const int targetOffset, int *const writingPos); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListReadWriteUtils); @@ -115,9 +85,18 @@ private: static const BigramFlags MASK_ATTRIBUTE_PROBABILITY; static const int ATTRIBUTE_ADDRESS_SHIFT; + // Returns true if the bigram entry is valid and put entry flags into out*. + static bool createAndGetBigramFlags(const int entryPos, const int targetPos, + const int probability, const bool hasNext, BigramFlags *const outBigramFlags); + static AK_FORCE_INLINE bool isOffsetNegative(const BigramFlags flags) { return (flags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) != 0; } + + static int getBigramAddressAndAdvancePosition(const uint8_t *const bigramsBuf, + const BigramFlags flags, int *const pos); + + static int getBigramTargetOffset(const int targetPtNodePos, const int entryPos); }; } // namespace latinime #endif // LATINIME_BIGRAM_LIST_READ_WRITE_UTILS_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp index 4c44d22fd..bc2f5ee58 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp @@ -16,58 +16,73 @@ #include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" +#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" +#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + namespace latinime { -const int DynamicBigramListPolicy::BIGRAM_LINK_COUNT_LIMIT = 10000; +const int DynamicBigramListPolicy::CONTINUING_BIGRAM_LINK_COUNT_LIMIT = 10000; +const int DynamicBigramListPolicy::BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT = 100000; void DynamicBigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability, - bool *const outHasNext, int *const pos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); + bool *const outHasNext, int *const bigramEntryPos) const { + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramEntryPos); const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { - *pos -= mBuffer->getOriginalBufferSize(); + *bigramEntryPos -= mBuffer->getOriginalBufferSize(); } - const BigramListReadWriteUtils::BigramFlags flags = - BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, pos); - int originalBigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( - buffer, flags, pos); + BigramListReadWriteUtils::BigramFlags bigramFlags; + int originalBigramPos; + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(buffer, &bigramFlags, + &originalBigramPos, bigramEntryPos); if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { originalBigramPos += mBuffer->getOriginalBufferSize(); } *outBigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(flags); - *outHasNext = BigramListReadWriteUtils::hasNext(flags); + *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags); + *outHasNext = BigramListReadWriteUtils::hasNext(bigramFlags); if (usesAdditionalBuffer) { - *pos += mBuffer->getOriginalBufferSize(); + *bigramEntryPos += mBuffer->getOriginalBufferSize(); } } -void DynamicBigramListPolicy::skipAllBigrams(int *const pos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); +void DynamicBigramListPolicy::skipAllBigrams(int *const bigramListPos) const { + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos); const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { - *pos -= mBuffer->getOriginalBufferSize(); + *bigramListPos -= mBuffer->getOriginalBufferSize(); } - BigramListReadWriteUtils::skipExistingBigrams(buffer, pos); + BigramListReadWriteUtils::skipExistingBigrams(buffer, bigramListPos); if (usesAdditionalBuffer) { - *pos += mBuffer->getOriginalBufferSize(); + *bigramListPos += mBuffer->getOriginalBufferSize(); } } -bool DynamicBigramListPolicy::copyAllBigrams(int *const fromPos, int *const toPos, - int *outBigramsCount) { +bool DynamicBigramListPolicy::copyAllBigrams(BufferWithExtendableBuffer *const bufferToWrite, + int *const fromPos, int *const toPos, int *const outBigramsCount) const { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos); if (usesAdditionalBuffer) { *fromPos -= mBuffer->getOriginalBufferSize(); } *outBigramsCount = 0; - BigramListReadWriteUtils::BigramFlags flags; + BigramListReadWriteUtils::BigramFlags bigramFlags; + int bigramEntryCount = 0; + int lastWrittenEntryPos = NOT_A_DICT_POS; do { + if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) { + AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d", + bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT); + ASSERT(false); + return false; + } // The buffer address can be changed after calling buffer writing methods. - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, fromPos); - int originalBigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( - buffer, flags, fromPos); + int originalBigramPos; + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, + fromPos); if (originalBigramPos == NOT_A_DICT_POS) { // skip invalid bigram entry. continue; @@ -76,132 +91,223 @@ bool DynamicBigramListPolicy::copyAllBigrams(int *const fromPos, int *const toPo originalBigramPos += mBuffer->getOriginalBufferSize(); } const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - BigramListReadWriteUtils::BigramFlags newBigramFlags; - uint32_t newBigramOffset; - int newBigramOffsetFieldSize; - if(!BigramListReadWriteUtils::createBigramEntryAndGetFlagsAndOffsetAndOffsetFieldSize( - *toPos, bigramPos, BigramListReadWriteUtils::getProbabilityFromFlags(flags), - BigramListReadWriteUtils::hasNext(flags), &newBigramFlags, &newBigramOffset, - &newBigramOffsetFieldSize)) { + if (bigramPos == NOT_A_DICT_POS) { + // Target PtNode has been invalidated. continue; } - // Write bigram entry. Target buffer is always the additional buffer. - if (!mBuffer->writeUintAndAdvancePosition(newBigramFlags, 1 /* size */,toPos)) { + lastWrittenEntryPos = *toPos; + if (!BigramListReadWriteUtils::createAndWriteBigramEntry(bufferToWrite, bigramPos, + BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags), + BigramListReadWriteUtils::hasNext(bigramFlags), toPos)) { return false; } - if (!mBuffer->writeUintAndAdvancePosition(newBigramOffset, newBigramOffsetFieldSize, - toPos)) { + (*outBigramsCount)++; + } while(BigramListReadWriteUtils::hasNext(bigramFlags)); + // Makes the last entry the terminal of the list. Updates the flags. + if (lastWrittenEntryPos != NOT_A_DICT_POS) { + if (!BigramListReadWriteUtils::setHasNextFlag(bufferToWrite, false /* hasNext */, + lastWrittenEntryPos)) { return false; } - (*outBigramsCount)++; - } while(BigramListReadWriteUtils::hasNext(flags)); + } if (usesAdditionalBuffer) { *fromPos += mBuffer->getOriginalBufferSize(); } return true; } -bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramPos, - const int probability, int *const pos) { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); +// Finding useless bigram entries and remove them. Bigram entry is useless when the target PtNode +// has been deleted or is not a valid terminal. +bool DynamicBigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries( + int *const bigramListPos) { + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos); + if (usesAdditionalBuffer) { + *bigramListPos -= mBuffer->getOriginalBufferSize(); + } + DynamicPatriciaTrieNodeReader nodeReader(mBuffer, this /* bigramsPolicy */, mShortcutPolicy); + BigramListReadWriteUtils::BigramFlags bigramFlags; + int bigramEntryCount = 0; + do { + if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) { + AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d", + bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT); + ASSERT(false); + return false; + } + int bigramEntryPos = *bigramListPos; + int originalBigramPos; + // The buffer address can be changed after calling buffer writing methods. + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, + bigramListPos); + if (usesAdditionalBuffer) { + bigramEntryPos += mBuffer->getOriginalBufferSize(); + } + if (originalBigramPos == NOT_A_DICT_POS) { + // This entry has already been removed. + continue; + } + if (usesAdditionalBuffer) { + originalBigramPos += mBuffer->getOriginalBufferSize(); + } + const int bigramTargetNodePos = + followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(bigramTargetNodePos); + // TODO: Update probability for supporting probability decaying. + if (nodeReader.isDeleted() || !nodeReader.isTerminal() + || bigramTargetNodePos == NOT_A_DICT_POS) { + // The target is no longer valid terminal. Invalidate the current bigram entry. + if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags, + NOT_A_DICT_POS /* targetOffset */, &bigramEntryPos)) { + return false; + } + } + } while(BigramListReadWriteUtils::hasNext(bigramFlags)); + return true; +} + +// Updates bigram target PtNode positions in the list after the placing step in GC. +bool DynamicBigramListPolicy::updateAllBigramTargetPtNodePositions(int *const bigramListPos, + const DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap *const + ptNodePositionRelocationMap) { + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos); if (usesAdditionalBuffer) { - *pos -= mBuffer->getOriginalBufferSize(); + *bigramListPos -= mBuffer->getOriginalBufferSize(); } - BigramListReadWriteUtils::BigramFlags flags; + BigramListReadWriteUtils::BigramFlags bigramFlags; + int bigramEntryCount = 0; do { - int entryPos = *pos; + if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) { + AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d", + bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT); + ASSERT(false); + return false; + } + int bigramEntryPos = *bigramListPos; + if (usesAdditionalBuffer) { + bigramEntryPos += mBuffer->getOriginalBufferSize(); + } + int bigramTargetPtNodePos; + // The buffer address can be changed after calling buffer writing methods. + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &bigramTargetPtNodePos, + bigramListPos); + if (bigramTargetPtNodePos == NOT_A_DICT_POS) { + continue; + } + if (usesAdditionalBuffer) { + bigramTargetPtNodePos += mBuffer->getOriginalBufferSize(); + } + + DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::const_iterator it = + ptNodePositionRelocationMap->find(bigramTargetPtNodePos); + if (it != ptNodePositionRelocationMap->end()) { + bigramTargetPtNodePos = it->second; + } else { + bigramTargetPtNodePos = NOT_A_DICT_POS; + } + if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags, + bigramTargetPtNodePos, &bigramEntryPos)) { + return false; + } + } while(BigramListReadWriteUtils::hasNext(bigramFlags)); + return true; +} + +bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramTargetPos, + const int probability, int *const bigramListPos) { + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos); + if (usesAdditionalBuffer) { + *bigramListPos -= mBuffer->getOriginalBufferSize(); + } + BigramListReadWriteUtils::BigramFlags bigramFlags; + int bigramEntryCount = 0; + do { + if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) { + AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d", + bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT); + ASSERT(false); + return false; + } + int entryPos = *bigramListPos; if (usesAdditionalBuffer) { entryPos += mBuffer->getOriginalBufferSize(); } + int originalBigramPos; // The buffer address can be changed after calling buffer writing methods. - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, pos); - int originalBigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( - buffer, flags, pos); + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, + bigramListPos); if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { originalBigramPos += mBuffer->getOriginalBufferSize(); } - if (followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos) == bigramPos) { + if (followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos) == bigramTargetPos) { // Update this bigram entry. const BigramListReadWriteUtils::BigramFlags updatedFlags = - BigramListReadWriteUtils::setProbabilityInFlags(flags, probability); - return mBuffer->writeUintAndAdvancePosition(updatedFlags, 1 /* size */, &entryPos); + BigramListReadWriteUtils::setProbabilityInFlags(bigramFlags, probability); + return BigramListReadWriteUtils::writeBigramEntry(mBuffer, updatedFlags, + originalBigramPos, &entryPos); } - if (BigramListReadWriteUtils::hasNext(flags)) { + if (BigramListReadWriteUtils::hasNext(bigramFlags)) { continue; } // The current last entry is found. // First, update the flags of the last entry. - const BigramListReadWriteUtils::BigramFlags updatedFlags = - BigramListReadWriteUtils::setHasNextFlag(flags); - if (!mBuffer->writeUintAndAdvancePosition(updatedFlags, 1 /* size */, &entryPos)) { + if (!BigramListReadWriteUtils::setHasNextFlag(mBuffer, true /* hasNext */, entryPos)) { return false; } if (usesAdditionalBuffer) { - *pos += mBuffer->getOriginalBufferSize(); + *bigramListPos += mBuffer->getOriginalBufferSize(); } // Then, add a new entry after the last entry. - return writeNewBigramEntry(bigramPos, probability, pos); - } while(BigramListReadWriteUtils::hasNext(flags)); + return writeNewBigramEntry(bigramTargetPos, probability, bigramListPos); + } while(BigramListReadWriteUtils::hasNext(bigramFlags)); // We return directly from the while loop. ASSERT(false); return false; } -bool DynamicBigramListPolicy::writeNewBigramEntry(const int bigramPos, const int probability, +bool DynamicBigramListPolicy::writeNewBigramEntry(const int bigramTargetPos, const int probability, int *const writingPos) { - BigramListReadWriteUtils::BigramFlags newBigramFlags; - uint32_t newBigramOffset; - int newBigramOffsetFieldSize; - if(!BigramListReadWriteUtils::createBigramEntryAndGetFlagsAndOffsetAndOffsetFieldSize( - *writingPos, bigramPos, probability, false /* hasNext */, &newBigramFlags, - &newBigramOffset, &newBigramOffsetFieldSize)) { - return false; - } - // Write bigram flags. - if (!mBuffer->writeUintAndAdvancePosition(newBigramFlags, 1 /* size */, writingPos)) { - return false; - } - // Write bigram positon offset. - if (!mBuffer->writeUintAndAdvancePosition(newBigramOffset, newBigramOffsetFieldSize, - writingPos)) { - return false; - } - return true; + // hasNext is false because we are adding a new bigram entry at the end of the bigram list. + return BigramListReadWriteUtils::createAndWriteBigramEntry(mBuffer, bigramTargetPos, + probability, false /* hasNext */, writingPos); } -bool DynamicBigramListPolicy::removeBigram(const int bigramListPos, const int targetBigramPos) { +bool DynamicBigramListPolicy::removeBigram(const int bigramListPos, const int bigramTargetPos) { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(bigramListPos); int pos = bigramListPos; if (usesAdditionalBuffer) { pos -= mBuffer->getOriginalBufferSize(); } - BigramListReadWriteUtils::BigramFlags flags; + BigramListReadWriteUtils::BigramFlags bigramFlags; + int bigramEntryCount = 0; do { + if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) { + AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d", + bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT); + ASSERT(false); + return false; + } + int bigramEntryPos = pos; + int originalBigramPos; // The buffer address can be changed after calling buffer writing methods. - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - flags = BigramListReadWriteUtils::getFlagsAndForwardPointer(buffer, &pos); - int bigramOffsetFieldPos = pos; + BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, &pos); if (usesAdditionalBuffer) { - bigramOffsetFieldPos += mBuffer->getOriginalBufferSize(); + bigramEntryPos += mBuffer->getOriginalBufferSize(); } - int originalBigramPos = BigramListReadWriteUtils::getBigramAddressAndForwardPointer( - buffer, flags, &pos); if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { originalBigramPos += mBuffer->getOriginalBufferSize(); } const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - if (bigramPos != targetBigramPos) { + if (bigramPos != bigramTargetPos) { continue; } - // Target entry is found. Write 0 into the bigram pos field to mark the bigram invalid. - const int bigramOffsetFieldSize = BigramListReadWriteUtils::attributeAddressSize(flags); - if (!mBuffer->writeUintAndAdvancePosition(0 /* data */, bigramOffsetFieldSize, - &bigramOffsetFieldPos)) { - return false; - } - return true; - } while(BigramListReadWriteUtils::hasNext(flags)); + // Target entry is found. Write an invalid target position to mark the bigram invalid. + return BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags, + NOT_A_DICT_POS /* targetOffset */, &bigramEntryPos); + } while(BigramListReadWriteUtils::hasNext(bigramFlags)); return false; } @@ -212,14 +318,14 @@ int DynamicBigramListPolicy::followBigramLinkAndGetCurrentBigramPtNodePos( } int currentPos = originalBigramPos; DynamicPatriciaTrieNodeReader nodeReader(mBuffer, this /* bigramsPolicy */, mShortcutPolicy); - nodeReader.fetchNodeInfoFromBuffer(currentPos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos); int bigramLinkCount = 0; while (nodeReader.getBigramLinkedNodePos() != NOT_A_DICT_POS) { currentPos = nodeReader.getBigramLinkedNodePos(); - nodeReader.fetchNodeInfoFromBuffer(currentPos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos); bigramLinkCount++; - if (bigramLinkCount > BIGRAM_LINK_COUNT_LIMIT) { - AKLOGI("Bigram link is invalid. start position: %d", bigramPos); + if (bigramLinkCount > CONTINUING_BIGRAM_LINK_COUNT_LIMIT) { + AKLOGE("Bigram link is invalid. start position: %d", bigramPos); ASSERT(false); return NOT_A_DICT_POS; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h index dafb62d80..8ea318a41 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h @@ -21,13 +21,13 @@ #include "defines.h" #include "suggest/core/policy/dictionary_bigrams_structure_policy.h" -#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" -#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" namespace latinime { +class BufferWithExtendableBuffer; +class DictionaryShortcutsStructurePolicy; + /* * This is a dynamic version of BigramListPolicy and supports an additional buffer. */ @@ -40,27 +40,36 @@ class DynamicBigramListPolicy : public DictionaryBigramsStructurePolicy { ~DynamicBigramListPolicy() {} void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, - int *const pos) const; + int *const bigramEntryPos) const; + + void skipAllBigrams(int *const bigramListPos) const; + + // Copy bigrams from the bigram list that starts at fromPos in mBuffer to toPos in + // bufferToWrite and advance these positions after bigram lists. This method skips invalid + // bigram entries and write the valid bigram entry count to outBigramsCount. + bool copyAllBigrams(BufferWithExtendableBuffer *const bufferToWrite, int *const fromPos, + int *const toPos, int *const outBigramsCount) const; - void skipAllBigrams(int *const pos) const; + bool updateAllBigramEntriesAndDeleteUselessEntries(int *const bigramListPos); - // Copy bigrams from the bigram list that starts at fromPos to toPos and advance these - // positions after bigram lists. This method skips invalid bigram entries and write the valid - // bigram entry count to outBigramsCount. - bool copyAllBigrams(int *const fromPos, int *const toPos, int *outBigramsCount); + bool updateAllBigramTargetPtNodePositions(int *const bigramListPos, + const DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap *const + ptNodePositionRelocationMap); - bool addNewBigramEntryToBigramList(const int bigramPos, const int probability, int *const pos); + bool addNewBigramEntryToBigramList(const int bigramTargetPos, const int probability, + int *const bigramListPos); - bool writeNewBigramEntry(const int bigramPos, const int probability, + bool writeNewBigramEntry(const int bigramTargetPos, const int probability, int *const writingPos); // Return if targetBigramPos is found or not. - bool removeBigram(const int bigramListPos, const int targetBigramPos); + bool removeBigram(const int bigramListPos, const int bigramTargetPos); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicBigramListPolicy); - static const int BIGRAM_LINK_COUNT_LIMIT; + static const int CONTINUING_BIGRAM_LINK_COUNT_LIMIT; + static const int BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT; BufferWithExtendableBuffer *const mBuffer; const DictionaryShortcutsStructurePolicy *const mShortcutPolicy; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp new file mode 100644 index 000000000..c60e45819 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h" + +namespace latinime { + +bool DynamicPatriciaTrieGcEventListeners + ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints) { + // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless + // children. + bool isUselessPtNode = !node->isTerminal(); + if (mChildrenValue > 0) { + isUselessPtNode = false; + } else if (node->isTerminal()) { + // Remove children as all children are useless. + int writingPos = node->getChildrenPosFieldPos(); + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( + mBuffer, NOT_A_DICT_POS /* childrenPosition */, &writingPos)) { + return false; + } + } + if (isUselessPtNode) { + // Current PtNode is no longer needed. Mark it as deleted. + if (!mWritingHelper->markNodeAsDeleted(node)) { + return false; + } + } else { + valueStack.back() += 1; + } + return true; +} + +// Writes dummy PtNode array size when the head of PtNode array is read. +bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onDescend(const int ptNodeArrayPos) { + mValidPtNodeCount = 0; + int writingPos = mBufferToWrite->getTailPosition(); + mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.insert( + DynamicPatriciaTrieWritingHelper::PtNodeArrayPositionRelocationMap::value_type( + ptNodeArrayPos, writingPos)); + // Writes dummy PtNode array size because arrays can have a forward link or needles PtNodes. + // This field will be updated later in onReadingPtNodeArrayTail() with actual PtNode count. + mPtNodeArraySizeFieldPos = writingPos; + return DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( + mBufferToWrite, 0 /* arraySize */, &writingPos); +} + +// Write PtNode array terminal and actual PtNode array size. +bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onReadingPtNodeArrayTail() { + int writingPos = mBufferToWrite->getTailPosition(); + // Write PtNode array terminal. + if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition( + mBufferToWrite, NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + // Write actual PtNode array size. + if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( + mBufferToWrite, mValidPtNodeCount, &mPtNodeArraySizeFieldPos)) { + return false; + } + return true; +} + +// Write valid PtNode to buffer and memorize mapping from the old position to the new position. +bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints) { + if (node->isDeleted()) { + // Current PtNode is not written in new buffer because it has been deleted. + mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert( + DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::value_type( + node->getHeadPos(), NOT_A_DICT_POS)); + return true; + } + int writingPos = mBufferToWrite->getTailPosition(); + mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert( + DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::value_type( + node->getHeadPos(), writingPos)); + mValidPtNodeCount++; + // Writes current PtNode. + return mWritingHelper->writePtNodeToBufferByCopyingPtNodeInfo(mBufferToWrite, node, + node->getParentPos(), nodeCodePoints, node->getCodePointCount(), + node->getProbability(), &writingPos); +} + +bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateAllPositionFields + ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints) { + // Updates parent position. + int parentPos = node->getParentPos(); + if (parentPos != NOT_A_DICT_POS) { + DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::const_iterator it = + mDictPositionRelocationMap->mPtNodePositionRelocationMap.find(parentPos); + if (it != mDictPositionRelocationMap->mPtNodePositionRelocationMap.end()) { + parentPos = it->second; + } + } + int writingPos = node->getHeadPos() + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE; + // Write updated parent offset. + if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(mBufferToWrite, + parentPos, node->getHeadPos(), &writingPos)) { + return false; + } + + // Updates children position. + int childrenPos = node->getChildrenPos(); + if (childrenPos != NOT_A_DICT_POS) { + DynamicPatriciaTrieWritingHelper::PtNodeArrayPositionRelocationMap::const_iterator it = + mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.find(childrenPos); + if (it != mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.end()) { + childrenPos = it->second; + } + } + writingPos = node->getChildrenPosFieldPos(); + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBufferToWrite, + childrenPos, &writingPos)) { + return false; + } + + // Updates bigram target PtNode positions in the bigram list. + int bigramsPos = node->getBigramsPos(); + if (bigramsPos != NOT_A_DICT_POS) { + if (!mBigramPolicy->updateAllBigramTargetPtNodePositions(&bigramsPos, + &mDictPositionRelocationMap->mPtNodePositionRelocationMap)) { + return false; + } + } + + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h new file mode 100644 index 000000000..4256f22fb --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h @@ -0,0 +1,178 @@ +/* + * 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. + */ + +#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H +#define LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H + +#include <vector> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "utils/hash_map_compat.h" + +namespace latinime { + +class DynamicPatriciaTrieGcEventListeners { + public: + // Updates all PtNodes that can be reached from the root. Checks if each PtNode is useless or + // not and marks useless PtNodes as deleted. Such deleted PtNodes will be discarded in the GC. + // TODO: Concatenate non-terminal PtNodes. + class TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + public: + TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( + DynamicPatriciaTrieWritingHelper *const writingHelper, + BufferWithExtendableBuffer *const buffer) + : mWritingHelper(writingHelper), mBuffer(buffer), valueStack(), + mChildrenValue(0) {} + + ~TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted() {}; + + bool onAscend() { + if (valueStack.empty()) { + return false; + } + mChildrenValue = valueStack.back(); + valueStack.pop_back(); + return true; + } + + bool onDescend(const int ptNodeArrayPos) { + valueStack.push_back(0); + return true; + } + + bool onReadingPtNodeArrayTail() { return true; } + + bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS( + TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted); + + DynamicPatriciaTrieWritingHelper *const mWritingHelper; + BufferWithExtendableBuffer *const mBuffer; + std::vector<int> valueStack; + int mChildrenValue; + }; + + // Updates all bigram entries that are held by valid PtNodes. This removes useless bigram + // entries. + class TraversePolicyToUpdateBigramProbability + : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + public: + TraversePolicyToUpdateBigramProbability(DynamicBigramListPolicy *const bigramPolicy) + : mBigramPolicy(bigramPolicy) {} + + bool onAscend() { return true; } + + bool onDescend(const int ptNodeArrayPos) { return true; } + + bool onReadingPtNodeArrayTail() { return true; } + + bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints) { + if (!node->isDeleted()) { + int pos = node->getBigramsPos(); + if (pos != NOT_A_DICT_POS) { + if (!mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries(&pos)) { + return false; + } + } + } + return true; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateBigramProbability); + + DynamicBigramListPolicy *const mBigramPolicy; + }; + + class TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + public: + TraversePolicyToPlaceAndWriteValidPtNodesToBuffer( + DynamicPatriciaTrieWritingHelper *const writingHelper, + BufferWithExtendableBuffer *const bufferToWrite, + DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const + dictPositionRelocationMap) + : mWritingHelper(writingHelper), mBufferToWrite(bufferToWrite), + mDictPositionRelocationMap(dictPositionRelocationMap), mValidPtNodeCount(0), + mPtNodeArraySizeFieldPos(NOT_A_DICT_POS) {}; + + bool onAscend() { return true; } + + bool onDescend(const int ptNodeArrayPos); + + bool onReadingPtNodeArrayTail(); + + bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToPlaceAndWriteValidPtNodesToBuffer); + + DynamicPatriciaTrieWritingHelper *const mWritingHelper; + BufferWithExtendableBuffer *const mBufferToWrite; + DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const + mDictPositionRelocationMap; + int mValidPtNodeCount; + int mPtNodeArraySizeFieldPos; + }; + + class TraversePolicyToUpdateAllPositionFields + : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + public: + TraversePolicyToUpdateAllPositionFields( + DynamicPatriciaTrieWritingHelper *const writingHelper, + DynamicBigramListPolicy *const bigramPolicy, + BufferWithExtendableBuffer *const bufferToWrite, + const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const + dictPositionRelocationMap) + : mWritingHelper(writingHelper), mBigramPolicy(bigramPolicy), + mBufferToWrite(bufferToWrite), + mDictPositionRelocationMap(dictPositionRelocationMap) {}; + + bool onAscend() { return true; } + + bool onDescend(const int ptNodeArrayPos) { return true; } + + bool onReadingPtNodeArrayTail() { return true; } + + bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateAllPositionFields); + + DynamicPatriciaTrieWritingHelper *const mWritingHelper; + DynamicBigramListPolicy *const mBigramPolicy; + BufferWithExtendableBuffer *const mBufferToWrite; + const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const + mDictPositionRelocationMap; + }; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieGcEventListeners); +}; +} // namespace latinime +#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp index 737098423..456352c17 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp @@ -23,26 +23,27 @@ namespace latinime { -void DynamicPatriciaTrieNodeReader::fetchNodeInfoFromBufferAndProcessMovedNode(const int nodePos, - const int maxCodePointCount, int *const outCodePoints) { - if (nodePos < 0 || nodePos >= mBuffer->getTailPosition()) { +void DynamicPatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProcessMovedPtNode( + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) { + if (ptNodePos < 0 || ptNodePos >= mBuffer->getTailPosition()) { AKLOGE("Fetching PtNode info form invalid dictionary position: %d, dictionary size: %d", - nodePos, mBuffer->getTailPosition()); + ptNodePos, mBuffer->getTailPosition()); ASSERT(false); invalidatePtNodeInfo(); return; } - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(nodePos); + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodePos); const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); - int pos = nodePos; - mHeadPos = nodePos; + int pos = ptNodePos; + mHeadPos = ptNodePos; if (usesAdditionalBuffer) { pos -= mBuffer->getOriginalBufferSize(); } mFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); - const int parentPos = - DynamicPatriciaTrieReadingUtils::getParentPosAndAdvancePosition(dictBuf, &pos); - mParentPos = (parentPos != 0) ? nodePos + parentPos : NOT_A_DICT_POS; + const int parentPosOffset = + DynamicPatriciaTrieReadingUtils::getParentPtNodePosOffsetAndAdvancePosition(dictBuf, + &pos); + mParentPos = DynamicPatriciaTrieReadingUtils::getParentPtNodePos(parentPosOffset, mHeadPos); if (outCodePoints != 0) { mCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( dictBuf, mFlags, maxCodePointCount, outCodePoints, &pos); @@ -99,7 +100,8 @@ void DynamicPatriciaTrieNodeReader::fetchNodeInfoFromBufferAndProcessMovedNode(c // Read destination node if the read node is a moved node. if (DynamicPatriciaTrieReadingUtils::isMoved(mFlags)) { // The destination position is stored at the same place as the parent position. - fetchNodeInfoFromBufferAndProcessMovedNode(mParentPos, maxCodePointCount, outCodePoints); + fetchPtNodeInfoFromBufferAndProcessMovedPtNode(mParentPos, maxCodePointCount, + outCodePoints); } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h index 6ef5f5813..3b36d425f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h @@ -48,17 +48,17 @@ class DynamicPatriciaTrieNodeReader { ~DynamicPatriciaTrieNodeReader() {} - // Reads node information from dictionary buffer and updates members with the information. - AK_FORCE_INLINE void fetchNodeInfoFromBuffer(const int nodePos) { - fetchNodeInfoFromBufferAndGetNodeCodePoints(nodePos , 0 /* maxCodePointCount */, - 0 /* outCodePoints */); + // Reads PtNode information from dictionary buffer and updates members with the information. + AK_FORCE_INLINE void fetchNodeInfoInBufferFromPtNodePos(const int ptNodePos) { + fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(ptNodePos , + 0 /* maxCodePointCount */, 0 /* outCodePoints */); } - AK_FORCE_INLINE void fetchNodeInfoFromBufferAndGetNodeCodePoints(const int nodePos, - const int maxCodePointCount, int *const outCodePoints) { + AK_FORCE_INLINE void fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints( + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) { mSiblingPos = NOT_A_DICT_POS; mBigramLinkedNodePos = NOT_A_DICT_POS; - fetchNodeInfoFromBufferAndProcessMovedNode(nodePos, maxCodePointCount, outCodePoints); + fetchPtNodeInfoFromBufferAndProcessMovedPtNode(ptNodePos, maxCodePointCount, outCodePoints); } // HeadPos is different from NodePos when the current PtNode is a moved PtNode. @@ -154,8 +154,8 @@ class DynamicPatriciaTrieNodeReader { int mBigramPos; int mSiblingPos; - void fetchNodeInfoFromBufferAndProcessMovedNode(const int nodePos, const int maxCodePointCount, - int *const outCodePoints); + void fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos, + const int maxCodePointCount, int *const outCodePoints); void invalidatePtNodeInfo(); }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp index 3cfbfd85b..42397c19e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp @@ -35,7 +35,7 @@ void DynamicPatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const d } DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithNodeArrayPos(dicNode->getChildrenPos()); + readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPos()); const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); while (!readingHelper.isEnd()) { childDicNodes->pushLeavingChild(dicNode, nodeReader->getHeadPos(), @@ -48,7 +48,7 @@ void DynamicPatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const d } int DynamicPatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( - const int nodePos, const int maxCodePointCount, int *const outCodePoints, + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) const { // This method traverses parent nodes from the terminal by following parent pointers; thus, // node code points are stored in the buffer in the reverse order. @@ -56,9 +56,9 @@ int DynamicPatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCoun DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); // First, read the terminal node and get its probability. - readingHelper.initWithNodePos(nodePos); + readingHelper.initWithPtNodePos(ptNodePos); if (!readingHelper.isValidTerminalNode()) { - // Node at the nodePos is not a valid terminal node. + // Node at the ptNodePos is not a valid terminal node. *outUnigramProbability = NOT_A_PROBABILITY; return 0; } @@ -67,7 +67,7 @@ int DynamicPatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCoun // Then, following parent node link to the dictionary root and fetch node code points. while (!readingHelper.isEnd()) { if (readingHelper.getTotalCodePointCount() > maxCodePointCount) { - // The nodePos is not a valid terminal node position in the dictionary. + // The ptNodePos is not a valid terminal node position in the dictionary. *outUnigramProbability = NOT_A_PROBABILITY; return 0; } @@ -98,7 +98,7 @@ int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const in } DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithNodeArrayPos(getRootPosition()); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); while (!readingHelper.isEnd()) { const int matchedCodePointCount = readingHelper.getPrevTotalCodePointCount(); @@ -148,39 +148,39 @@ int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability, } } -int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_PROBABILITY; } DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoFromBuffer(nodePos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); if (nodeReader.isDeleted() || nodeReader.isBlacklisted() || nodeReader.isNotAWord()) { return NOT_A_PROBABILITY; } return getProbability(nodeReader.getProbability(), NOT_A_PROBABILITY); } -int DynamicPatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int DynamicPatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; } DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoFromBuffer(nodePos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); if (nodeReader.isDeleted()) { return NOT_A_DICT_POS; } return nodeReader.getShortcutPos(); } -int DynamicPatriciaTriePolicy::getBigramsPositionOfNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int DynamicPatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; } DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoFromBuffer(nodePos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); if (nodeReader.isDeleted()) { return NOT_A_DICT_POS; } @@ -195,7 +195,7 @@ bool DynamicPatriciaTriePolicy::addUnigramWord(const int *const word, const int } DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithNodeArrayPos(getRootPosition()); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, &mBigramListPolicy, &mShortcutListPolicy); return writingHelper.addUnigramWord(&readingHelper, word, length, probability); @@ -248,7 +248,9 @@ void DynamicPatriciaTriePolicy::flush(const char *const filePath) { AKLOGI("Warning: flush() is called for non-updatable dictionary."); return; } - // TODO: Implement. + DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, + &mBigramListPolicy, &mShortcutListPolicy); + writingHelper.writeToDictFile(filePath, &mHeaderPolicy); } void DynamicPatriciaTriePolicy::flushWithGC(const char *const filePath) { @@ -256,7 +258,9 @@ void DynamicPatriciaTriePolicy::flushWithGC(const char *const filePath) { AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); return; } - // TODO: Implement. + DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, + &mBigramListPolicy, &mShortcutListPolicy); + writingHelper.writeToDictFileWithGC(getRootPosition(), filePath, &mHeaderPolicy); } bool DynamicPatriciaTriePolicy::needsToRunGC() const { @@ -264,8 +268,8 @@ bool DynamicPatriciaTriePolicy::needsToRunGC() const { AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary."); return false; } - // TODO: Implement. - return false; + // TODO: Implement more properly. + return mBufferWithExtendableBuffer.isNearSizeLimit(); } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h index 2cbb0ff3b..06d8095d8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h @@ -33,7 +33,7 @@ class DicNodeVector; class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: DynamicPatriciaTriePolicy(const MmappedBuffer *const buffer) - : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer()), + : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()), mBufferWithExtendableBuffer(mBuffer->getBuffer() + mHeaderPolicy.getSize(), mBuffer->getBufferSize() - mHeaderPolicy.getSize()), mShortcutListPolicy(&mBufferWithExtendableBuffer), @@ -51,7 +51,7 @@ class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { DicNodeVector *const childDicNodes) const; int getCodePointsAndProbabilityAndReturnCodePointCount( - const int terminalNodePos, const int maxCodePointCount, int *const outCodePoints, + const int terminalPtNodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) const; int getTerminalNodePositionOfWord(const int *const inWord, @@ -59,11 +59,11 @@ class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getProbability(const int unigramProbability, const int bigramProbability) const; - int getUnigramProbabilityOfPtNode(const int nodePos) const; + int getUnigramProbabilityOfPtNode(const int ptNodePos) const; - int getShortcutPositionOfNode(const int nodePos) const; + int getShortcutPositionOfPtNode(const int ptNodePos) const; - int getBigramsPositionOfNode(const int nodePos) const; + int getBigramsPositionOfPtNode(const int ptNodePos) const; const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { return &mHeaderPolicy; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp index a0b5be6a4..f4a2ef389 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp @@ -23,36 +23,167 @@ namespace latinime { // To avoid infinite loop caused by invalid or malicious forward links. const int DynamicPatriciaTrieReadingHelper::MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP = 100000; const int DynamicPatriciaTrieReadingHelper::MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP = 100000; +const size_t DynamicPatriciaTrieReadingHelper::MAX_READING_STATE_STACK_SIZE = MAX_WORD_LENGTH; + +// Visits all PtNodes in post-order depth first manner. +// For example, visits c -> b -> y -> x -> a for the following dictionary: +// a _ b _ c +// \ x _ y +bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPostorderDepthFirstManner( + TraversingEventListener *const listener) { + bool alreadyVisitedChildren = false; + // Descend from the root to the root PtNode array. + if (!listener->onDescend(getPosOfLastPtNodeArrayHead())) { + return false; + } + while (!isEnd()) { + if (!alreadyVisitedChildren) { + if (mNodeReader.hasChildren()) { + // Move to the first child. + if (!listener->onDescend(mNodeReader.getChildrenPos())) { + return false; + } + pushReadingStateToStack(); + readChildNode(); + } else { + alreadyVisitedChildren = true; + } + } else { + if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) { + return false; + } + readNextSiblingNode(); + if (isEnd()) { + // All PtNodes in current linked PtNode arrays have been visited. + // Return to the parent. + if (!listener->onReadingPtNodeArrayTail()) { + return false; + } + if (mReadingStateStack.size() <= 0) { + break; + } + if (!listener->onAscend()) { + return false; + } + popReadingStateFromStack(); + alreadyVisitedChildren = true; + } else { + // Process sibling PtNode. + alreadyVisitedChildren = false; + } + } + } + // Ascend from the root PtNode array to the root. + if (!listener->onAscend()) { + return false; + } + return !isError(); +} + +// Visits all PtNodes in PtNode array level pre-order depth first manner, which is the same order +// that PtNodes are written in the dictionary buffer. +// For example, visits a -> b -> x -> c -> y for the following dictionary: +// a _ b _ c +// \ x _ y +bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + TraversingEventListener *const listener) { + bool alreadyVisitedAllPtNodesInArray = false; + bool alreadyVisitedChildren = false; + // Descend from the root to the root PtNode array. + if (!listener->onDescend(getPosOfLastPtNodeArrayHead())) { + return false; + } + pushReadingStateToStack(); + while (!isEnd()) { + if (alreadyVisitedAllPtNodesInArray) { + if (alreadyVisitedChildren) { + // Move to next sibling PtNode's children. + readNextSiblingNode(); + if (isEnd()) { + // Return to the parent PTNode. + if (!listener->onAscend()) { + return false; + } + if (mReadingStateStack.size() <= 0) { + break; + } + popReadingStateFromStack(); + alreadyVisitedChildren = true; + alreadyVisitedAllPtNodesInArray = true; + } else { + alreadyVisitedChildren = false; + } + } else { + if (mNodeReader.hasChildren()) { + // Move to the first child. + if (!listener->onDescend(mNodeReader.getChildrenPos())) { + return false; + } + pushReadingStateToStack(); + readChildNode(); + // Push state to return the head of PtNode array. + pushReadingStateToStack(); + alreadyVisitedAllPtNodesInArray = false; + alreadyVisitedChildren = false; + } else { + alreadyVisitedChildren = true; + } + } + } else { + if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) { + return false; + } + readNextSiblingNode(); + if (isEnd()) { + if (!listener->onReadingPtNodeArrayTail()) { + return false; + } + // Return to the head of current PtNode array. + popReadingStateFromStack(); + alreadyVisitedAllPtNodesInArray = true; + } + } + } + popReadingStateFromStack(); + // Ascend from the root PtNode array to the root. + if (!listener->onAscend()) { + return false; + } + return !isError(); +} // Read node array size and process empty node arrays. Nodes and arrays are counted up in this // method to avoid an infinite loop. -void DynamicPatriciaTrieReadingHelper::nextNodeArray() { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mPos); +void DynamicPatriciaTrieReadingHelper::nextPtNodeArray() { + mReadingState.mPosOfLastPtNodeArrayHead = mReadingState.mPos; + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos); const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { - mPos -= mBuffer->getOriginalBufferSize(); + mReadingState.mPos -= mBuffer->getOriginalBufferSize(); } - mNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(dictBuf, - &mPos); + mReadingState.mNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( + dictBuf, &mReadingState.mPos); if (usesAdditionalBuffer) { - mPos += mBuffer->getOriginalBufferSize(); + mReadingState.mPos += mBuffer->getOriginalBufferSize(); } // Count up nodes and node arrays to avoid infinite loop. - mTotalNodeCount += mNodeCount; - mNodeArrayCount++; - if (mNodeCount < 0 || mTotalNodeCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP - || mNodeArrayCount > MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP) { + mReadingState.mTotalNodeCount += mReadingState.mNodeCount; + mReadingState.mNodeArrayCount++; + if (mReadingState.mNodeCount < 0 + || mReadingState.mTotalNodeCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP + || mReadingState.mNodeArrayCount > MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP) { // Invalid dictionary. AKLOGI("Invalid dictionary. nodeCount: %d, totalNodeCount: %d, MAX_CHILD_COUNT: %d" "nodeArrayCount: %d, MAX_NODE_ARRAY_COUNT: %d", - mNodeCount, mTotalNodeCount, MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP, - mNodeArrayCount, MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP); + mReadingState.mNodeCount, mReadingState.mTotalNodeCount, + MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP, mReadingState.mNodeArrayCount, + MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP); ASSERT(false); mIsError = true; - mPos = NOT_A_DICT_POS; + mReadingState.mPos = NOT_A_DICT_POS; return; } - if (mNodeCount == 0) { + if (mReadingState.mNodeCount == 0) { // Empty node array. Try following forward link. followForwardLink(); } @@ -60,24 +191,24 @@ void DynamicPatriciaTrieReadingHelper::nextNodeArray() { // Follow the forward link and read the next node array if exists. void DynamicPatriciaTrieReadingHelper::followForwardLink() { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mPos); + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos); const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { - mPos -= mBuffer->getOriginalBufferSize(); + mReadingState.mPos -= mBuffer->getOriginalBufferSize(); } const int forwardLinkPosition = - DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(dictBuf, mPos); + DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(dictBuf, mReadingState.mPos); if (usesAdditionalBuffer) { - mPos += mBuffer->getOriginalBufferSize(); + mReadingState.mPos += mBuffer->getOriginalBufferSize(); } - mPosOfLastForwardLinkField = mPos; + mReadingState.mPosOfLastForwardLinkField = mReadingState.mPos; if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) { // Follow the forward link. - mPos += forwardLinkPosition; - nextNodeArray(); + mReadingState.mPos += forwardLinkPosition; + nextPtNodeArray(); } else { // All node arrays have been read. - mPos = NOT_A_DICT_POS; + mReadingState.mPos = NOT_A_DICT_POS; } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h index 120fd7699..b033eee05 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h @@ -17,6 +17,9 @@ #ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H #define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H +#include <cstddef> +#include <vector> + #include "defines.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" @@ -34,12 +37,35 @@ class DictionaryShortcutsStructurePolicy; */ class DynamicPatriciaTrieReadingHelper { public: + class TraversingEventListener { + public: + virtual ~TraversingEventListener() {}; + + // Returns whether the event handling was succeeded or not. + virtual bool onAscend() = 0; + + // Returns whether the event handling was succeeded or not. + virtual bool onDescend(const int ptNodeArrayPos) = 0; + + // Returns whether the event handling was succeeded or not. + virtual bool onReadingPtNodeArrayTail() = 0; + + // Returns whether the event handling was succeeded or not. + virtual bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, + const int *const nodeCodePoints) = 0; + + protected: + TraversingEventListener() {}; + + private: + DISALLOW_COPY_AND_ASSIGN(TraversingEventListener); + }; + DynamicPatriciaTrieReadingHelper(const BufferWithExtendableBuffer *const buffer, const DictionaryBigramsStructurePolicy *const bigramsPolicy, const DictionaryShortcutsStructurePolicy *const shortcutsPolicy) - : mIsError(false), mPos(NOT_A_DICT_POS), mNodeCount(0), mPrevTotalCodePointCount(0), - mTotalNodeCount(0), mNodeArrayCount(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS), - mBuffer(buffer), mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy) {} + : mIsError(false), mReadingState(), mBuffer(buffer), + mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy), mReadingStateStack() {} ~DynamicPatriciaTrieReadingHelper() {} @@ -48,41 +74,43 @@ class DynamicPatriciaTrieReadingHelper { } AK_FORCE_INLINE bool isEnd() const { - return mPos == NOT_A_DICT_POS; + return mReadingState.mPos == NOT_A_DICT_POS; } - // Initialize reading state with the head position of a node array. - AK_FORCE_INLINE void initWithNodeArrayPos(const int nodeArrayPos) { - if (nodeArrayPos == NOT_A_DICT_POS) { - mPos = NOT_A_DICT_POS; + // Initialize reading state with the head position of a PtNode array. + AK_FORCE_INLINE void initWithPtNodeArrayPos(const int ptNodeArrayPos) { + if (ptNodeArrayPos == NOT_A_DICT_POS) { + mReadingState.mPos = NOT_A_DICT_POS; } else { mIsError = false; - mPos = nodeArrayPos; - mNodeCount = 0; - mPrevTotalCodePointCount = 0; - mTotalNodeCount = 0; - mNodeArrayCount = 0; - mPosOfLastForwardLinkField = NOT_A_DICT_POS; - nextNodeArray(); + mReadingState.mPos = ptNodeArrayPos; + mReadingState.mPrevTotalCodePointCount = 0; + mReadingState.mTotalNodeCount = 0; + mReadingState.mNodeArrayCount = 0; + mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; + mReadingStateStack.clear(); + nextPtNodeArray(); if (!isEnd()) { - fetchNodeInfo(); + fetchPtNodeInfo(); } } } // Initialize reading state with the head position of a node. - AK_FORCE_INLINE void initWithNodePos(const int nodePos) { - if (nodePos == NOT_A_DICT_POS) { - mPos = NOT_A_DICT_POS; + AK_FORCE_INLINE void initWithPtNodePos(const int ptNodePos) { + if (ptNodePos == NOT_A_DICT_POS) { + mReadingState.mPos = NOT_A_DICT_POS; } else { mIsError = false; - mPos = nodePos; - mNodeCount = 1; - mPrevTotalCodePointCount = 0; - mTotalNodeCount = 1; - mNodeArrayCount = 1; - mPosOfLastForwardLinkField = NOT_A_DICT_POS; - fetchNodeInfo(); + mReadingState.mPos = ptNodePos; + mReadingState.mNodeCount = 1; + mReadingState.mPrevTotalCodePointCount = 0; + mReadingState.mTotalNodeCount = 1; + mReadingState.mNodeArrayCount = 1; + mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; + mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS; + mReadingStateStack.clear(); + fetchPtNodeInfo(); } } @@ -100,12 +128,12 @@ class DynamicPatriciaTrieReadingHelper { // Return code point count exclude the last read node's code points. AK_FORCE_INLINE int getPrevTotalCodePointCount() const { - return mPrevTotalCodePointCount; + return mReadingState.mPrevTotalCodePointCount; } // Return code point count include the last read node's code points. AK_FORCE_INLINE int getTotalCodePointCount() const { - return mPrevTotalCodePointCount + mNodeReader.getCodePointCount(); + return mReadingState.mPrevTotalCodePointCount + mNodeReader.getCodePointCount(); } AK_FORCE_INLINE void fetchMergedNodeCodePointsInReverseOrder( @@ -121,85 +149,136 @@ class DynamicPatriciaTrieReadingHelper { } AK_FORCE_INLINE void readNextSiblingNode() { - mNodeCount -= 1; - mPos = mNodeReader.getSiblingNodePos(); - if (mNodeCount <= 0) { + mReadingState.mNodeCount -= 1; + mReadingState.mPos = mNodeReader.getSiblingNodePos(); + if (mReadingState.mNodeCount <= 0) { // All nodes in the current node array have been read. followForwardLink(); if (!isEnd()) { - fetchNodeInfo(); + fetchPtNodeInfo(); } } else { - fetchNodeInfo(); + fetchPtNodeInfo(); } } // Read the first child node of the current node. AK_FORCE_INLINE void readChildNode() { if (mNodeReader.hasChildren()) { - mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); - mTotalNodeCount = 0; - mNodeArrayCount = 0; - mPos = mNodeReader.getChildrenPos(); - mPosOfLastForwardLinkField = NOT_A_DICT_POS; + mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); + mReadingState.mTotalNodeCount = 0; + mReadingState.mNodeArrayCount = 0; + mReadingState.mPos = mNodeReader.getChildrenPos(); + mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; // Read children node array. - nextNodeArray(); + nextPtNodeArray(); if (!isEnd()) { - fetchNodeInfo(); + fetchPtNodeInfo(); } } else { - mPos = NOT_A_DICT_POS; + mReadingState.mPos = NOT_A_DICT_POS; } } // Read the parent node of the current node. AK_FORCE_INLINE void readParentNode() { if (mNodeReader.getParentPos() != NOT_A_DICT_POS) { - mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); - mTotalNodeCount = 1; - mNodeArrayCount = 1; - mNodeCount = 1; - mPos = mNodeReader.getParentPos(); - mPosOfLastForwardLinkField = NOT_A_DICT_POS; - fetchNodeInfo(); + mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); + mReadingState.mTotalNodeCount = 1; + mReadingState.mNodeArrayCount = 1; + mReadingState.mNodeCount = 1; + mReadingState.mPos = mNodeReader.getParentPos(); + mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; + mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS; + fetchPtNodeInfo(); } else { - mPos = NOT_A_DICT_POS; + mReadingState.mPos = NOT_A_DICT_POS; } } AK_FORCE_INLINE int getPosOfLastForwardLinkField() const { - return mPosOfLastForwardLinkField; + return mReadingState.mPosOfLastForwardLinkField; + } + + AK_FORCE_INLINE int getPosOfLastPtNodeArrayHead() const { + return mReadingState.mPosOfLastPtNodeArrayHead; + } + + AK_FORCE_INLINE void reloadCurrentPtNodeInfo() { + if (!isEnd()) { + fetchPtNodeInfo(); + } } + bool traverseAllPtNodesInPostorderDepthFirstManner(TraversingEventListener *const listener); + + bool traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + TraversingEventListener *const listener); + private: DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieReadingHelper); + class ReadingState { + public: + // Note that copy constructor and assignment operator are used for this class to use + // std::vector. + ReadingState() : mPos(NOT_A_DICT_POS), mNodeCount(0), mPrevTotalCodePointCount(0), + mTotalNodeCount(0), mNodeArrayCount(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS), + mPosOfLastPtNodeArrayHead(NOT_A_DICT_POS) {} + + int mPos; + // Node count of a node array. + int mNodeCount; + int mPrevTotalCodePointCount; + int mTotalNodeCount; + int mNodeArrayCount; + int mPosOfLastForwardLinkField; + int mPosOfLastPtNodeArrayHead; + }; + static const int MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP; static const int MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP; + static const size_t MAX_READING_STATE_STACK_SIZE; bool mIsError; - int mPos; - // Node count of a node array. - int mNodeCount; - int mPrevTotalCodePointCount; - int mTotalNodeCount; - int mNodeArrayCount; - int mPosOfLastForwardLinkField; + ReadingState mReadingState; const BufferWithExtendableBuffer *const mBuffer; DynamicPatriciaTrieNodeReader mNodeReader; int mMergedNodeCodePoints[MAX_WORD_LENGTH]; + std::vector<ReadingState> mReadingStateStack; - void nextNodeArray(); + void nextPtNodeArray(); void followForwardLink(); - AK_FORCE_INLINE void fetchNodeInfo() { - mNodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(mPos, MAX_WORD_LENGTH, - mMergedNodeCodePoints); + AK_FORCE_INLINE void fetchPtNodeInfo() { + mNodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(mReadingState.mPos, + MAX_WORD_LENGTH, mMergedNodeCodePoints); if (mNodeReader.getCodePointCount() <= 0) { // Empty node is not allowed. mIsError = true; - mPos = NOT_A_DICT_POS; + mReadingState.mPos = NOT_A_DICT_POS; + } + } + + AK_FORCE_INLINE void pushReadingStateToStack() { + if (mReadingStateStack.size() > MAX_READING_STATE_STACK_SIZE) { + AKLOGI("Reading state stack overflow. Max size: %d", MAX_READING_STATE_STACK_SIZE); + ASSERT(false); + mIsError = true; + mReadingState.mPos = NOT_A_DICT_POS; + } else { + mReadingStateStack.push_back(mReadingState); + } + } + + AK_FORCE_INLINE void popReadingStateFromStack() { + if (mReadingStateStack.empty()) { + mReadingState.mPos = NOT_A_DICT_POS; + } else { + mReadingState = mReadingStateStack.back(); + mReadingStateStack.pop_back(); + fetchPtNodeInfo(); } } }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp index 8428c0b15..d68446db6 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp @@ -28,24 +28,42 @@ const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_NOT_MOVED = 0xC0; const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_MOVED = 0x40; const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_DELETED = 0x80; +// TODO: Make DICT_OFFSET_ZERO_OFFSET = 0. +// Currently, DICT_OFFSET_INVALID is 0 in Java side but offset can be 0 during GC. So, the maximum +// value of offsets, which is 0x7FFFFF is used to represent 0 offset. +const int DptReadingUtils::DICT_OFFSET_INVALID = 0; +const int DptReadingUtils::DICT_OFFSET_ZERO_OFFSET = 0x7FFFFF; + /* static */ int DptReadingUtils::getForwardLinkPosition(const uint8_t *const buffer, const int pos) { int linkAddressPos = pos; return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos); } -/* static */ int DptReadingUtils::getParentPosAndAdvancePosition(const uint8_t *const buffer, - int *const pos) { +/* static */ int DptReadingUtils::getParentPtNodePosOffsetAndAdvancePosition( + const uint8_t *const buffer, int *const pos) { return ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); } +/* static */ int DptReadingUtils::getParentPtNodePos(const int parentOffset, const int ptNodePos) { + if (parentOffset == DICT_OFFSET_INVALID) { + return NOT_A_DICT_POS; + } else if (parentOffset == DICT_OFFSET_ZERO_OFFSET) { + return ptNodePos; + } else { + return parentOffset + ptNodePos; + } +} + /* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition( const uint8_t *const buffer, int *const pos) { const int base = *pos; const int offset = ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); - if (offset == 0) { - // 0 offset means that the node does not have children. + if (offset == DICT_OFFSET_INVALID) { + // The PtNode does not have children. return NOT_A_DICT_POS; + } else if (offset == DICT_OFFSET_ZERO_OFFSET) { + return base; } else { return base + offset; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h index db5f9b1bd..67c3cc57e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h @@ -27,13 +27,19 @@ class DynamicPatriciaTrieReadingUtils { public: typedef uint8_t NodeFlags; + static const int DICT_OFFSET_INVALID; + static const int DICT_OFFSET_ZERO_OFFSET; + static int getForwardLinkPosition(const uint8_t *const buffer, const int pos); static AK_FORCE_INLINE bool isValidForwardLinkPosition(const int forwardLinkAddress) { return forwardLinkAddress != 0; } - static int getParentPosAndAdvancePosition(const uint8_t *const buffer, int *const pos); + static int getParentPtNodePosOffsetAndAdvancePosition(const uint8_t *const buffer, + int *const pos); + + static int getParentPtNodePos(const int parentOffset, const int ptNodePos); static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer, int *const pos); diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp index 311d31e5d..a51ae5e1d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp @@ -16,17 +16,27 @@ #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" +#include <cstdio> +#include <cstring> + #include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h" +#include "utils/hash_map_compat.h" namespace latinime { const int DynamicPatriciaTrieWritingHelper::CHILDREN_POSITION_FIELD_SIZE = 3; +const char *const DynamicPatriciaTrieWritingHelper::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = + ".tmp"; +// TODO: Make MAX_DICTIONARY_SIZE 8MB. +const size_t DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024; bool DynamicPatriciaTrieWritingHelper::addUnigramWord( DynamicPatriciaTrieReadingHelper *const readingHelper, @@ -83,7 +93,7 @@ bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const const int probability) { int mMergedNodeCodePoints[MAX_WORD_LENGTH]; DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - nodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(word0Pos, MAX_WORD_LENGTH, + nodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(word0Pos, MAX_WORD_LENGTH, mMergedNodeCodePoints); // Move node to add bigram entry. const int newNodePos = mBuffer->getTailPosition(); @@ -91,13 +101,13 @@ bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const return false; } int writingPos = newNodePos; - // Write a new PtNode using original PtNode's info to the tail of the dictionary. - if (!writePtNodeToBufferByCopyingPtNodeInfo(&nodeReader, nodeReader.getParentPos(), + // Write a new PtNode using original PtNode's info to the tail of the dictionary in mBuffer. + if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, &nodeReader, nodeReader.getParentPos(), mMergedNodeCodePoints, nodeReader.getCodePointCount(), nodeReader.getProbability(), &writingPos)) { return false; } - nodeReader.fetchNodeInfoFromBuffer(newNodePos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(newNodePos); if (nodeReader.getBigramsPos() != NOT_A_DICT_POS) { // Insert a new bigram entry into the existing bigram list. int bigramListPos = nodeReader.getBigramsPos(); @@ -124,13 +134,56 @@ bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const // Remove a bigram relation from word0Pos to word1Pos. bool DynamicPatriciaTrieWritingHelper::removeBigramWords(const int word0Pos, const int word1Pos) { DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - nodeReader.fetchNodeInfoFromBuffer(word0Pos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(word0Pos); if (nodeReader.getBigramsPos() == NOT_A_DICT_POS) { return false; } return mBigramPolicy->removeBigram(nodeReader.getBigramsPos(), word1Pos); } +void DynamicPatriciaTrieWritingHelper::writeToDictFile(const char *const fileName, + const HeaderPolicy *const headerPolicy) { + BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); + if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */)) { + return; + } + flushAllToFile(fileName, &headerBuffer, mBuffer); +} + +void DynamicPatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeArrayPos, + const char *const fileName, const HeaderPolicy *const headerPolicy) { + BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); + if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, true /* updatesLastUpdatedTime */)) { + return; + } + BufferWithExtendableBuffer newDictBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */, + MAX_DICTIONARY_SIZE); + if (!runGC(rootPtNodeArrayPos, &newDictBuffer)) { + return; + } + flushAllToFile(fileName, &headerBuffer, &newDictBuffer); +} + +bool DynamicPatriciaTrieWritingHelper::markNodeAsDeleted( + const DynamicPatriciaTrieNodeReader *const nodeToUpdate) { + int pos = nodeToUpdate->getHeadPos(); + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPatriciaTrieReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */, + true /* isDeleted */); + int writingPos = nodeToUpdate->getHeadPos(); + // Update flags. + return DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags, + &writingPos); +} + bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition( const DynamicPatriciaTrieNodeReader *const originalNode, const int movedPos, const int bigramLinkedNodePos) { @@ -153,9 +206,8 @@ bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition( return false; } // Update moved position, which is stored in the parent offset field. - const int movedPosOffset = movedPos - originalNode->getHeadPos(); - if (!DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition( - mBuffer, movedPosOffset, &writingPos)) { + if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( + mBuffer, movedPos, originalNode->getHeadPos(), &writingPos)) { return false; } // Update bigram linked node position, which is stored in the children position field. @@ -168,13 +220,12 @@ bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition( // Update children's parent position. DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy); const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); - readingHelper.initWithNodeArrayPos(originalNode->getChildrenPos()); + readingHelper.initWithPtNodeArrayPos(originalNode->getChildrenPos()); while (!readingHelper.isEnd()) { - const int childPtNodeWrittenPos = nodeReader->getHeadPos(); - const int parentOffset = movedPos - childPtNodeWrittenPos; - int parentOffsetFieldPos = childPtNodeWrittenPos + 1 /* Flags */; - if (!DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition( - mBuffer, parentOffset, &parentOffsetFieldPos)) { + int parentOffsetFieldPos = nodeReader->getHeadPos() + + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE; + if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( + mBuffer, movedPos, nodeReader->getHeadPos(), &parentOffsetFieldPos)) { // Parent offset cannot be written because of a bug or a broken dictionary; thus, // we give up to update dictionary. return false; @@ -186,7 +237,8 @@ bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition( } // Write new PtNode at writingPos. -bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(const bool isBlacklisted, +bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer( + BufferWithExtendableBuffer *const bufferToWrite, const bool isBlacklisted, const bool isNotAWord, const int parentPos, const int *const codePoints, const int codePointCount, const int probability, const int childrenPos, const int originalBigramListPos, const int originalShortcutListPos, @@ -194,38 +246,38 @@ bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(const boo const int nodePos = *writingPos; // Write dummy flags. The Node flags are updated with appropriate flags at the last step of the // PtNode writing. - if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, 0 /* nodeFlags */, - writingPos)) { + if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(bufferToWrite, + 0 /* nodeFlags */, writingPos)) { return false; } // Calculate a parent offset and write the offset. - const int parentOffset = (parentPos != NOT_A_DICT_POS) ? parentPos - nodePos : NOT_A_DICT_POS; - if (!DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition(mBuffer, - parentOffset, writingPos)) { + if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(bufferToWrite, + parentPos, nodePos, writingPos)) { return false; } // Write code points - if (!DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(mBuffer, codePoints, - codePointCount, writingPos)) { + if (!DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(bufferToWrite, + codePoints, codePointCount, writingPos)) { return false; } // Write probability when the probability is a valid probability, which means this node is // terminal. if (probability != NOT_A_PROBABILITY) { - if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer, + if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(bufferToWrite, probability, writingPos)) { return false; } } // Write children position - if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, + if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(bufferToWrite, childrenPos, writingPos)) { return false; } // Copy shortcut list when the originalShortcutListPos is valid dictionary position. if (originalShortcutListPos != NOT_A_DICT_POS) { int fromPos = originalShortcutListPos; - if (!mShortcutPolicy->copyAllShortcutsAndReturnIfSucceededOrNot(&fromPos, writingPos)) { + if (!mShortcutPolicy->copyAllShortcutsAndReturnIfSucceededOrNot(bufferToWrite, &fromPos, + writingPos)) { return false; } } @@ -233,7 +285,7 @@ bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(const boo int bigramCount = 0; if (originalBigramListPos != NOT_A_DICT_POS) { int fromPos = originalBigramListPos; - if (!mBigramPolicy->copyAllBigrams(&fromPos, writingPos, &bigramCount)) { + if (!mBigramPolicy->copyAllBigrams(bufferToWrite, &fromPos, writingPos, &bigramCount)) { return false; } } @@ -245,27 +297,29 @@ bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(const boo bigramCount > 0 /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */, CHILDREN_POSITION_FIELD_SIZE); int flagsFieldPos = nodePos; - if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, nodeFlags, + if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(bufferToWrite, nodeFlags, &flagsFieldPos)) { return false; } return true; } -bool DynamicPatriciaTrieWritingHelper::writePtNodeToBuffer(const int parentPos, +bool DynamicPatriciaTrieWritingHelper::writePtNodeToBuffer( + BufferWithExtendableBuffer *const bufferToWrite, const int parentPos, const int *const codePoints, const int codePointCount, const int probability, int *const writingPos) { - return writePtNodeWithFullInfoToBuffer(false /* isBlacklisted */, false /* isNotAWord */, - parentPos, codePoints, codePointCount, probability, + return writePtNodeWithFullInfoToBuffer(bufferToWrite, false /* isBlacklisted */, + false /* isNotAWord */, parentPos, codePoints, codePointCount, probability, NOT_A_DICT_POS /* childrenPos */, NOT_A_DICT_POS /* originalBigramsPos */, NOT_A_DICT_POS /* originalShortcutPos */, writingPos); } bool DynamicPatriciaTrieWritingHelper::writePtNodeToBufferByCopyingPtNodeInfo( + BufferWithExtendableBuffer *const bufferToWrite, const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos, const int *const codePoints, const int codePointCount, const int probability, int *const writingPos) { - return writePtNodeWithFullInfoToBuffer(originalNode->isBlacklisted(), + return writePtNodeWithFullInfoToBuffer(bufferToWrite, originalNode->isBlacklisted(), originalNode->isNotAWord(), parentPos, codePoints, codePointCount, probability, originalNode->getChildrenPos(), originalNode->getBigramsPos(), originalNode->getShortcutPos(), writingPos); @@ -299,8 +353,9 @@ bool DynamicPatriciaTrieWritingHelper::setPtNodeProbability( if (!markNodeAsMovedAndSetPosition(originalPtNode, movedPos, movedPos)) { return false; } - if (!writePtNodeToBufferByCopyingPtNodeInfo(originalPtNode, originalPtNode->getParentPos(), - codePoints, originalPtNode->getCodePointCount(), probability, &movedPos)) { + if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, originalPtNode, + originalPtNode->getParentPos(), codePoints, originalPtNode->getCodePointCount(), + probability, &movedPos)) { return false; } } @@ -328,8 +383,8 @@ bool DynamicPatriciaTrieWritingHelper::createNewPtNodeArrayWithAChildPtNode( 1 /* arraySize */, &writingPos)) { return false; } - if (!writePtNodeToBuffer(parentPtNodePos, nodeCodePoints, nodeCodePointCount, probability, - &writingPos)) { + if (!writePtNodeToBuffer(mBuffer, parentPtNodePos, nodeCodePoints, nodeCodePointCount, + probability, &writingPos)) { return false; } if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, @@ -358,8 +413,9 @@ bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( // Write the 1st part of the reallocating node. The children position will be updated later // with actual children position. const int newProbability = addsExtraChild ? NOT_A_PROBABILITY : probabilityOfNewPtNode; - if (!writePtNodeToBuffer(reallocatingPtNode->getParentPos(), reallocatingPtNodeCodePoints, - overlappingCodePointCount, newProbability, &writingPos)) { + if (!writePtNodeToBuffer(mBuffer, reallocatingPtNode->getParentPos(), + reallocatingPtNodeCodePoints, overlappingCodePointCount, newProbability, + &writingPos)) { return false; } const int actualChildrenPos = writingPos; @@ -371,14 +427,15 @@ bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( } // Write the 2nd part of the reallocating node. const int secondPartOfReallocatedPtNodePos = writingPos; - if (!writePtNodeToBufferByCopyingPtNodeInfo(reallocatingPtNode, firstPartOfReallocatedPtNodePos, + if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, reallocatingPtNode, + firstPartOfReallocatedPtNodePos, reallocatingPtNodeCodePoints + overlappingCodePointCount, reallocatingPtNode->getCodePointCount() - overlappingCodePointCount, reallocatingPtNode->getProbability(), &writingPos)) { return false; } if (addsExtraChild) { - if (!writePtNodeToBuffer(firstPartOfReallocatedPtNodePos, + if (!writePtNodeToBuffer(mBuffer, firstPartOfReallocatedPtNodePos, newNodeCodePoints + overlappingCodePointCount, newNodeCodePointCount - overlappingCodePointCount, probabilityOfNewPtNode, &writingPos)) { @@ -396,7 +453,7 @@ bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( } // Load node info. Information of the 1st part will be fetched. DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - nodeReader.fetchNodeInfoFromBuffer(firstPartOfReallocatedPtNodePos); + nodeReader.fetchNodeInfoInBufferFromPtNodePos(firstPartOfReallocatedPtNodePos); // Update children position. int childrenPosFieldPos = nodeReader.getChildrenPosFieldPos(); if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, @@ -406,4 +463,107 @@ bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( return true; } +// TODO: Create a struct which contains header, body and etc... and use here as an argument. +void DynamicPatriciaTrieWritingHelper::flushAllToFile(const char *const fileName, + BufferWithExtendableBuffer *const dictHeader, + BufferWithExtendableBuffer *const dictBody) const { + const int tmpFileNameBufSize = strlen(fileName) + + strlen(TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE) + 1 /* terminator */; + // Name of a temporary file used for writing that is a connected string of original name and + // TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE. + char tmpFileName[tmpFileNameBufSize]; + snprintf(tmpFileName, tmpFileNameBufSize, "%s%s", fileName, + TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); + FILE *const file = fopen(tmpFileName, "wb"); + if (!file) { + AKLOGI("Dictionary file %s cannnot be opened.", tmpFileName); + ASSERT(false); + return; + } + // Write the dictionary header. + if (!writeBufferToFilePointer(file, dictHeader)) { + remove(tmpFileName); + AKLOGI("Dictionary header cannnot be written. size: %d", dictHeader->getTailPosition()); + ASSERT(false); + return; + } + // Write the dictionary body. + if (!writeBufferToFilePointer(file, dictBody)) { + remove(tmpFileName); + AKLOGI("Dictionary body cannnot be written. size: %d", dictBody->getTailPosition()); + ASSERT(false); + return; + } + fclose(file); + rename(tmpFileName, fileName); +} + +// This closes file pointer when an error is caused and returns whether the writing was succeeded +// or not. +bool DynamicPatriciaTrieWritingHelper::writeBufferToFilePointer(FILE *const file, + const BufferWithExtendableBuffer *const buffer) const { + const int originalBufSize = buffer->getOriginalBufferSize(); + if (originalBufSize > 0 && fwrite(buffer->getBuffer(false /* usesAdditionalBuffer */), + originalBufSize, 1, file) < 1) { + fclose(file); + return false; + } + const int additionalBufSize = buffer->getTailPosition() - buffer->getOriginalBufferSize(); + if (additionalBufSize > 0 && fwrite(buffer->getBuffer(true /* usesAdditionalBuffer */), + additionalBufSize, 1, file) < 1) { + fclose(file); + return false; + } + return true; +} + +bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, + BufferWithExtendableBuffer *const bufferToWrite) { + DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy); + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPatriciaTrieGcEventListeners + ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( + this, mBuffer); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) { + return false; + } + + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateBigramProbability + traversePolicyToUpdateBigramProbability(mBigramPolicy); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateBigramProbability)) { + return false; + } + + // Mapping from positions in mBuffer to positions in bufferToWrite. + DictPositionRelocationMap dictPositionRelocationMap; + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + traversePolicyToPlaceAndWriteValidPtNodesToBuffer(this, bufferToWrite, + &dictPositionRelocationMap); + if (!readingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToPlaceAndWriteValidPtNodesToBuffer)) { + return false; + } + + // Create policy instance for the GCed dictionary. + DynamicShortcutListPolicy newDictShortcutPolicy(bufferToWrite); + DynamicBigramListPolicy newDictBigramPolicy(bufferToWrite, &newDictShortcutPolicy); + // Create reading helper for the GCed dictionary. + DynamicPatriciaTrieReadingHelper newDictReadingHelper(bufferToWrite, &newDictBigramPolicy, + &newDictShortcutPolicy); + newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateAllPositionFields + traversePolicyToUpdateAllPositionFields(this, &newDictBigramPolicy, bufferToWrite, + &dictPositionRelocationMap); + if (!newDictReadingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToUpdateAllPositionFields)) { + return false; + } + return true; +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h index 20e35abcf..028fa6075 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h @@ -17,7 +17,11 @@ #ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H #define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H +#include <cstdio> +#include <stdint.h> + #include "defines.h" +#include "utils/hash_map_compat.h" namespace latinime { @@ -26,9 +30,24 @@ class DynamicBigramListPolicy; class DynamicPatriciaTrieNodeReader; class DynamicPatriciaTrieReadingHelper; class DynamicShortcutListPolicy; +class HeaderPolicy; class DynamicPatriciaTrieWritingHelper { public: + typedef hash_map_compat<int, int> PtNodeArrayPositionRelocationMap; + typedef hash_map_compat<int, int> PtNodePositionRelocationMap; + struct DictPositionRelocationMap { + public: + DictPositionRelocationMap() + : mPtNodeArrayPositionRelocationMap(), mPtNodePositionRelocationMap() {} + + PtNodeArrayPositionRelocationMap mPtNodeArrayPositionRelocationMap; + PtNodePositionRelocationMap mPtNodePositionRelocationMap; + + private: + DISALLOW_COPY_AND_ASSIGN(DictPositionRelocationMap); + }; + DynamicPatriciaTrieWritingHelper(BufferWithExtendableBuffer *const buffer, DynamicBigramListPolicy *const bigramPolicy, DynamicShortcutListPolicy *const shortcutPolicy) @@ -46,10 +65,28 @@ class DynamicPatriciaTrieWritingHelper { // Remove a bigram relation from word0Pos to word1Pos. bool removeBigramWords(const int word0Pos, const int word1Pos); + void writeToDictFile(const char *const fileName, const HeaderPolicy *const headerPolicy); + + void writeToDictFileWithGC(const int rootPtNodeArrayPos, const char *const fileName, + const HeaderPolicy *const headerPolicy); + + // CAVEAT: This method must be called only from inner classes of + // DynamicPatriciaTrieGcEventListeners. + bool markNodeAsDeleted(const DynamicPatriciaTrieNodeReader *const nodeToUpdate); + + // CAVEAT: This method must be called only from this class or inner classes of + // DynamicPatriciaTrieGcEventListeners. + bool writePtNodeToBufferByCopyingPtNodeInfo(BufferWithExtendableBuffer *const bufferToWrite, + const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos, + const int *const codePoints, const int codePointCount, const int probability, + int *const writingPos); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingHelper); static const int CHILDREN_POSITION_FIELD_SIZE; + static const char *const TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE; + static const size_t MAX_DICTIONARY_SIZE; BufferWithExtendableBuffer *const mBuffer; DynamicBigramListPolicy *const mBigramPolicy; @@ -58,18 +95,15 @@ class DynamicPatriciaTrieWritingHelper { bool markNodeAsMovedAndSetPosition(const DynamicPatriciaTrieNodeReader *const nodeToUpdate, const int movedPos, const int bigramLinkedNodePos); - bool writePtNodeWithFullInfoToBuffer(const bool isBlacklisted, const bool isNotAWord, + bool writePtNodeWithFullInfoToBuffer(BufferWithExtendableBuffer *const bufferToWrite, + const bool isBlacklisted, const bool isNotAWord, const int parentPos, const int *const codePoints, const int codePointCount, const int probability, const int childrenPos, const int originalBigramListPos, const int originalShortcutListPos, int *const writingPos); - bool writePtNodeToBuffer(const int parentPos, const int *const codePoints, - const int codePointCount, const int probability, int *const writingPos); - - bool writePtNodeToBufferByCopyingPtNodeInfo( - const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos, - const int *const codePoints, const int codePointCount, const int probability, - int *const writingPos); + bool writePtNodeToBuffer(BufferWithExtendableBuffer *const bufferToWrite, + const int parentPos, const int *const codePoints, const int codePointCount, + const int probability, int *const writingPos); bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, const int nodeCodePointCount, const int probability, int *const forwardLinkFieldPos); @@ -89,6 +123,15 @@ class DynamicPatriciaTrieWritingHelper { const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount, const int probabilityOfNewPtNode, const int *const newNodeCodePoints, const int newNodeCodePointCount); + + void flushAllToFile(const char *const fileName, + BufferWithExtendableBuffer *const dictHeader, + BufferWithExtendableBuffer *const dictBody) const; + + bool writeBufferToFilePointer(FILE *const file, + const BufferWithExtendableBuffer *const buffer) const; + + bool runGC(const int rootPtNodeArrayPos, BufferWithExtendableBuffer *const bufferToWrite); }; } // namespace latinime #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp index b261e594d..5a3983776 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp @@ -39,18 +39,20 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; /* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, int *const forwardLinkFieldPos) { - const int offset = (forwardLinkPos != NOT_A_DICT_POS) ? - forwardLinkPos - (*forwardLinkFieldPos) : 0; - return writeDictOffset(buffer, offset, forwardLinkFieldPos); + return writeDictOffset(buffer, forwardLinkPos, (*forwardLinkFieldPos), forwardLinkFieldPos); } /* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const size_t arraySize, int *const arraySizeFieldPos) { - if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) { + // Currently, all array size field to be created has LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE to + // simplify updating process. + // TODO: Use SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE for small arrays. + /*if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) { return buffer->writeUintAndAdvancePosition(arraySize, SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE, arraySizeFieldPos); - } else if (arraySize <= MAX_PTNODE_ARRAY_SIZE) { + } else */ + if (arraySize <= MAX_PTNODE_ARRAY_SIZE) { uint32_t data = arraySize | LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; return buffer->writeUintAndAdvancePosition(data, LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE, arraySizeFieldPos); @@ -69,11 +71,10 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; } // Note that parentOffset is offset from node's head position. -/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentOffsetAndAdvancePosition( - BufferWithExtendableBuffer *const buffer, const int parentOffset, +/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int parentPos, const int basePos, int *const parentPosFieldPos) { - int offset = (parentOffset != NOT_A_DICT_POS) ? parentOffset : 0; - return writeDictOffset(buffer, offset, parentPosFieldPos); + return writeDictOffset(buffer, parentPos, basePos, parentPosFieldPos); } /* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition( @@ -106,13 +107,19 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; /* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int childrenPosition, int *const childrenPositionFieldPos) { - int offset = (childrenPosition != NOT_A_DICT_POS) ? - childrenPosition - (*childrenPositionFieldPos) : 0; - return writeDictOffset(buffer, offset, childrenPositionFieldPos); + return writeDictOffset(buffer, childrenPosition, (*childrenPositionFieldPos), + childrenPositionFieldPos); } /* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset( - BufferWithExtendableBuffer *const buffer, const int offset, int *const offsetFieldPos) { + BufferWithExtendableBuffer *const buffer, const int targetPos, const int basePos, + int *const offsetFieldPos) { + int offset = targetPos - basePos; + if (targetPos == NOT_A_DICT_POS) { + offset = DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID; + } else if (offset == 0) { + offset = DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET; + } if (offset > MAX_DICT_OFFSET_VALUE || offset < MIN_DICT_OFFSET_VALUE) { AKLOGI("offset cannot be written because the offset is too large or too small: %d", offset); diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h index 183ede444..a37e9fb3d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h @@ -28,6 +28,8 @@ class BufferWithExtendableBuffer; class DynamicPatriciaTrieWritingUtils { public: + static const int NODE_FLAG_FIELD_SIZE; + static bool writeForwardLinkPositionAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, int *const forwardLinkFieldPos); @@ -39,8 +41,8 @@ class DynamicPatriciaTrieWritingUtils { const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos); - static bool writeParentOffsetAndAdvancePosition(BufferWithExtendableBuffer *const buffer, - const int parentPosition, int *const parentPosFieldPos); + static bool writeParentPosOffsetAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int parentPosition, const int basePos, int *const parentPosFieldPos); static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, const int *const codePoints, const int codePointCount, int *const codePointFieldPos); @@ -63,11 +65,10 @@ class DynamicPatriciaTrieWritingUtils { static const int MAX_DICT_OFFSET_VALUE; static const int MIN_DICT_OFFSET_VALUE; static const int DICT_OFFSET_NEGATIVE_FLAG; - static const int NODE_FLAG_FIELD_SIZE; static const int PROBABILITY_FIELD_SIZE; - static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int offset, - int *const offsetFieldPos); + static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int targetPos, + const int basePos, int *const offsetFieldPos); }; } // namespace latinime #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp index 196da5c97..47ace23a1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -17,6 +17,8 @@ #include "suggest/policyimpl/dictionary/header/header_policy.h" #include <cstddef> +#include <cstdio> +#include <ctime> namespace latinime { @@ -36,7 +38,7 @@ void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *out } std::vector<int> keyCodePointVector; insertCharactersIntoVector(key, &keyCodePointVector); - HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector); + HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector); if (it == mAttributeMap.end()) { // The key was not found. outValue[0] = '?'; @@ -85,7 +87,7 @@ int HeaderPolicy::readLastUpdatedTime() const { bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outValue) const { std::vector<int> keyVector; insertCharactersIntoVector(key, &keyVector); - HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector); + HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector); if (it == mAttributeMap.end()) { // The key was not found. return false; @@ -94,10 +96,56 @@ bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outV return true; } -/* static */ HeaderReadingUtils::AttributeMap HeaderPolicy::createAttributeMapAndReadAllAttributes( - const uint8_t *const dictBuf) { - HeaderReadingUtils::AttributeMap attributeMap; - HeaderReadingUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap); +bool HeaderPolicy::writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite, + const bool updatesLastUpdatedTime) const { + int writingPos = 0; + if (!HeaderReadWriteUtils::writeDictionaryVersion(bufferToWrite, mDictFormatVersion, + &writingPos)) { + return false; + } + if (!HeaderReadWriteUtils::writeDictionaryFlags(bufferToWrite, mDictionaryFlags, + &writingPos)) { + return false; + } + // Temporarily writes a dummy header size. + int headerSizeFieldPos = writingPos; + if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, 0 /* size */, + &writingPos)) { + return false; + } + if (updatesLastUpdatedTime) { + // Set current time as a last updated time. + HeaderReadWriteUtils::AttributeMap attributeMapTowrite(mAttributeMap); + std::vector<int> updatedTimekey; + insertCharactersIntoVector(LAST_UPDATED_TIME_KEY, &updatedTimekey); + const time_t currentTime = time(NULL); + std::vector<int> updatedTimeValue; + char charBuf[LARGEST_INT_DIGIT_COUNT + 1]; + snprintf(charBuf, LARGEST_INT_DIGIT_COUNT + 1, "%ld", currentTime); + insertCharactersIntoVector(charBuf, &updatedTimeValue); + attributeMapTowrite[updatedTimekey] = updatedTimeValue; + if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &attributeMapTowrite, + &writingPos)) { + return false; + } + } else { + if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &mAttributeMap, + &writingPos)) { + return false; + } + } + // Writes an actual header size. + if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, writingPos, + &headerSizeFieldPos)) { + return false; + } + return true; +} + +/* static */ HeaderReadWriteUtils::AttributeMap + HeaderPolicy::createAttributeMapAndReadAllAttributes(const uint8_t *const dictBuf) { + HeaderReadWriteUtils::AttributeMap attributeMap; + HeaderReadWriteUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap); return attributeMap; } 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 930b475c7..6b396f3f2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -22,15 +22,18 @@ #include "defines.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" -#include "suggest/policyimpl/dictionary/header/header_reading_utils.h" +#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" namespace latinime { class HeaderPolicy : public DictionaryHeaderStructurePolicy { public: - explicit HeaderPolicy(const uint8_t *const dictBuf) - : mDictBuf(dictBuf), mDictionaryFlags(HeaderReadingUtils::getFlags(dictBuf)), - mSize(HeaderReadingUtils::getHeaderSize(dictBuf)), + explicit HeaderPolicy(const uint8_t *const dictBuf, const int dictSize) + : mDictBuf(dictBuf), + mDictFormatVersion(FormatUtils::detectFormatVersion(dictBuf, dictSize)), + mDictionaryFlags(HeaderReadWriteUtils::getFlags(dictBuf)), + mSize(HeaderReadWriteUtils::getHeaderSize(dictBuf)), mAttributeMap(createAttributeMapAndReadAllAttributes(mDictBuf)), mMultiWordCostMultiplier(readMultipleWordCostMultiplier()), mUsesForgettingCurve(readUsesForgettingCurveFlag()), @@ -43,16 +46,15 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { } AK_FORCE_INLINE bool supportsDynamicUpdate() const { - return HeaderReadingUtils::supportsDynamicUpdate(mDictionaryFlags); + return HeaderReadWriteUtils::supportsDynamicUpdate(mDictionaryFlags); } AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const { - return HeaderReadingUtils::requiresGermanUmlautProcessing(mDictionaryFlags); + return HeaderReadWriteUtils::requiresGermanUmlautProcessing(mDictionaryFlags); } AK_FORCE_INLINE bool requiresFrenchLigatureProcessing() const { - return HeaderReadingUtils::requiresFrenchLigatureProcessing( - mDictionaryFlags); + return HeaderReadWriteUtils::requiresFrenchLigatureProcessing(mDictionaryFlags); } AK_FORCE_INLINE float getMultiWordCostMultiplier() const { @@ -70,6 +72,9 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { void readHeaderValueOrQuestionMark(const char *const key, int *outValue, int outValueSize) const; + bool writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite, + const bool updatesLastUpdatedTime) const; + private: DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPolicy); @@ -80,9 +85,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE; const uint8_t *const mDictBuf; - const HeaderReadingUtils::DictionaryFlags mDictionaryFlags; + const FormatUtils::FORMAT_VERSION mDictFormatVersion; + const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags; const int mSize; - HeaderReadingUtils::AttributeMap mAttributeMap; + HeaderReadWriteUtils::AttributeMap mAttributeMap; const float mMultiWordCostMultiplier; const bool mUsesForgettingCurve; const int mLastUpdatedTime; @@ -95,7 +101,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { bool getAttributeValueAsInt(const char *const key, int *const outValue) const; - static HeaderReadingUtils::AttributeMap createAttributeMapAndReadAllAttributes( + static HeaderReadWriteUtils::AttributeMap createAttributeMapAndReadAllAttributes( const uint8_t *const dictBuf); static int parseIntAttributeValue(const std::vector<int> *const attributeValue); 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 new file mode 100644 index 000000000..80fe88671 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" + +#include <vector> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" + +namespace latinime { + +const int HeaderReadWriteUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256; +const int HeaderReadWriteUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256; + +const int HeaderReadWriteUtils::HEADER_MAGIC_NUMBER_SIZE = 4; +const int HeaderReadWriteUtils::HEADER_DICTIONARY_VERSION_SIZE = 2; +const int HeaderReadWriteUtils::HEADER_FLAG_SIZE = 2; +const int HeaderReadWriteUtils::HEADER_SIZE_FIELD_SIZE = 4; + +const HeaderReadWriteUtils::DictionaryFlags HeaderReadWriteUtils::NO_FLAGS = 0; +// Flags for special processing +// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or +// something very bad (like, the apocalypse) will happen. Please update both at the same time. +const HeaderReadWriteUtils::DictionaryFlags + HeaderReadWriteUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; +const HeaderReadWriteUtils::DictionaryFlags + HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2; +const HeaderReadWriteUtils::DictionaryFlags + HeaderReadWriteUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4; + +/* static */ int HeaderReadWriteUtils::getHeaderSize(const uint8_t *const dictBuf) { + // See the format of the header in the comment in + // BinaryDictionaryFormatUtils::detectFormatVersion() + return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE + + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE); +} + +/* static */ HeaderReadWriteUtils::DictionaryFlags + HeaderReadWriteUtils::getFlags(const uint8_t *const dictBuf) { + return ByteArrayUtils::readUint16(dictBuf, + HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE); +} + +/* static */ void HeaderReadWriteUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf, + AttributeMap *const headerAttributes) { + const int headerSize = getHeaderSize(dictBuf); + int pos = getHeaderOptionsPosition(); + if (pos == NOT_A_DICT_POS) { + // The header doesn't have header options. + return; + } + int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH]; + int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH]; + while (pos < headerSize) { + const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, + MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos); + std::vector<int> key; + key.insert(key.end(), keyBuffer, keyBuffer + keyLength); + const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, + MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos); + std::vector<int> value; + value.insert(value.end(), valueBuffer, valueBuffer + valueLength); + headerAttributes->insert(AttributeMap::value_type(key, value)); + } +} + +/* static */ bool HeaderReadWriteUtils::writeDictionaryVersion( + BufferWithExtendableBuffer *const buffer, const FormatUtils::FORMAT_VERSION version, + int *const writingPos) { + if (!buffer->writeUintAndAdvancePosition(FormatUtils::MAGIC_NUMBER, HEADER_MAGIC_NUMBER_SIZE, + writingPos)) { + return false; + } + switch (version) { + case FormatUtils::VERSION_2: + // Version 2 dictionary writing is not supported. + return false; + case FormatUtils::VERSION_3: + return buffer->writeUintAndAdvancePosition(3 /* data */, + HEADER_DICTIONARY_VERSION_SIZE, writingPos); + default: + return false; + } +} + +/* static */ bool HeaderReadWriteUtils::writeDictionaryFlags( + BufferWithExtendableBuffer *const buffer, const DictionaryFlags flags, + int *const writingPos) { + return buffer->writeUintAndAdvancePosition(flags, HEADER_FLAG_SIZE, writingPos); +} + +/* static */ bool HeaderReadWriteUtils::writeDictionaryHeaderSize( + BufferWithExtendableBuffer *const buffer, const int size, int *const writingPos) { + return buffer->writeUintAndAdvancePosition(size, HEADER_SIZE_FIELD_SIZE, writingPos); +} + +/* static */ bool HeaderReadWriteUtils::writeHeaderAttributes( + BufferWithExtendableBuffer *const buffer, const AttributeMap *const headerAttributes, + int *const writingPos) { + for (AttributeMap::const_iterator it = headerAttributes->begin(); + it != headerAttributes->end(); ++it) { + // Write a key. + if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(), + true /* writesTerminator */, writingPos)) { + return false; + } + // Write a value. + if (!buffer->writeCodePointsAndAdvancePosition(&(it->second.at(0)), it->second.size(), + true /* writesTerminator */, writingPos)) { + return false; + } + } + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h index 5716198fb..6cce73375 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h @@ -14,18 +14,21 @@ * limitations under the License. */ -#ifndef LATINIME_HEADER_READING_UTILS_H -#define LATINIME_HEADER_READING_UTILS_H +#ifndef LATINIME_HEADER_READ_WRITE_UTILS_H +#define LATINIME_HEADER_READ_WRITE_UTILS_H #include <map> #include <stdint.h> #include <vector> #include "defines.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" namespace latinime { -class HeaderReadingUtils { +class BufferWithExtendableBuffer; + +class HeaderReadWriteUtils { public: typedef uint16_t DictionaryFlags; typedef std::map<std::vector<int>, std::vector<int> > AttributeMap; @@ -54,8 +57,20 @@ class HeaderReadingUtils { static void fetchAllHeaderAttributes(const uint8_t *const dictBuf, AttributeMap *const headerAttributes); + static bool writeDictionaryVersion(BufferWithExtendableBuffer *const buffer, + const FormatUtils::FORMAT_VERSION version, int *const writingPos); + + static bool writeDictionaryFlags(BufferWithExtendableBuffer *const buffer, + const DictionaryFlags flags, int *const writingPos); + + static bool writeDictionaryHeaderSize(BufferWithExtendableBuffer *const buffer, + const int size, int *const writingPos); + + static bool writeHeaderAttributes(BufferWithExtendableBuffer *const buffer, + const AttributeMap *const headerAttributes, int *const writingPos); + private: - DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadingUtils); + DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadWriteUtils); static const int MAX_ATTRIBUTE_KEY_LENGTH; static const int MAX_ATTRIBUTE_VALUE_LENGTH; @@ -75,4 +90,4 @@ class HeaderReadingUtils { static const DictionaryFlags CONTAINS_BIGRAMS_FLAG; }; } -#endif /* LATINIME_HEADER_READING_UTILS_H */ +#endif /* LATINIME_HEADER_READ_WRITE_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp deleted file mode 100644 index 186c043c1..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "suggest/policyimpl/dictionary/header/header_reading_utils.h" - -#include <vector> - -#include "defines.h" -#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" - -namespace latinime { - -const int HeaderReadingUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256; -const int HeaderReadingUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256; - -const int HeaderReadingUtils::HEADER_MAGIC_NUMBER_SIZE = 4; -const int HeaderReadingUtils::HEADER_DICTIONARY_VERSION_SIZE = 2; -const int HeaderReadingUtils::HEADER_FLAG_SIZE = 2; -const int HeaderReadingUtils::HEADER_SIZE_FIELD_SIZE = 4; - -const HeaderReadingUtils::DictionaryFlags HeaderReadingUtils::NO_FLAGS = 0; -// Flags for special processing -// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or -// something very bad (like, the apocalypse) will happen. Please update both at the same time. -const HeaderReadingUtils::DictionaryFlags - HeaderReadingUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; -const HeaderReadingUtils::DictionaryFlags - HeaderReadingUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2; -const HeaderReadingUtils::DictionaryFlags - HeaderReadingUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4; - -/* static */ int HeaderReadingUtils::getHeaderSize(const uint8_t *const dictBuf) { - // See the format of the header in the comment in - // BinaryDictionaryFormatUtils::detectFormatVersion() - return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE - + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE); -} - -/* static */ HeaderReadingUtils::DictionaryFlags - HeaderReadingUtils::getFlags(const uint8_t *const dictBuf) { - return ByteArrayUtils::readUint16(dictBuf, - HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE); -} - -/* static */ void HeaderReadingUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf, - AttributeMap *const headerAttributes) { - const int headerSize = getHeaderSize(dictBuf); - int pos = getHeaderOptionsPosition(); - if (pos == NOT_A_DICT_POS) { - // The header doesn't have header options. - return; - } - int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH]; - int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH]; - while (pos < headerSize) { - const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, - MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos); - std::vector<int> key; - key.insert(key.end(), keyBuffer, keyBuffer + keyLength); - const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf, - MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos); - std::vector<int> value; - value.insert(value.end(), valueBuffer, valueBuffer + valueLength); - headerAttributes->insert(AttributeMap::value_type(key, value)); - } -} - -} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp index e6cff431b..5269795a4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp @@ -49,7 +49,7 @@ void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, // with a z, it's the last PtNode of the root array, so all children addresses will be smaller // than the position we look for, and we have to descend the z node). /* Parameters : - * nodePos: the byte position of the terminal PtNode of the word we are searching for (this is + * ptNodePos: the byte position of the terminal PtNode of the word we are searching for (this is * what is stored as the "bigram position" in each bigram) * outCodePoints: an array to write the found word, with MAX_WORD_LENGTH size. * outUnigramProbability: a pointer to an int to write the probability into. @@ -57,7 +57,7 @@ void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, */ // TODO: Split this function to be more readable int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( - const int nodePos, const int maxCodePointCount, int *const outCodePoints, + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) const { int pos = getRootPosition(); int wordPos = 0; @@ -78,7 +78,7 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); const int character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition( mDictRoot, &pos); - if (nodePos == startPos) { + if (ptNodePos == startPos) { // We found the position. Copy the rest of the code points in the buffer and return // the length. outCodePoints[wordPos] = character; @@ -121,7 +121,7 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( // Here comes the tricky part. First, read the children position. const int childrenPos = PatriciaTrieReadingUtils ::readChildrenPositionAndAdvancePosition(mDictRoot, flags, ¤tPos); - if (childrenPos > nodePos) { + if (childrenPos > ptNodePos) { // If the children pos is greater than the position, it means the previous // PtNode, which position is stored in lastCandidatePtNodePos, was the right // one. @@ -213,7 +213,7 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( } } - // If we have looked through all the PtNodes and found no match, the nodePos is + // If we have looked through all the PtNodes and found no match, the ptNodePos is // not the position of a terminal in this dictionary. return 0; } @@ -319,11 +319,11 @@ int PatriciaTriePolicy::getProbability(const int unigramProbability, } } -int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_PROBABILITY; } - int pos = nodePos; + int pos = ptNodePos; const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); if (!PatriciaTrieReadingUtils::isTerminal(flags)) { @@ -341,11 +341,11 @@ int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int nodePos) const { mDictRoot, &pos), NOT_A_PROBABILITY); } -int PatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; } - int pos = nodePos; + int pos = ptNodePos; const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); if (!PatriciaTrieReadingUtils::hasShortcutTargets(flags)) { @@ -361,11 +361,11 @@ int PatriciaTriePolicy::getShortcutPositionOfNode(const int nodePos) const { return pos; } -int PatriciaTriePolicy::getBigramsPositionOfNode(const int nodePos) const { - if (nodePos == NOT_A_DICT_POS) { +int PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; } - int pos = nodePos; + int pos = ptNodePos; const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); if (!PatriciaTrieReadingUtils::hasBigrams(flags)) { @@ -385,8 +385,8 @@ int PatriciaTriePolicy::getBigramsPositionOfNode(const int nodePos) const { } int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNode, - const int nodePos, DicNodeVector *childDicNodes) const { - int pos = nodePos; + const int ptNodePos, DicNodeVector *childDicNodes) const { + int pos = ptNodePos; const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos); int mergedNodeCodePoints[MAX_WORD_LENGTH]; @@ -404,7 +404,7 @@ int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNod if (PatriciaTrieReadingUtils::hasBigrams(flags)) { getBigramsStructurePolicy()->skipAllBigrams(&pos); } - childDicNodes->pushLeavingChild(dicNode, nodePos, childrenPos, probability, + childDicNodes->pushLeavingChild(dicNode, ptNodePos, childrenPos, probability, PatriciaTrieReadingUtils::isTerminal(flags), PatriciaTrieReadingUtils::hasChildrenInFlags(flags), PatriciaTrieReadingUtils::isBlacklisted(flags) || diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h index cee3e4ab2..19155f938 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h @@ -34,7 +34,7 @@ class DicNodeVector; class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: PatriciaTriePolicy(const MmappedBuffer *const buffer) - : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer()), + : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()), mDictRoot(mBuffer->getBuffer() + mHeaderPolicy.getSize()), mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot) {} @@ -58,11 +58,11 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getProbability(const int unigramProbability, const int bigramProbability) const; - int getUnigramProbabilityOfPtNode(const int nodePos) const; + int getUnigramProbabilityOfPtNode(const int ptNodePos) const; - int getShortcutPositionOfNode(const int nodePos) const; + int getShortcutPositionOfPtNode(const int ptNodePos) const; - int getBigramsPositionOfNode(const int nodePos) const; + int getBigramsPositionOfPtNode(const int ptNodePos) const; const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { return &mHeaderPolicy; @@ -121,7 +121,7 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { const BigramListPolicy mBigramListPolicy; const ShortcutListPolicy mShortcutListPolicy; - int createAndGetLeavingChildNode(const DicNode *const dicNode, const int nodePos, + int createAndGetLeavingChildNode(const DicNode *const dicNode, const int ptNodePos, DicNodeVector *const childDicNodes) const; }; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h index 1803c09cb..bd3211f6a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h @@ -31,7 +31,7 @@ namespace latinime { */ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { public: - explicit DynamicShortcutListPolicy(BufferWithExtendableBuffer *const buffer) + explicit DynamicShortcutListPolicy(const BufferWithExtendableBuffer *const buffer) : mBuffer(buffer) {} ~DynamicShortcutListPolicy() {} @@ -82,18 +82,20 @@ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { } } - // Copy shortcuts from the shortcut list that starts at fromPos to toPos and advance these - // positions after the shortcut lists. This returns whether the copy was succeeded or not. - bool copyAllShortcutsAndReturnIfSucceededOrNot(int *const fromPos, int *const toPos) { + // Copy shortcuts from the shortcut list that starts at fromPos in mBuffer to toPos in + // bufferToWrite and advance these positions after the shortcut lists. This returns whether + // the copy was succeeded or not. + bool copyAllShortcutsAndReturnIfSucceededOrNot(BufferWithExtendableBuffer *const bufferToWrite, + int *const fromPos, int *const toPos) const { const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { *fromPos -= mBuffer->getOriginalBufferSize(); } const int shortcutListSize = ShortcutListReadingUtils - ::getShortcutListSizeAndForwardPointer(buffer, fromPos); + ::getShortcutListSizeAndForwardPointer(mBuffer->getBuffer(usesAdditionalBuffer), + fromPos); // Copy shortcut list size. - if (!mBuffer->writeUintAndAdvancePosition( + if (!bufferToWrite->writeUintAndAdvancePosition( shortcutListSize + ShortcutListReadingUtils::getShortcutListSizeFieldSize(), ShortcutListReadingUtils::getShortcutListSizeFieldSize(), toPos)) { return false; @@ -102,7 +104,7 @@ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { for (int i = 0; i < shortcutListSize; ++i) { const uint8_t data = ByteArrayUtils::readUint8AndAdvancePosition( mBuffer->getBuffer(usesAdditionalBuffer), fromPos); - if (!mBuffer->writeUintAndAdvancePosition(data, 1 /* size */, toPos)) { + if (!bufferToWrite->writeUintAndAdvancePosition(data, 1 /* size */, toPos)) { return false; } } @@ -115,7 +117,7 @@ class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { private: DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicShortcutListPolicy); - BufferWithExtendableBuffer *const mBuffer; + const BufferWithExtendableBuffer *const mBuffer; }; } // namespace latinime #endif // LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp index 0fed275e9..f692882f2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp @@ -18,9 +18,10 @@ namespace latinime { -const size_t BufferWithExtendableBuffer::INITIAL_ADDITIONAL_BUFFER_SIZE = 16 * 1024; const size_t BufferWithExtendableBuffer::MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 1024; -const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 16 * 1024; +const int BufferWithExtendableBuffer::NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE = 90; +// TODO: Needs to allocate larger memory corresponding to the current vector size. +const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 128 * 1024; bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos) { @@ -64,6 +65,16 @@ bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *co return true; } +bool BufferWithExtendableBuffer::extendBuffer() { + const size_t sizeAfterExtending = + mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP; + if (sizeAfterExtending > mMaxAdditionalBufferSize) { + return false; + } + mAdditionalBuffer.resize(mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP); + return true; +} + bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int size) { if (isInAdditionalBuffer(pos)) { const int tailPosition = getTailPosition(); 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 c6a484131..17d2e39c2 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 @@ -32,9 +32,11 @@ namespace latinime { // raw pointer but provides several methods that handle boundary checking for writing data. class BufferWithExtendableBuffer { public: - BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize) + BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize, + const int maxAdditionalBufferSize = MAX_ADDITIONAL_BUFFER_SIZE) : mOriginalBuffer(originalBuffer), mOriginalBufferSize(originalBufferSize), - mAdditionalBuffer(INITIAL_ADDITIONAL_BUFFER_SIZE), mUsedAdditionalBufferSize(0) {} + mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0), + mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} AK_FORCE_INLINE int getTailPosition() const { return mOriginalBufferSize + mUsedAdditionalBufferSize; @@ -61,6 +63,11 @@ class BufferWithExtendableBuffer { return mOriginalBufferSize; } + AK_FORCE_INLINE bool isNearSizeLimit() const { + return mAdditionalBuffer.size() >= ((mMaxAdditionalBufferSize + * NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE) / 100); + } + /** * For writing. * @@ -75,28 +82,22 @@ class BufferWithExtendableBuffer { private: DISALLOW_COPY_AND_ASSIGN(BufferWithExtendableBuffer); - static const size_t INITIAL_ADDITIONAL_BUFFER_SIZE; static const size_t MAX_ADDITIONAL_BUFFER_SIZE; + static const int NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE; static const size_t EXTEND_ADDITIONAL_BUFFER_SIZE_STEP; uint8_t *const mOriginalBuffer; const int mOriginalBufferSize; std::vector<uint8_t> mAdditionalBuffer; int mUsedAdditionalBufferSize; + const size_t mMaxAdditionalBufferSize; // Return if the buffer is successfully extended or not. - AK_FORCE_INLINE bool extendBuffer() { - if (mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP - > MAX_ADDITIONAL_BUFFER_SIZE) { - return false; - } - mAdditionalBuffer.resize(mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP); - return true; - } + bool extendBuffer(); // Returns if it is possible to write size-bytes from pos. When pos is at the tail position of // the additional buffer, try extending the buffer. - AK_FORCE_INLINE bool checkAndPrepareWriting(const int pos, const int size); + bool checkAndPrepareWriting(const int pos, const int size); }; } #endif /* LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_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 3796c7b7b..1d77d5c27 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -20,20 +20,10 @@ namespace latinime { -/** - * Dictionary size - */ -// Any file smaller than this is not a dictionary. -const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 4; +const uint32_t FormatUtils::MAGIC_NUMBER = 0x9BC13AFE; -/** - * Format versions - */ -// 32 bit magic number is stored at the beginning of the dictionary header to reject unsupported -// or obsolete dictionary formats. -const uint32_t FormatUtils::HEADER_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE; -// Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12 -const int FormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12; +// 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::detectFormatVersion( const uint8_t *const dict, const int dictSize) { @@ -45,16 +35,10 @@ const int FormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12; } const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0); switch (magicNumber) { - case HEADER_VERSION_2_MAGIC_NUMBER: - // Version 2 header are at least 12 bytes long. - // If this header has the version 2 magic number but is less than 12 bytes long, - // then it's an unknown format and we need to avoid confidently reading the next bytes. - if (dictSize < HEADER_VERSION_2_MINIMUM_SIZE) { - return UNKNOWN_VERSION; - } + case MAGIC_NUMBER: // Version 2 header is as follows: // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE - // Version number (2 bytes) + // Dictionary format version number (2 bytes) // Options (2 bytes) // Header size (4 bytes) : integer, big endian if (ByteArrayUtils::readUint16(dict, 4) == 2) { 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 f84321577..79ed0de29 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -34,14 +34,16 @@ class FormatUtils { UNKNOWN_VERSION }; + // 32 bit magic number is stored at the beginning of the dictionary header to reject + // unsupported or obsolete dictionary formats. + static const uint32_t MAGIC_NUMBER; + static FORMAT_VERSION detectFormatVersion(const uint8_t *const dict, const int dictSize); private: DISALLOW_IMPLICIT_CONSTRUCTORS(FormatUtils); static const int DICTIONARY_MINIMUM_SIZE; - static const uint32_t HEADER_VERSION_2_MAGIC_NUMBER; - static const int HEADER_VERSION_2_MINIMUM_SIZE; }; } // namespace latinime #endif /* LATINIME_FORMAT_UTILS_H */ diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 4d231cde7..96a2217a3 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; +import android.util.Pair; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.DictEncoder; @@ -151,7 +152,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); for (int i = 0; i < wordCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); - probabilityMap.put(word, random.nextInt() & 0xFF); + probabilityMap.put(word, random.nextInt(0xFF)); } for (String word : probabilityMap.keySet()) { binaryDictionary.addUnigramWord(word, probabilityMap.get(word)); @@ -163,8 +164,6 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWords() { - // TODO: Add a test to check the frequency of the bigram score which uses current value - // calculated in the native code File dictFile = null; try { dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); @@ -179,6 +178,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final int unigramProbability = 100; final int bigramProbability = 10; + final int updatedBigramProbability = 15; binaryDictionary.addUnigramWord("aaa", unigramProbability); binaryDictionary.addUnigramWord("abb", unigramProbability); binaryDictionary.addUnigramWord("bcc", unigramProbability); @@ -187,21 +187,49 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); binaryDictionary.addBigramWords("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")); + + binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability); + final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability, + updatedBigramProbability); + assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb")); assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa")); assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc")); assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("bcc", "aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("bcc", "bbc")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("aaa", "aaa")); + + // Testing bigram link. + binaryDictionary.addUnigramWord("abcde", unigramProbability); + binaryDictionary.addUnigramWord("fghij", unigramProbability); + binaryDictionary.addBigramWords("abcde", "fghij", bigramProbability); + binaryDictionary.addUnigramWord("fgh", unigramProbability); + binaryDictionary.addUnigramWord("abc", unigramProbability); + binaryDictionary.addUnigramWord("f", unigramProbability); + assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij")); + assertEquals(Dictionary.NOT_A_PROBABILITY, + binaryDictionary.getBigramProbability("abcde", "fgh")); + binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability); + assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij")); dictFile.delete(); } public void testRandomlyAddBigramWords() { - // TODO: Add a test to check the frequency of the bigram score which uses current value - // calculated in the native code final int wordCount = 100; final int bigramCount = 1000; final int codePointSetSize = 50; @@ -222,29 +250,38 @@ public class BinaryDictionaryTests extends AndroidTestCase { // Test a word that isn't contained within the dictionary. final Random random = new Random(seed); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); - final int unigramProbability = 100; - final int bigramProbability = 10; + final int[] unigramProbabilities = new int[wordCount]; for (int i = 0; i < wordCount; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities[i] = unigramProbability; binaryDictionary.addUnigramWord(word, unigramProbability); } - final boolean[][] bigramRelations = new boolean[wordCount][wordCount]; + final int[][] probabilities = new int[wordCount][wordCount]; + + for (int i = 0; i < wordCount; ++i) { + for (int j = 0; j < wordCount; ++j) { + probabilities[i][j] = Dictionary.NOT_A_PROBABILITY; + } + } + for (int i = 0; i < bigramCount; i++) { final int word0Index = random.nextInt(wordCount); final int word1Index = random.nextInt(wordCount); final String word0 = words.get(word0Index); final String word1 = words.get(word1Index); - - bigramRelations[word0Index][word1Index] = true; + final int bigramProbability = random.nextInt(0xF); + probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability( + unigramProbabilities[word1Index], bigramProbability); binaryDictionary.addBigramWords(word0, word1, bigramProbability); } for (int i = 0; i < words.size(); i++) { for (int j = 0; j < words.size(); j++) { - assertEquals(bigramRelations[i][j], - binaryDictionary.isValidBigram(words.get(i), words.get(j))); + assertEquals(probabilities[i][j], + binaryDictionary.getBigramProbability(words.get(i), words.get(j))); } } @@ -263,7 +300,6 @@ public class BinaryDictionaryTests extends AndroidTestCase { BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - final int unigramProbability = 100; final int bigramProbability = 10; binaryDictionary.addUnigramWord("aaa", unigramProbability); @@ -299,4 +335,319 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } + + public void testFlushDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int probability = 100; + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + // Close without flushing. + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd")); + + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(probability, binaryDictionary.getFrequency("aaa")); + assertEquals(probability, binaryDictionary.getFrequency("abcd")); + binaryDictionary.addUnigramWord("bcde", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + assertEquals(probability, binaryDictionary.getFrequency("bcde")); + binaryDictionary.close(); + + dictFile.delete(); + } + + public void testFlushWithGCDictionary() { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int unigramProbability = 100; + final int bigramProbability = 10; + binaryDictionary.addUnigramWord("aaa", unigramProbability); + binaryDictionary.addUnigramWord("abb", unigramProbability); + binaryDictionary.addUnigramWord("bcc", unigramProbability); + binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability); + binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); + binaryDictionary.addBigramWords("abb", "bcc", bigramProbability); + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + 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")); + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + dictFile.delete(); + } + + // TODO: Evaluate performance of GC + public void testAddBigramWordsAndFlashWithGC() { + final int wordCount = 100; + final int bigramCount = 1000; + final int codePointSetSize = 30; + // TODO: Use various seeds such as a current timestamp to make this test more random. + final int seed = 314159265; + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + + 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>(); + // Test a word that isn't contained within the dictionary. + final Random random = new Random(seed); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final int[] unigramProbabilities = new int[wordCount]; + for (int i = 0; i < wordCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities[i] = unigramProbability; + binaryDictionary.addUnigramWord(word, unigramProbability); + } + + final int[][] probabilities = new int[wordCount][wordCount]; + + for (int i = 0; i < wordCount; ++i) { + for (int j = 0; j < wordCount; ++j) { + probabilities[i][j] = Dictionary.NOT_A_PROBABILITY; + } + } + + for (int i = 0; i < bigramCount; i++) { + final int word0Index = random.nextInt(wordCount); + final int word1Index = random.nextInt(wordCount); + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int bigramProbability = random.nextInt(0xF); + probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability( + unigramProbabilities[word1Index], bigramProbability); + binaryDictionary.addBigramWords(word0, word1, bigramProbability); + } + + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + for (int i = 0; i < words.size(); i++) { + for (int j = 0; j < words.size(); j++) { + assertEquals(probabilities[i][j], + binaryDictionary.getBigramProbability(words.get(i), words.get(j))); + } + } + dictFile.delete(); + } + + public void testRandomOperetionsAndFlashWithGC() { + final int flashWithGCIterationCount = 50; + final int operationCountInEachIteration = 200; + final int initialUnigramCount = 100; + final float addUnigramProb = 0.5f; + final float addBigramProb = 0.8f; + final float removeBigramProb = 0.2f; + final int codePointSetSize = 30; + final int seed = 141421356; + + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + + 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 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>(); + for (int i = 0; i < initialUnigramCount; ++i) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + + for (int gcCount = 0; gcCount < flashWithGCIterationCount; gcCount++) { + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + for (int opCount = 0; opCount < operationCountInEachIteration; opCount++) { + // Add unigram. + if (random.nextFloat() < addUnigramProb) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + // Add bigram. + if (random.nextFloat() < addBigramProb && words.size() > 2) { + final int word0Index = random.nextInt(words.size()); + int word1Index = random.nextInt(words.size() - 1); + if (word0Index <= word1Index) { + word1Index++; + } + final String word0 = words.get(word0Index); + final String word1 = words.get(word1Index); + final int bigramProbability = random.nextInt(0xF); + final Pair<String, String> bigram = new Pair<String, String>(word0, word1); + bigramWords.add(bigram); + bigramProbabilities.put(bigram, bigramProbability); + binaryDictionary.addBigramWords(word0, word1, bigramProbability); + } + // Remove bigram. + if (random.nextFloat() < removeBigramProb && !bigramWords.isEmpty()) { + final int bigramIndex = random.nextInt(bigramWords.size()); + final Pair<String, String> bigram = bigramWords.get(bigramIndex); + bigramWords.remove(bigramIndex); + bigramProbabilities.remove(bigram); + binaryDictionary.removeBigramWords(bigram.first, bigram.second); + } + } + + // Test whether the all unigram operations are collectlly handled. + for (int i = 0; i < words.size(); i++) { + final String word = words.get(i); + final int unigramProbability = unigramProbabilities.get(word); + assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word)); + } + // 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); + } else { + probability = Dictionary.NOT_A_PROBABILITY; + } + assertEquals(probability, + binaryDictionary.getBigramProbability(bigram.first, bigram.second)); + } + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + } + + dictFile.delete(); + } + + public void testAddManyUnigramsAndFlushWithGC() { + final int flashWithGCIterationCount = 3; + final int codePointSetSize = 50; + final int seed = 22360679; + + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } catch (UnsupportedFormatException e) { + fail("UnsupportedFormatException while writing an initial dictionary : " + e); + } + + final ArrayList<String> words = new ArrayList<String>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + BinaryDictionary binaryDictionary; + for (int i = 0; i < flashWithGCIterationCount; i++) { + binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + while(!binaryDictionary.needsToRunGC()) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + final int unigramProbability = random.nextInt(0xFF); + unigramProbabilities.put(word, unigramProbability); + binaryDictionary.addUnigramWord(word, unigramProbability); + } + + for (int j = 0; j < words.size(); j++) { + final String word = words.get(j); + final int unigramProbability = unigramProbabilities.get(word); + assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word)); + } + + binaryDictionary.flushWithGC(); + binaryDictionary.close(); + } + + dictFile.delete(); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index fe92be618..cc2569f5e 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -134,6 +134,13 @@ public class InputLogicTests extends InputTestsBase { assertEquals("simple auto-correct", EXPECTED_RESULT, mEditText.getText().toString()); } + public void testAutoCorrectWithQuote() { + final String STRING_TO_TYPE = "didn' "; + final String EXPECTED_RESULT = "didn't "; + type(STRING_TO_TYPE); + assertEquals("auto-correct with quote", EXPECTED_RESULT, mEditText.getText().toString()); + } + public void testAutoCorrectWithPeriod() { final String STRING_TO_TYPE = "tgis."; final String EXPECTED_RESULT = "this."; diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 4cf83339a..a594baf0b 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -64,4 +64,37 @@ public class SuggestedWordsTests extends AndroidTestCase { assertEquals("0", wordsWithoutTyped.getWord(0)); assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind); } + + // Helper for testGetTransformedWordInfo + private SuggestedWordInfo createWordInfo(final String s) { + // Use 100 as the frequency because the numerical value does not matter as + // long as it's > 1 and < INT_MAX. + return new SuggestedWordInfo(s, 100, + SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + } + + // Helper for testGetTransformedWordInfo + private SuggestedWordInfo transformWordInfo(final String info, + final int trailingSingleQuotesCount) { + return Suggest.getTransformedSuggestedWordInfo(createWordInfo(info), + Locale.ENGLISH, false /* isAllUpperCase */, false /* isFirstCharCapitalized */, + trailingSingleQuotesCount); + } + + public void testGetTransformedSuggestedWordInfo() { + SuggestedWordInfo result = transformWordInfo("word", 0); + assertEquals(result.mWord, "word"); + result = transformWordInfo("word", 1); + assertEquals(result.mWord, "word'"); + result = transformWordInfo("word", 3); + assertEquals(result.mWord, "word'''"); + result = transformWordInfo("didn't", 0); + assertEquals(result.mWord, "didn't"); + result = transformWordInfo("didn't", 1); + assertEquals(result.mWord, "didn't"); + result = transformWordInfo("didn't", 3); + assertEquals(result.mWord, "didn't''"); + } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index 8bc0095a5..a4d94262f 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -25,6 +25,7 @@ import android.util.SparseArray; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -75,6 +76,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */); private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE = new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION4_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(4, false /* supportsDynamicUpdate */); + private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */); private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; @@ -114,6 +119,17 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } } + private DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + return new Ver4DictEncoder(getContext().getCacheDir()); + } else if (formatOptions.mVersion == 3 || formatOptions.mVersion == 2) { + return new Ver3DictEncoder(file); + } else { + throw new RuntimeException("The format option has a wrong version : " + + formatOptions.mVersion); + } + } + private void generateWords(final int number, final Random random, final int[] codePointSet) { final Set<String> wordSet = CollectionUtils.newHashSet(); while (wordSet.size() < number) { @@ -165,7 +181,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { long now = -1, diff = -1; try { - final DictEncoder dictEncoder = new Ver3DictEncoder(file); + final DictEncoder dictEncoder = getDictEncoder(file, formatOptions); now = System.currentTimeMillis(); // If you need to dump the dict to a textual file, uncomment the line below and the @@ -246,16 +262,28 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { return file; } + private DictDecoder getDictDecoder(final File file, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + final FileHeader header = new FileHeader(0, dictOptions, formatOptions); + return FormatSpec.getDictDecoder(new File(getContext().getCacheDir(), + header.getId() + "." + header.getVersion()), bufferType); + } else { + return FormatSpec.getDictDecoder(file, bufferType); + } + } // Tests for readDictionaryBinary and writeDictionaryBinary private long timeReadingAndCheckDict(final File file, final List<String> words, final SparseArray<List<Integer>> bigrams, - final HashMap<String, List<String>> shortcutMap, final int bufferType) { + final HashMap<String, List<String>> shortcutMap, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { long now, diff = -1; FusionDictionary dict = null; try { - final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType); + final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, + dictOptions); now = System.currentTimeMillis(); dict = dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */); diff = System.currentTimeMillis() - now; @@ -286,7 +314,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { checkDictionary(dict, words, bigrams, shortcuts); final long write = timeWritingDictToFile(file, dict, formatOptions); - final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType); + final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType, + formatOptions, dict.mOptions); return "PROF: read=" + read + "ms, write=" + write + "ms :" + message + " : " + outputOptions(bufferType, formatOptions); @@ -330,6 +359,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2); runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -342,6 +373,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2); runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -397,7 +430,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, - final SparseArray<List<Integer>> bigrams, final int bufferType) { + final SparseArray<List<Integer>> bigrams, final int bufferType, + final FormatOptions formatOptions, final DictionaryOptions dictOptions) { FileInputStream inStream = null; final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap(); @@ -407,7 +441,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { long now = -1, diff = -1; try { - final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType); + final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, + dictOptions); now = System.currentTimeMillis(); dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams); diff = System.currentTimeMillis() - now; @@ -444,9 +479,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { timeWritingDictToFile(file, dict, formatOptions); - long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType); + long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType, + formatOptions, dict.mOptions); long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */, - bufferType); + bufferType, formatOptions, dict.mOptions); return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap + " : " + message + " : " + outputOptions(bufferType, formatOptions); @@ -458,7 +494,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { formatOptions, "unigram")); results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType, formatOptions, "chain")); - results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType, + results.add(runReadUnigramsAndBigramsBinary(sWords, sStarBigrams, bufferType, formatOptions, "star")); } @@ -468,6 +504,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2); runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -480,6 +518,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2); runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); for (final String result : results) { Log.d(TAG, result); @@ -503,7 +543,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { address, fileHeader.mFormatOptions).mWord; } - private long runGetTerminalPosition(final DictDecoder dictDecoder, final String word, + private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word, int index, boolean contained) { final int expectedFrequency = (UNIGRAM_FREQ + index) % 255; long diff = -1; @@ -523,7 +563,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { return diff; } - public void testGetTerminalPosition() { + private void runGetTerminalPosition(final ArrayList<String> words, + final SparseArray<List<Integer>> bigrams, final int bufferType, + final FormatOptions formatOptions, final String message) { final String dictName = "testGetTerminalPosition"; final String dictVersion = Long.toString(System.currentTimeMillis()); final File file = setUpDictionaryFile(dictName, dictVersion); @@ -531,16 +573,18 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), getDictionaryOptions(dictName, dictVersion)); addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); - timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE); + addBigrams(dict, words, bigrams); + timeWritingDictToFile(file, dict, formatOptions); - final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY); + final DictDecoder dictDecoder = getDictDecoder(file, DictDecoder.USE_BYTEARRAY, + formatOptions, dict.mOptions); try { dictDecoder.openDictBuffer(); } catch (IOException e) { // ignore Log.e(TAG, "IOException while opening the buffer", e); } - assertTrue("Can't get the buffer", dictDecoder.isOpenedDictBuffer()); + assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); try { // too long word @@ -559,10 +603,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { // Test a word that is contained within the dictionary. long sum = 0; for (int i = 0; i < sWords.size(); ++i) { - final long time = runGetTerminalPosition(dictDecoder, sWords.get(i), i, true); + final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), i, true); sum += time == -1 ? 0 : time; } - Log.d(TAG, "per a search : " + (((double)sum) / sWords.size() / 1000000)); + Log.d(TAG, "per search : " + (((double)sum) / sWords.size() / 1000000) + " : " + message + + " : " + outputOptions(bufferType, formatOptions)); // Test a word that isn't contained within the dictionary. final Random random = new Random((int)System.currentTimeMillis()); @@ -571,7 +616,32 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { for (int i = 0; i < 1000; ++i) { final String word = CodePointUtils.generateWord(random, codePointSet); if (sWords.indexOf(word) != -1) continue; - runGetTerminalPosition(dictDecoder, word, i, false); + checkGetTerminalPosition(dictDecoder, word, i, false); + } + } + + private void runGetTerminalPositionTests(final ArrayList<String> results, final int bufferType, + final FormatOptions formatOptions) { + runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram"); + } + + public void testGetTerminalPosition() { + final ArrayList<String> results = CollectionUtils.newArrayList(); + + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION2); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); + + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION2); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); + + for (final String result : results) { + Log.d(TAG, result); } } @@ -593,7 +663,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { // ignore Log.e(TAG, "IOException while opening the buffer", e); } - assertTrue("Can't get the buffer", dictDecoder.isOpenedDictBuffer()); + assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); try { MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index bf44a1424..d605cdb84 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -100,7 +100,11 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS)); } catch (InterruptedException e) { } - for (int i = 0; i < 10 && i < numberOfWords; ++i) { + // Limit word count to check when using a Java on memory dictionary. + final int wordCountToCheck = + ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? + numberOfWords : 10; + for (int i = 0; i < wordCountToCheck; ++i) { final String word = words.get(i); // This may fail as long as we use tryLock on inserting the bigram words assertTrue(dict.isInDictionaryForTests(word)); @@ -202,4 +206,41 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } } } + + public void testAddManyWords() { + File dictFile = null; + final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); + final int numberOfWords = + ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? + 10000 : 1000; + final Random random = new Random(123456); + + UserHistoryPredictionDictionary dict = + PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(), + testFilenameSuffix, mPrefs); + try { + addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random, + true /* checksContents */); + dict.close(); + } finally { + try { + Log.d(TAG, "waiting for writing ..."); + dict.shutdownExecutorForTests(); + while (!dict.isTerminatedForTests()) { + Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); + } + } catch (InterruptedException e) { + Log.d(TAG, "InterruptedException: ", e); + } + final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix + + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + dictFile = new File(getContext().getFilesDir(), fileName); + if (dictFile != null) { + assertTrue(dictFile.exists()); + assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); + dictFile.delete(); + } + } + } + } diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java index c6fa943fe..eb9fb984e 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java @@ -20,7 +20,14 @@ import com.android.inputmethod.latin.settings.SettingsValues; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; +import android.text.style.SuggestionSpan; +import android.text.style.URLSpan; +import android.text.SpannableStringBuilder; +import android.text.Spannable; +import android.text.Spanned; +import java.util.Arrays; +import java.util.List; import java.util.Locale; @SmallTest @@ -268,4 +275,44 @@ public class StringUtilsTests extends AndroidTestCase { final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2); assertTrue(bytesStr.equals(bytesStr2)); } + + public void testJsonStringUtils() { + final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; + final List<Object> objArray = Arrays.asList(objs); + final String str = StringUtils.listToJsonStr(objArray); + final List<Object> newObjArray = StringUtils.jsonStrToList(str); + for (int i = 0; i < objs.length; ++i) { + assertEquals(objs[i], newObjArray.get(i)); + } + } + + public void testConcatWithSuggestionSpansOnly() { + SpannableStringBuilder s = new SpannableStringBuilder("test string\ntest string\n" + + "test string\ntest string\ntest string\ntest string\ntest string\ntest string\n" + + "test string\ntest string\n"); + final int N = 10; + for (int i = 0; i < N; ++i) { + // Put a PARAGRAPH-flagged span that should not be found in the result. + s.setSpan(new SuggestionSpan(getContext(), + new String[] {"" + i}, Spannable.SPAN_PARAGRAPH), + i * 12, i * 12 + 12, Spannable.SPAN_PARAGRAPH); + // Put a normal suggestion span that should be found in the result. + s.setSpan(new SuggestionSpan(getContext(), new String[] {"" + i}, 0), i, i * 2, 0); + // Put a URL span than should not be found in the result. + s.setSpan(new URLSpan("http://a"), i, i * 2, 0); + } + + final CharSequence a = s.subSequence(0, 15); + final CharSequence b = s.subSequence(15, s.length()); + final Spanned result = + (Spanned)StringUtils.concatWithNonParagraphSuggestionSpansOnly(a, b); + + Object[] spans = result.getSpans(0, result.length(), SuggestionSpan.class); + for (int i = 0; i < spans.length; i++) { + final int flags = result.getSpanFlags(spans[i]); + assertEquals("Should not find a span with PARAGRAPH flag", + flags & Spannable.SPAN_PARAGRAPH, 0); + assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan); + } + } } 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 5302e976e..5c7e8b4f2 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -23,8 +23,8 @@ import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.MakedictLog; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; -import com.android.inputmethod.latin.makedict.Ver3DictDecoder; import com.android.inputmethod.latin.makedict.Ver3DictEncoder; +import com.android.inputmethod.latin.makedict.Ver4DictEncoder; import java.io.BufferedWriter; import java.io.File; @@ -45,9 +45,9 @@ import org.xml.sax.SAXException; public class DictionaryMaker { static class Arguments { - private static final String OPTION_VERSION_1 = "-1"; private static final String OPTION_VERSION_2 = "-2"; private static final String OPTION_VERSION_3 = "-3"; + private static final String OPTION_VERSION_4 = "-4"; private static final String OPTION_INPUT_SOURCE = "-s"; private static final String OPTION_INPUT_BIGRAM_XML = "-b"; private static final String OPTION_INPUT_SHORTCUT_XML = "-c"; @@ -128,12 +128,12 @@ public class DictionaryMaker { + "| [-s <combined format input]" + "| [-s <binary input>] [-d <binary output>] [-x <xml output>] " + " [-o <combined output>]" - + "[-1] [-2] [-3]\n" + + "[-2] [-3] [-4]\n" + "\n" + " Converts a source dictionary file to one or several outputs.\n" + " Source can be an XML file, with an optional XML bigrams file, or a\n" + " binary dictionary file.\n" - + " Binary version 1 (Ice Cream Sandwich), 2 (Jelly Bean), 3, XML and\n" + + " Binary version 2 (Jelly Bean), 3, 4, XML and\n" + " combined format outputs are supported."; } @@ -160,8 +160,8 @@ public class DictionaryMaker { // Do nothing, this is the default } else if (OPTION_VERSION_3.equals(arg)) { outputBinaryFormatVersion = 3; - } else if (OPTION_VERSION_1.equals(arg)) { - outputBinaryFormatVersion = 1; + } else if (OPTION_VERSION_4.equals(arg)) { + outputBinaryFormatVersion = 4; } else if (OPTION_HELP.equals(arg)) { displayHelp(); } else { @@ -357,7 +357,12 @@ public class DictionaryMaker { throws FileNotFoundException, IOException, UnsupportedFormatException { final File outputFile = new File(outputFilename); final FormatSpec.FormatOptions formatOptions = new FormatSpec.FormatOptions(version); - final DictEncoder dictEncoder = new Ver3DictEncoder(outputFile); + final DictEncoder dictEncoder; + if (version == 4) { + dictEncoder = new Ver4DictEncoder(outputFile); + } else { + dictEncoder = new Ver3DictEncoder(outputFile); + } dictEncoder.writeDictionary(dict, formatOptions); } diff --git a/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml index f6c64285c..2f34128bd 100644 --- a/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml @@ -18,7 +18,23 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- U+058A: "֊" ARMENIAN HYPHEN --> + <!-- U+055C: "՜" ARMENIAN EXCLAMATION MARK --> + <!-- U+055D: "՝" ARMENIAN COMMA --> <!-- U+055E: "՞" ARMENIAN QUESTION MARK --> - <string name="more_keys_for_punctuation">"!fixedColumnOrder!4,՞,!,\\,,\?,:,;,\@"</string> - <string name="more_keys_for_tablet_period">՞,\?</string> + <!-- U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING --> + <!-- U+055A: "՚" ARMENIAN APOSTROPHE --> + <!-- U+055B: "՛" ARMENIAN EMPHASIS MARK --> + <!-- U+055F: "՟" ARMENIAN ABBREVIATION MARK --> + <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,!,?,\\,,.,֊,՜,՝,՞,:,;,\@,ՙ,՚,՛,՟"</string> + <!-- U+055E: "՞" ARMENIAN QUESTION MARK --> + <!-- U+00BF: "¿" INVERTED QUESTION MARK --> + <string name="more_keys_for_symbols_question">՞,¿</string> + <!-- U+055C: "՜" ARMENIAN EXCLAMATION MARK --> + <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK --> + <string name="more_keys_for_symbols_exclamation">՜,¡</string> + <!-- U+058F: "֏" ARMENIAN DRAM SIGN --> + <!-- TODO: Enable this when we have glyph for the following letter + <string name="keylabel_for_currency">֏</string> + --> </resources> diff --git a/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml new file mode 100644 index 000000000..c33831c56 --- /dev/null +++ b/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml @@ -0,0 +1,29 @@ +<?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. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for "switch to alphabetic" key. + U+1780: "ក" KHMER LETTER KA + U+1781: "ខ" KHMER LETTER KHA + U+1782: "គ" KHMER LETTER KO --> + <string name="label_to_alpha_key">កខគ</string> + <!-- U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL --> + <string name="more_keys_for_currency_dollar">៛,¢,£,€,¥,₱</string> + +</resources> |