diff options
author | 2013-12-13 17:09:16 +0900 | |
---|---|---|
committer | 2013-12-13 17:13:32 +0900 | |
commit | 2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc (patch) | |
tree | 56a5652edf71dd19d04161f72e3e013608cc2a9c | |
parent | 18d033405c18a8dc28f60ca22d1d0df23a679384 (diff) | |
download | latinime-2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc.tar.gz latinime-2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc.tar.xz latinime-2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc.zip |
Reset to 9bd6dac4708ad94fd0257c53e977df62b152e20c
The bulk merge from -bayo to klp-dev should not have been merged to master.
Change-Id: I527a03a76f5247e4939a672f27c314dc11cbb854
472 files changed, 14878 insertions, 10181 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 031d62e0c..0d80c037b 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -18,7 +18,7 @@ coreApp="true" package="com.android.inputmethod.latin"> - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> @@ -32,7 +32,7 @@ <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" /> <application android:label="@string/english_ime_name" - android:icon="@mipmap/ic_launcher_keyboard" + android:icon="@drawable/ic_launcher_keyboard" android:killAfterRestore="false" android:supportsRtl="true"> @@ -57,7 +57,7 @@ <activity android:name=".setup.SetupActivity" android:label="@string/english_ime_name" - android:icon="@mipmap/ic_launcher_keyboard" + android:icon="@drawable/ic_launcher_keyboard" android:launchMode="singleTask" android:noHistory="true"> <intent-filter> diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png Binary files differdeleted file mode 100644 index 3e25180f0..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png Binary files differdeleted file mode 100644 index bad360f77..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png Binary files differdeleted file mode 100644 index 49f519860..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png Binary files differdeleted file mode 100644 index e784eddf8..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png Binary files differdeleted file mode 100644 index a4731cf1a..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png Binary files differdeleted file mode 100644 index 03e163c9c..000000000 --- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-hdpi/ic_launcher_keyboard.png b/java/res/drawable-hdpi/ic_launcher_keyboard.png Binary files differnew file mode 100644 index 000000000..7ae00ed3f --- /dev/null +++ b/java/res/drawable-hdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-hdpi/ic_subtype_mic_dark.png b/java/res/drawable-hdpi/ic_subtype_mic_dark.png Binary files differdeleted file mode 100644 index eacbcd255..000000000 --- a/java/res/drawable-hdpi/ic_subtype_mic_dark.png +++ /dev/null diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_klp.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_klp.9.png Binary files differindex 50ed568ff..be394151a 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_background_klp.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_klp.9.png 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 differindex 5af09ad8c..2ea4a74a5 100644 --- a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png +++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png Binary files differdeleted file mode 100644 index 12bc97928..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png Binary files differdeleted file mode 100644 index 44bd414a1..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png Binary files differdeleted file mode 100644 index 43fdf5b88..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png Binary files differdeleted file mode 100644 index 1c1f3d711..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png Binary files differdeleted file mode 100644 index dacb675a9..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png Binary files differdeleted file mode 100644 index 3daa69f31..000000000 --- a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/ic_launcher_keyboard.png b/java/res/drawable-mdpi/ic_launcher_keyboard.png Binary files differnew file mode 100644 index 000000000..cc73f3be1 --- /dev/null +++ b/java/res/drawable-mdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_klp.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_klp.9.png Binary files differindex 564f5460c..625490b1f 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_background_klp.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_klp.9.png 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 differdeleted file mode 100644 index 537f39b02..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png +++ /dev/null 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 differdeleted file mode 100644 index 84a63dc7f..000000000 --- a/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.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 differindex 36c8c9623..613f4dc88 100644 --- a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png +++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png Binary files differdeleted file mode 100644 index 026005d6f..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_normal.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png Binary files differdeleted file mode 100644 index 38c5f244b..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png Binary files differdeleted file mode 100644 index f1223e50e..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png Binary files differdeleted file mode 100644 index ec35db54d..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_pressed.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png Binary files differdeleted file mode 100644 index bd30464d6..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png Binary files differdeleted file mode 100644 index a3ff5d1bb..000000000 --- a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/ic_launcher_keyboard.png b/java/res/drawable-xhdpi/ic_launcher_keyboard.png Binary files differnew file mode 100644 index 000000000..f2ac50dfe --- /dev/null +++ b/java/res/drawable-xhdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-xhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xhdpi/ic_subtype_mic_dark.png Binary files differdeleted file mode 100644 index 17581ba89..000000000 --- a/java/res/drawable-xhdpi/ic_subtype_mic_dark.png +++ /dev/null diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_klp.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_klp.9.png Binary files differindex e8c65f677..c211d89c8 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_klp.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_klp.9.png 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 differindex 99ee97dbf..15a97394e 100644 --- a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png +++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable-xxhdpi/ic_launcher_keyboard.png b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png Binary files differnew file mode 100644 index 000000000..df386e827 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png Binary files differdeleted file mode 100644 index 811103a56..000000000 --- a/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png +++ /dev/null diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_klp.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_klp.9.png Binary files differindex 11eee94f3..fd2f9e514 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_klp.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_klp.9.png 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 differindex 7041bb6ce..bf643e1d8 100644 --- a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png +++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png diff --git a/java/res/drawable/btn_keyboard_spacebar_gb.xml b/java/res/drawable/btn_keyboard_spacebar_gb.xml new file mode 100644 index 000000000..4d51f3c9c --- /dev/null +++ b/java/res/drawable/btn_keyboard_spacebar_gb.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_light_pressed" /> + <item android:drawable="@drawable/btn_keyboard_key_light_normal" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_ics.xml b/java/res/drawable/btn_keyboard_spacebar_ics.xml new file mode 100644 index 000000000..4530ea079 --- /dev/null +++ b/java/res/drawable/btn_keyboard_spacebar_ics.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_light_pressed_ics" /> + <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_klp.xml b/java/res/drawable/btn_keyboard_spacebar_klp.xml new file mode 100644 index 000000000..6b07a392f --- /dev/null +++ b/java/res/drawable/btn_keyboard_spacebar_klp.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2013 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_light_pressed_klp" /> + <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" /> +</selector> diff --git a/java/res/layout/emoji_keyboard_page.xml b/java/res/layout/emoji_keyboard_page.xml index e0b752b32..9afad366a 100644 --- a/java/res/layout/emoji_keyboard_page.xml +++ b/java/res/layout/emoji_keyboard_page.xml @@ -18,16 +18,9 @@ */ --> -<com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier +<com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/emoji_keyboard_scroller" - android:clipToPadding="false" - android:layout_width="match_parent" - android:layout_height="match_parent" -> - <com.android.inputmethod.keyboard.internal.ScrollKeyboardView - android:id="@+id/emoji_keyboard_page" - android:layoutDirection="ltr" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> -</com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier> + android:id="@+id/emoji_keyboard_page" + android:layoutDirection="ltr" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> diff --git a/java/res/layout/emoji_palettes_view.xml b/java/res/layout/emoji_palettes_view.xml index 1c6da90ba..b25285967 100644 --- a/java/res/layout/emoji_palettes_view.xml +++ b/java/res/layout/emoji_palettes_view.xml @@ -29,7 +29,7 @@ <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" - android:layout_height="@dimen/suggestions_strip_height" + android:layout_height="@dimen/config_suggestions_strip_height" > <TabHost android:id="@+id/emoji_category_tabhost" @@ -73,7 +73,7 @@ android:background="@color/emoji_key_background_color" android:src="@drawable/sym_keyboard_delete_holo_dark" /> </LinearLayout> - <android.support.v4.view.ViewPager + <com.android.inputmethod.keyboard.internal.CustomViewPager android:id="@+id/emoji_keyboard_pager" android:layout_width="match_parent" android:layout_height="wrap_content" /> diff --git a/java/res/layout/hint_add_to_dictionary.xml b/java/res/layout/hint_add_to_dictionary.xml index 68a9faf19..d42908260 100644 --- a/java/res/layout/hint_add_to_dictionary.xml +++ b/java/res/layout/hint_add_to_dictionary.xml @@ -23,7 +23,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:textSize="@dimen/suggestion_text_size" + android:textSize="@dimen/config_suggestion_text_size" android:gravity="center" android:paddingLeft="0dp" android:paddingTop="0dp" diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index 1e7a3844e..ed387e5b2 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -41,10 +41,10 @@ android:id="@+id/suggestion_strip_view" android:layoutDirection="ltr" android:layout_width="match_parent" - android:layout_height="@dimen/suggestions_strip_height" + android:layout_height="@dimen/config_suggestions_strip_height" android:gravity="center_vertical" - android:paddingRight="@dimen/suggestions_strip_padding" - android:paddingLeft="@dimen/suggestions_strip_padding" + android:paddingRight="@dimen/config_suggestions_strip_horizontal_padding" + android:paddingLeft="@dimen/config_suggestions_strip_horizontal_padding" style="?attr/suggestionStripViewStyle" /> <!-- To ensure that key preview popup is correctly placed when the current system locale is diff --git a/java/res/layout/key_preview_klp.xml b/java/res/layout/key_preview.xml index 160aeb9a9..16d4c72c3 100644 --- a/java/res/layout/key_preview_klp.xml +++ b/java/res/layout/key_preview.xml @@ -21,7 +21,7 @@ <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@drawable/keyboard_key_feedback_klp" android:minWidth="32dp" android:gravity="center" + style="?attr/keyPreviewTextViewStyle" /> diff --git a/java/res/layout/more_keys_keyboard.xml b/java/res/layout/more_keys_keyboard.xml index 6637117e0..f3795afdc 100644 --- a/java/res/layout/more_keys_keyboard.xml +++ b/java/res/layout/more_keys_keyboard.xml @@ -22,11 +22,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - style="?attr/moreKeysKeyboardContainerStyle" + android:orientation="vertical" > <com.android.inputmethod.keyboard.MoreKeysKeyboardView - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" android:id="@+id/more_keys_keyboard_view" android:layout_width="wrap_content" android:layout_height="wrap_content" /> diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml index 8659f0761..0869992c9 100644 --- a/java/res/layout/more_suggestions.xml +++ b/java/res/layout/more_suggestions.xml @@ -22,16 +22,15 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - style="?attr/moreKeysKeyboardContainerStyle" + android:orientation="vertical" > <com.android.inputmethod.latin.suggestions.MoreSuggestionsView xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" android:id="@+id/more_suggestions_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - latin:keyLetterSize="@dimen/suggestion_text_size" - latin:keyLabelSize="@dimen/suggestion_text_size" - latin:keyHintLetterRatio="@fraction/more_suggestions_info_ratio" + latin:keyLetterSize="@dimen/config_suggestion_text_size" + latin:keyLabelSize="@dimen/config_suggestion_text_size" + latin:keyHintLetterRatio="@fraction/config_more_suggestions_info_ratio" latin:keyHintLetterColor="@android:color/white" /> </LinearLayout> diff --git a/java/res/layout/suggestion_word.xml b/java/res/layout/suggestion_word.xml index c82a13c99..47d2bd864 100644 --- a/java/res/layout/suggestion_word.xml +++ b/java/res/layout/suggestion_word.xml @@ -24,12 +24,12 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:minWidth="@dimen/suggestion_min_width" - android:textSize="@dimen/suggestion_text_size" + android:minWidth="@dimen/config_suggestion_min_width" + android:textSize="@dimen/config_suggestion_text_size" android:gravity="center" - android:paddingLeft="@dimen/suggestion_padding" + android:paddingLeft="@dimen/config_suggestion_text_horizontal_padding" android:paddingTop="0dp" - android:paddingRight="@dimen/suggestion_padding" + android:paddingRight="@dimen/config_suggestion_text_horizontal_padding" android:paddingBottom="0dp" android:hapticFeedbackEnabled="false" android:focusable="false" diff --git a/java/res/layout/user_dictionary_add_word.xml b/java/res/layout/user_dictionary_add_word.xml index bbf9b1b5b..607f5c4a8 100644 --- a/java/res/layout/user_dictionary_add_word.xml +++ b/java/res/layout/user_dictionary_add_word.xml @@ -52,7 +52,7 @@ android:hint="@string/user_dict_settings_add_word_hint" android:imeOptions="flagNoFullscreen" android:inputType="textNoSuggestions" - android:maxLength="@integer/user_dictionary_max_word_length" > + android:maxLength="@integer/config_user_dictionary_max_word_length" > <requestFocus /> </EditText> diff --git a/java/res/layout/user_dictionary_add_word_fullscreen.xml b/java/res/layout/user_dictionary_add_word_fullscreen.xml index 219485b66..9bcb189b4 100644 --- a/java/res/layout/user_dictionary_add_word_fullscreen.xml +++ b/java/res/layout/user_dictionary_add_word_fullscreen.xml @@ -30,7 +30,7 @@ android:hint="@string/user_dict_settings_add_word_hint" android:imeOptions="flagNoFullscreen" android:inputType="textNoSuggestions" - android:maxLength="@integer/user_dictionary_max_word_length" > + android:maxLength="@integer/config_user_dictionary_max_word_length" > <requestFocus /> </EditText> @@ -61,7 +61,7 @@ android:hint="@string/user_dict_settings_add_shortcut_hint" android:imeOptions="flagNoFullscreen" android:inputType="textNoSuggestions" - android:maxLength="@integer/user_dictionary_max_word_length" /> + android:maxLength="@integer/config_user_dictionary_max_word_length" /> <TextView android:id="@+id/user_dictionary_add_locale_label" diff --git a/java/res/mipmap-hdpi/ic_launcher_keyboard.png b/java/res/mipmap-hdpi/ic_launcher_keyboard.png Binary files differdeleted file mode 100644 index 36b1ccae2..000000000 --- a/java/res/mipmap-hdpi/ic_launcher_keyboard.png +++ /dev/null diff --git a/java/res/mipmap-mdpi/ic_launcher_keyboard.png b/java/res/mipmap-mdpi/ic_launcher_keyboard.png Binary files differdeleted file mode 100644 index 67ef189ff..000000000 --- a/java/res/mipmap-mdpi/ic_launcher_keyboard.png +++ /dev/null diff --git a/java/res/mipmap-xhdpi/ic_launcher_keyboard.png b/java/res/mipmap-xhdpi/ic_launcher_keyboard.png Binary files differdeleted file mode 100644 index b33208332..000000000 --- a/java/res/mipmap-xhdpi/ic_launcher_keyboard.png +++ /dev/null diff --git a/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png b/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png Binary files differdeleted file mode 100644 index acc424fe2..000000000 --- a/java/res/mipmap-xxhdpi/ic_launcher_keyboard.png +++ /dev/null diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict Binary files differindex 69796bbaa..c65698d7b 100644 --- a/java/res/raw/main_de.dict +++ b/java/res/raw/main_de.dict diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml index 045e97d94..9607ceb4c 100644 --- a/java/res/values-af/strings.xml +++ b/java/res/values-af/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"tyd"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Steminvoerinstellings"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Op hoofsleutelbord"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Op simbolesleutelbord"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Af"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofoon op hoofsleutelbord"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofoon op simbolesleutelbord"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Steminvoer is gedeaktiveer"</string> <string name="configure_input_method" msgid="373356270290742459">"Stel invoermetodes op"</string> <string name="language_selection_title" msgid="1651299598555326750">"Invoertale"</string> <string name="send_feedback" msgid="1780431884109392046">"Stuur terugvoer"</string> diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml index 0b81034f4..d131e340c 100644 --- a/java/res/values-am/strings.xml +++ b/java/res/values-am/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ጊዜ"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"ዩ አር ኤል"</string> <string name="voice_input" msgid="3583258583521397548">"የድምፅ ግቤት ቁልፍ"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"በዋናቁልፍ ሰሌዳ ላይ"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"በምልክቶች ቁልፍ ሰሌዳ ላይ"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"ውጪ"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ድምፅ ማጉያ በዋናው ቁልፍሰሌዳው ላይ"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"የድምፅ ማጉያ ምልክትበቁልፍ ሰሌዳላይ"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"የድምፅ ግቤት ቦዝኗል"</string> <string name="configure_input_method" msgid="373356270290742459">"ግቤት ሜተዶችን አዋቀር"</string> <string name="language_selection_title" msgid="1651299598555326750">"ቋንቋዎች አግቤት"</string> <string name="send_feedback" msgid="1780431884109392046">"ግብረ-መልስ ላክ"</string> diff --git a/java/res/values-fa/donottranslate.xml b/java/res/values-ar/config-spacing-and-punctuations.xml index 57de2538b..9e637e458 100644 --- a/java/res/values-fa/donottranslate.xml +++ b/java/res/values-ar/config-spacing-and-punctuations.xml @@ -21,5 +21,5 @@ <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <!-- Symbols that are suggested between words --> - <string name="suggested_punctuations">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> + <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> </resources> diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml index da331196c..226936b14 100644 --- a/java/res/values-ar/strings.xml +++ b/java/res/values-ar/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"الوقت"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"عنوان URL"</string> <string name="voice_input" msgid="3583258583521397548">"مفتاح الإدخال الصوتي"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"لوحة مفاتيح رئيسية"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"لوحة مفاتيح الرموز"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"إيقاف"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ميكروفون على لوحة مفاتيح رئيسية"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ميكروفون على لوحة مفاتيح الرموز"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"الإدخال الصوتي مُعطل"</string> <string name="configure_input_method" msgid="373356270290742459">"تهيئة طرق الإدخال"</string> <string name="language_selection_title" msgid="1651299598555326750">"لغات الإدخال"</string> <string name="send_feedback" msgid="1780431884109392046">"إرسال تعليقات"</string> diff --git a/java/res/values-be/bools.xml b/java/res/values-be-rBY/bools.xml index 840d20c21..840d20c21 100644 --- a/java/res/values-be/bools.xml +++ b/java/res/values-be-rBY/bools.xml diff --git a/java/res/values-be/strings-action-keys.xml b/java/res/values-be-rBY/strings-action-keys.xml index 91416c8b7..91416c8b7 100644 --- a/java/res/values-be/strings-action-keys.xml +++ b/java/res/values-be-rBY/strings-action-keys.xml diff --git a/java/res/values-be/strings-appname.xml b/java/res/values-be/strings-appname.xml deleted file mode 100644 index 2f9593bdc..000000000 --- a/java/res/values-be/strings-appname.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_name" msgid="5940510615957428904">"Клавіятура Android (AOSP)"</string> - <string name="spell_checker_service_name" msgid="1254221805440242662">"Iнструмент праверкi правапiсу для Android (AOSP)"</string> - <string name="english_ime_settings" msgid="5760361067176802794">"Налады клавіятуры Android (AOSP)"</string> - <string name="android_spell_checker_settings" msgid="6123949487832861885">"Налады інструмента праверкі правапісу для Android (AOSP)"</string> -</resources> diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml deleted file mode 100644 index 02972f07e..000000000 --- a/java/res/values-be/strings.xml +++ /dev/null @@ -1,253 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- -/* -** -** Copyright 2008, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - --> - -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string> - <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string> - <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string> - <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Модуль праверкі правапісу выкарыстоўвае запісы са спісу кантактаў"</string> - <string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібрацыя пры націску клавіш"</string> - <string name="sound_on_keypress" msgid="6093592297198243644">"Гук пры націску"</string> - <string name="popup_on_keypress" msgid="123894815723512944">"Па націску на клавішы ўсплывае акно"</string> - <string name="general_category" msgid="1859088467017573195">"Агульныя"</string> - <string name="correction_category" msgid="2236750915056607613">"Выпраўленне тэксту"</string> - <string name="gesture_typing_category" msgid="497263612130532630">"Набор жэстамі"</string> - <string name="misc_category" msgid="6894192814868233453">"Іншыя параметры"</string> - <string name="advanced_settings" msgid="362895144495591463">"Адмысловыя налады"</string> - <string name="advanced_settings_summary" msgid="4487980456152830271">"Функцыi для спецыялістаў"</string> - <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Перакл. да інш. спос. ув."</string> - <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Кнопка пераключэння мовы звязана i з iншымi спосабамi ўводу"</string> - <string name="show_language_switch_key" msgid="5915478828318774384">"Кнопка пераключэння мовы"</string> - <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Паказваць, калі ўключана некалькі моў ўводу"</string> - <string name="sliding_key_input_preview" msgid="6604262359510068370">"Iндыкатар слайд-шоу"</string> - <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Паказаць візуальны сігнал падчас слiзгання клавiш Shift або Symbol"</string> - <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затрым. скр. падк. клав."</string> - <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Няма затрымкі"</string> - <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string> - <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> мс"</string> - <!-- no translation found for settings_system_default (6268225104743331821) --> - <skip /> - <string name="use_contacts_dict" msgid="4435317977804180815">"Прапан. імёны кантактаў"</string> - <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Выкарыстоўваць імёны са спісу кантактаў для прапаноў і выпраўл."</string> - <string name="use_double_space_period" msgid="8781529969425082860">"Падвойны iнтэрвал"</string> - <string name="use_double_space_period_summary" msgid="6532892187247952799">"Падвойнае нацiсканне на прабел ўстаўляе iнтэрвал з наступным прабелам"</string> - <string name="auto_cap" msgid="1719746674854628252">"Аўтаматычна рабіць вялікія літары"</string> - <string name="auto_cap_summary" msgid="7934452761022946874">"Пісаць з загалоўнай літары першае слова ў кожным сказе"</string> - <string name="edit_personal_dictionary" msgid="3996910038952940420">"Персанальны слоўнік"</string> - <string name="configure_dictionaries_title" msgid="4238652338556902049">"Дадатковыя слоўнікі"</string> - <string name="main_dictionary" msgid="4798763781818361168">"Асноўны слоўнік"</string> - <string name="prefs_show_suggestions" msgid="8026799663445531637">"Паказаць прапановы на выпраўленне"</string> - <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Паказваць прапанаваныя словы падчас набору тэксту"</string> - <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Заўсёды паказваць"</string> - <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Паказаць у партрэтным рэжыме"</string> - <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Заўседы хаваць"</string> - <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Блакіраваць абразлівыя словы"</string> - <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Не прапануйце патэнцыяльна абразлівых слоў"</string> - <string name="auto_correction" msgid="7630720885194996950">"Аўтавыпраўленне"</string> - <string name="auto_correction_summary" msgid="5625751551134658006">"Прабелы і пунктуацыйныя знакі дазваляюць аўтаматычна выпраўляць памылкова ўведзеныя словы"</string> - <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Адключаны"</string> - <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Сціплы"</string> - <!-- no translation found for auto_correction_threshold_mode_aggressive (7319007299148899623) --> - <skip /> - <!-- no translation found for auto_correction_threshold_mode_very_aggressive (1853309024129480416) --> - <skip /> - <string name="bigram_prediction" msgid="1084449187723948550">"Падказкi для наступнага слова"</string> - <string name="bigram_prediction_summary" msgid="3896362682751109677">"Выкарыстоўваць папярэдняе слова, каб атрымлiваць падказкi"</string> - <string name="gesture_input" msgid="826951152254563827">"Уключыць набор жэстамі"</string> - <string name="gesture_input_summary" msgid="9180350639305731231">"Уводзьце слова, перасоўваючы палец па літарах"</string> - <string name="gesture_preview_trail" msgid="3802333369335722221">"Паказаць след жэста"</string> - <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Дынамічны плаваючы прагляд"</string> - <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Праглядаць прапанаванае слова падчас жэсту"</string> - <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Захаваныя"</string> - <string name="spoken_use_headphones" msgid="896961781287283493">"Каб праслухаць паролi, падключыце гарнiтуру."</string> - <string name="spoken_current_text_is" msgid="2485723011272583845">"Бягучы тэкст %s"</string> - <string name="spoken_no_text_entered" msgid="7479685225597344496">"Тэкст не ўведзены"</string> - <!-- no translation found for spoken_auto_correct (8005997889020109763) --> - <skip /> - <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) --> - <skip /> - <string name="spoken_description_unknown" msgid="3197434010402179157">"Клавішны код %d"</string> - <string name="spoken_description_shift" msgid="244197883292549308">"Зрух"</string> - <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift уключаны (націснiце, каб адключыць)"</string> - <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps Lock уключаны (націснiце, каб адключыць)"</string> - <string name="spoken_description_delete" msgid="8740376944276199801">"Выдаліць"</string> - <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Сімвалы"</string> - <string name="spoken_description_to_alpha" msgid="23129338819771807">"Літары"</string> - <string name="spoken_description_to_numeric" msgid="591752092685161732">"Лічбы"</string> - <string name="spoken_description_settings" msgid="4627462689603838099">"Налады"</string> - <string name="spoken_description_tab" msgid="2667716002663482248">"Укладка"</string> - <string name="spoken_description_space" msgid="2582521050049860859">"Прабел"</string> - <string name="spoken_description_mic" msgid="615536748882611950">"Галасавы ўвод"</string> - <string name="spoken_description_smiley" msgid="2256309826200113918">"Смайлік"</string> - <string name="spoken_description_return" msgid="8178083177238315647">"Увод"</string> - <string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string> - <string name="spoken_description_dot" msgid="40711082435231673">"Кропка"</string> - <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пераключыць мову"</string> - <string name="spoken_description_action_next" msgid="8636078276664150324">"Далей"</string> - <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string> - <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift уключаны"</string> - <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock уключаны"</string> - <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift адключаны"</string> - <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Рэжым знакаў"</string> - <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Рэжым лiтар"</string> - <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Рэжым тэлефона"</string> - <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Рэжым тэлефонных знакаў"</string> - <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Клавіятура схавана"</string> - <string name="announce_keyboard_mode" msgid="4729081055438508321">"Паказана клавiятура ў рэжыме \" <xliff:g id="MODE">%s</xliff:g>\""</string> - <string name="keyboard_mode_date" msgid="3137520166817128102">"дата"</string> - <string name="keyboard_mode_date_time" msgid="339593358488851072">"дата i час"</string> - <string name="keyboard_mode_email" msgid="6216248078128294262">"электронная пошта"</string> - <string name="keyboard_mode_im" msgid="1137405089766557048">"абмен паведамленнямі"</string> - <string name="keyboard_mode_number" msgid="7991623440699957069">"нумар"</string> - <string name="keyboard_mode_phone" msgid="6851627527401433229">"тэлефон"</string> - <string name="keyboard_mode_text" msgid="6479436687899701619">"тэкст"</string> - <string name="keyboard_mode_time" msgid="4381856885582143277">"час"</string> - <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> - <string name="voice_input" msgid="3583258583521397548">"Ключ галасавога ўводу"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"На асн. клавіятуры"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"На сімв. клавіятуры"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Адключана"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Мік. на асн. клав."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Мік. на сімв. клав."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Галасавы набор адкл."</string> - <string name="configure_input_method" msgid="373356270290742459">"Налада метадаў уводу"</string> - <string name="language_selection_title" msgid="1651299598555326750">"Мовы ўводу"</string> - <string name="send_feedback" msgid="1780431884109392046">"Адправіць водгук"</string> - <string name="select_language" msgid="3693815588777926848">"Мовы ўводу"</string> - <string name="hint_add_to_dictionary" msgid="573678656946085380">"Дакраніцеся зноў, каб захаваць"</string> - <string name="has_dictionary" msgid="6071847973466625007">"Слоўнік даступны"</string> - <string name="prefs_enable_log" msgid="6620424505072963557">"Уключыць зваротную сувязь з карыстальнікамі"</string> - <string name="prefs_description_log" msgid="7525225584555429211">"Дапамажыце палепшыць гэты рэдактар метаду ўводу, аўтаматычна адпраўляючы статыстыку выкарыстання і справаздачы аб збоях Google."</string> - <string name="keyboard_layout" msgid="8451164783510487501">"Тэма клавіятуры"</string> - <string name="subtype_en_GB" msgid="88170601942311355">"Англійская (ЗК)"</string> - <string name="subtype_en_US" msgid="6160452336634534239">"Англійская (ЗША)"</string> - <string name="subtype_es_US" msgid="5583145191430180200">"iспанская (ЗША)"</string> - <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Англійская (Вялікабрытанія) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string> - <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Англійская (ЗША) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string> - <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"iспанская (ЗША) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string> - <!-- no translation found for subtype_nepali_traditional (9032247506728040447) --> - <skip /> - <!-- no translation found for subtype_no_language (7137390094240139495) --> - <skip /> - <!-- no translation found for subtype_no_language_qwerty (244337630616742604) --> - <skip /> - <!-- no translation found for subtype_no_language_qwertz (443066912507547976) --> - <skip /> - <!-- no translation found for subtype_no_language_azerty (8144348527575640087) --> - <skip /> - <!-- no translation found for subtype_no_language_dvorak (1564494667584718094) --> - <skip /> - <!-- no translation found for subtype_no_language_colemak (5837418400010302623) --> - <skip /> - <!-- no translation found for subtype_no_language_pcqwerty (5354918232046200018) --> - <skip /> - <!-- no translation found for subtype_emoji (7483586578074549196) --> - <skip /> - <string name="custom_input_styles_title" msgid="8429952441821251512">"Карыстальніцкія стылі ўводу"</string> - <string name="add_style" msgid="6163126614514489951">"Дадаць стыль"</string> - <string name="add" msgid="8299699805688017798">"Дадаць"</string> - <string name="remove" msgid="4486081658752944606">"Выдаліць"</string> - <string name="save" msgid="7646738597196767214">"Захаваць"</string> - <string name="subtype_locale" msgid="8576443440738143764">"Мова"</string> - <string name="keyboard_layout_set" msgid="4309233698194565609">"Раскладка"</string> - <string name="custom_input_style_note_message" msgid="8826731320846363423">"Карыстальніцкі метад уводу павінен быць уключаны, перш чым пачаць выкарыстоўваць яго. Жадаеце ўключыць яго зараз?"</string> - <string name="enable" msgid="5031294444630523247">"Уключыць"</string> - <string name="not_now" msgid="6172462888202790482">"Не цяпер"</string> - <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Такі метад уводу ўжо існуе: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string> - <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Рэжым даследвання выкарыстальнасці"</string> - <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Затрымка доўгага націску клавішы"</string> - <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Працягласць вібрацыі пры націску клавіш"</string> - <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Гучнасць гуку пры націску клавіш"</string> - <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Чытанне знешняга файла слоўніка"</string> - <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"У папцы загрузак няма файлаў слоўніка"</string> - <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Вылучыце файл слоўніка для ўсталёўкі"</string> - <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Сапраўды ўсталяваць гэты файл на мове: <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string> - <string name="error" msgid="8940763624668513648">"Была памылка"</string> - <string name="button_default" msgid="3988017840431881491">"Па змаўчанні"</string> - <string name="setup_welcome_title" msgid="6112821709832031715">"Вітаем у прыкладанні <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="setup_welcome_additional_description" msgid="8150252008545768953">"з уводам жэстамі"</string> - <string name="setup_start_action" msgid="8936036460897347708">"Пачаць"</string> - <string name="setup_next_action" msgid="371821437915144603">"Далей"</string> - <string name="setup_steps_title" msgid="6400373034871816182">"Наладка прыкладання <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="setup_step1_title" msgid="3147967630253462315">"Уключыць прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string> - <string name="setup_step1_instruction" msgid="2578631936624637241">"Праверце прыкладанне \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" на сваёй мове і параметры ўводу. Гэта дасць магчымасць дазволіць яму працаваць на вашай прыладзе."</string> - <string name="setup_step1_finished_instruction" msgid="10761482004957994">"Прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g> ужо ўключана для вашай мовы і параметраў уводу, так што гэты крок зроблены. Пераходзім да наступнага!"</string> - <string name="setup_step1_action" msgid="4366513534999901728">"Уключыць у наладах"</string> - <string name="setup_step2_title" msgid="6860725447906690594">"Пераключыцца на прыкладанне <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string> - <string name="setup_step2_instruction" msgid="9141481964870023336">"Выберыце \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" як актыўны метад уводу тэксту."</string> - <string name="setup_step2_action" msgid="1660330307159824337">"Пераключэнне метадаў уводу"</string> - <string name="setup_step3_title" msgid="3154757183631490281">"Усё гатова!"</string> - <string name="setup_step3_instruction" msgid="8025981829605426000">"Цяпер вы можаце ўводзіць ўсе свае любімыя прыкладанні з iмем <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string> - <string name="setup_step3_action" msgid="600879797256942259">"Наладка дадатковых моў"</string> - <string name="setup_finish_action" msgid="276559243409465389">"Гатова"</string> - <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Паказаць значок прыкладання"</string> - <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Паказаць значок прыкладання ў панэлi запуску"</string> - <string name="app_name" msgid="6320102637491234792">"Пастаўшчык слоўніка"</string> - <string name="dictionary_provider_name" msgid="3027315045397363079">"Пастаўшчык слоўніка"</string> - <string name="dictionary_service_name" msgid="6237472350693511448">"Слоўнік"</string> - <string name="download_description" msgid="6014835283119198591">"Інфармацыя абнаўлення слоўніка"</string> - <string name="dictionary_settings_title" msgid="8091417676045693313">"Дадатковыя слоўнікі"</string> - <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Даступны слоўнік"</string> - <string name="dictionary_settings_summary" msgid="5305694987799824349">"Налады для слоўнікаў"</string> - <string name="user_dictionaries" msgid="3582332055892252845">"Карыстальніцкія слоўнікі"</string> - <string name="default_user_dict_pref_name" msgid="1625055720489280530">"Карыстацкі слоўнік"</string> - <string name="dictionary_available" msgid="4728975345815214218">"Даступны слоўнік"</string> - <string name="dictionary_downloading" msgid="2982650524622620983">"Спампоўваецца зараз"</string> - <string name="dictionary_installed" msgid="8081558343559342962">"Усталявана"</string> - <string name="dictionary_disabled" msgid="8950383219564621762">"Усталявана, адключана"</string> - <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Праблема падключэння да слоўніка"</string> - <string name="no_dictionaries_available" msgid="8039920716566132611">"Слоўнікаў няма"</string> - <string name="check_for_updates_now" msgid="8087688440916388581">"Абнавіць"</string> - <string name="last_update" msgid="730467549913588780">"Апошняе абнаўленне"</string> - <string name="message_updating" msgid="4457761393932375219">"Праверка наяўнасці абнаўленняў"</string> - <string name="message_loading" msgid="8689096636874758814">"Загрузка..."</string> - <string name="main_dict_description" msgid="3072821352793492143">"Асноўны слоўнік"</string> - <string name="cancel" msgid="6830980399865683324">"Адмяніць"</string> - <string name="install_dict" msgid="180852772562189365">"Усталяваць"</string> - <string name="cancel_download_dict" msgid="7843340278507019303">"Адмена"</string> - <string name="delete_dict" msgid="756853268088330054">"Выдаліць"</string> - <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Для выбранай мовы на мабільнай прыладзе ёсць слоўнік.<br/> Мы рэкамендуем <b>спампаваць</b> слоўнік для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\" для паляпшэння зручнасці набору.<br/> <br/> Спампоўка можа заняць хвіліну або дзве ў 3G-сетках. Калі ў вас няма <b>безлімітнага тарыфнага плану перадачы дадзеных</b>, могуць прымяняцца дадатковыя плацяжы<br/>. Калі вы не ведаеце дакладна, які ў вас тарыфны план, мы рэкамендуем знайсці падлучэнне да сеткі Wi-Fi, каб пачаць аўтаматычную спампоўку.<br/> <br/> Парада: можна спампоўваць і выдаляць слоўнікі, перайшоўшы ў раздзел <b>Мова і ўвод</b> у меню <b>Налады</b> вашай мабільнай прылады."</string> - <string name="download_over_metered" msgid="1643065851159409546">"Спампаваць зараз (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>МБ)"</string> - <string name="do_not_download_over_metered" msgid="2176209579313941583">"Спампаваць праз Wi-Fi"</string> - <string name="dict_available_notification_title" msgid="6514288591959117288">"Слоўнік для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\""</string> - <string name="dict_available_notification_description" msgid="1075194169443163487">"Нацiснiце, каб прагледзець i спампаваць"</string> - <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Загрузка: прапановы для мовы \"<xliff:g id="LANGUAGE">%1$s</xliff:g>\" хутка з\'явяцца."</string> - <string name="version_text" msgid="2715354215568469385">"Версія <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string> - <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Дадаць"</string> - <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Дадаць у слоўнік"</string> - <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Выраз"</string> - <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Дадатковыя параметры"</string> - <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Асн. параметры"</string> - <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OК"</string> - <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Слова:"</string> - <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Шлях хуткага доступу:"</string> - <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Мова:"</string> - <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Увядзіце слова"</string> - <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Дадатковы цэтлiк"</string> - <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Рэдагаваць слова"</string> - <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Рэдагаваць"</string> - <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Выдаліць"</string> - <string name="user_dict_settings_empty_text" msgid="558499587532668203">"У вашым карыстальніцкім слоўніку няма ніводнага слова. Вы можаце дадаваць словы, дакранаючыся да кнопкі \"+\" у пункце меню \"Дадаць\"."</string> - <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Для ўсіх моў"</string> - <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Іншыя мовы..."</string> - <string name="user_dict_settings_delete" msgid="110413335187193859">"Выдаліць"</string> - <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> -</resources> diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml index c3fbd7982..604c8d97c 100644 --- a/java/res/values-bg/strings.xml +++ b/java/res/values-bg/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"часа"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL адреси"</string> <string name="voice_input" msgid="3583258583521397548">"Клавиш за гласово въвеждане"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"На осн. клавиатура"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"На клав. на симв."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Изкл."</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Микр. на осн. клав."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Микр. на клав. на симв."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Глас. въвежд. е деакт."</string> <string name="configure_input_method" msgid="373356270290742459">"Конфигуриране на въвеждането"</string> <string name="language_selection_title" msgid="1651299598555326750">"Входни езици"</string> <string name="send_feedback" msgid="1780431884109392046">"Изпращане на отзиви"</string> diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml index 0b9ee037e..e4623da51 100644 --- a/java/res/values-ca/strings.xml +++ b/java/res/values-ca/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tecla d\'entrada de veu"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Al teclat principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Al teclat de símbols"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Desactivada"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micròfon al teclat principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en tecl. símb."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de veu desactivada"</string> <string name="configure_input_method" msgid="373356270290742459">"Configura mètodes d\'entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomes"</string> <string name="send_feedback" msgid="1780431884109392046">"Envia comentaris"</string> diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml index c73e8ab1c..4b883e35a 100644 --- a/java/res/values-cs/strings.xml +++ b/java/res/values-cs/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"čas"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"adresy URL"</string> <string name="voice_input" msgid="3583258583521397548">"Klávesa hlasového vstupu"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Na hlavní klávesnici"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Na klávesnici se symboly"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Vypnuto"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon na hlavní klávesnici"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon na klávesnici se symboly"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup vypnut"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurace metod zadávání"</string> <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string> <string name="send_feedback" msgid="1780431884109392046">"Odeslat zpětnou vazbu"</string> diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml index 86bdad469..33ac25b45 100644 --- a/java/res/values-da/strings.xml +++ b/java/res/values-da/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"klokkeslæt"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"Webadresse"</string> <string name="voice_input" msgid="3583258583521397548">"Nøgle til stemmeinput"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"På hovedtastatur"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"På symboltastatur"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Fra"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mik. på hovedtastatur"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. på symboltastatur"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Stemmeinput deaktiveret"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inputmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string> <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string> diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml index b65053465..563f4c277 100644 --- a/java/res/values-de/strings.xml +++ b/java/res/values-de/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"Zeit"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Taste für Spracheingabe"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Auf Haupttastatur"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Auf Symboltastatur"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Aus"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikro auf Haupttastatur"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikro auf Symboltastatur"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spracheingabe deaktiviert"</string> <string name="configure_input_method" msgid="373356270290742459">"Eingabemethoden konfigurieren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string> <string name="send_feedback" msgid="1780431884109392046">"Feedback geben"</string> diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml index 79e83423c..138d64da5 100644 --- a/java/res/values-el/strings.xml +++ b/java/res/values-el/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ώρα"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"διεύθυνση URL"</string> <string name="voice_input" msgid="3583258583521397548">"Κλειδί φωνητικής εξόδου"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Στο κύριο πληκτρολ."</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Πληκτρ. συμβ. ενερ."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Απενεργοποίηση"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Μικ. στο κύριο πληκ."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Μικ. στο πληκ. συμβ."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Απεν. φωνητ. είσοδος"</string> <string name="configure_input_method" msgid="373356270290742459">"Διαμόρφωση μεθόδων εισαγωγής"</string> <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string> <string name="send_feedback" msgid="1780431884109392046">"Αποστολή σχολίων"</string> diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml index 4bc1b15cf..56a8b90de 100644 --- a/java/res/values-en-rGB/strings.xml +++ b/java/res/values-en-rGB/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"time"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"On main keyboard"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"On symbols keyboard"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Off"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic on main keyboard"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic on symbols keyboard"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string> <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string> <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string> <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string> diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml index 4bc1b15cf..56a8b90de 100644 --- a/java/res/values-en-rIN/strings.xml +++ b/java/res/values-en-rIN/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"time"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"On main keyboard"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"On symbols keyboard"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Off"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic on main keyboard"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic on symbols keyboard"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string> <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string> <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string> <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string> diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml index 1fd9cf8f0..e33bbd5a7 100644 --- a/java/res/values-es-rUS/strings.xml +++ b/java/res/values-es-rUS/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tecla de entrada por voz"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"En el teclado principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"En el teclado de símbolos"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Desactivado"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micrófono en el teclado principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrófono en el teclado de símbolos"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"La entrada por voz está inhabilitada"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> <string name="send_feedback" msgid="1780431884109392046">"Enviar comentarios"</string> diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml index 39b45e0c4..8240c89a5 100644 --- a/java/res/values-es/strings.xml +++ b/java/res/values-es/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tecla de entrada de voz"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"En teclado principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"En teclado de símbolos"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"No"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micrófono en teclado principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrófono en teclado de símbolos"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entrada de voz inhabilitada"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas"</string> <string name="send_feedback" msgid="1780431884109392046">"Danos tu opinión"</string> diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml index e0f992c0d..a01d5636c 100644 --- a/java/res/values-et-rEE/strings.xml +++ b/java/res/values-et-rEE/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"aeg"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Häälesisendi klahv"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Peamisel klaviatuuril"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Sümbolite klaviatuuril"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Väljas"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon peamisel klaviatuuril"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. sümb. klaviat."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Kõnesisend on keelatud"</string> <string name="configure_input_method" msgid="373356270290742459">"Sisestusmeetodite seadistamine"</string> <string name="language_selection_title" msgid="1651299598555326750">"Sisestuskeeled"</string> <string name="send_feedback" msgid="1780431884109392046">"Saatke tagasisidet"</string> diff --git a/java/res/values-iw/donottranslate.xml b/java/res/values-fa/config-spacing-and-punctuations.xml index 57de2538b..9e637e458 100644 --- a/java/res/values-iw/donottranslate.xml +++ b/java/res/values-fa/config-spacing-and-punctuations.xml @@ -21,5 +21,5 @@ <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <!-- Symbols that are suggested between words --> - <string name="suggested_punctuations">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> + <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> </resources> diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml index af886ef8c..714dbc8fc 100644 --- a/java/res/values-fa/strings.xml +++ b/java/res/values-fa/strings.xml @@ -121,12 +121,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"زمان"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"نشانی اینترنتی"</string> <string name="voice_input" msgid="3583258583521397548">"کلید ورودی صدا"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"در صفحهکلید اصلی"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"در صفحهکلید نمادها"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"خاموش"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"میکروفن در صفحهکلید اصلی"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحهکلید نمادها"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ورودی صدا غیرفعال است"</string> <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روشهای ورودی"</string> <string name="language_selection_title" msgid="1651299598555326750">"زبانهای ورودی"</string> <string name="send_feedback" msgid="1780431884109392046">"ارسال بازخورد"</string> diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml index a58bfac3e..f623d1a1e 100644 --- a/java/res/values-fi/strings.xml +++ b/java/res/values-fi/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"aika"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL-osoite"</string> <string name="voice_input" msgid="3583258583521397548">"Äänisyöteavain"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Päänäppäimistössä"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Symbolinäppäim."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Ei käytössä"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikr. päänäppäim."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. symbolinäpp."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Äänisyöte ei käyt."</string> <string name="configure_input_method" msgid="373356270290742459">"Määritä syöttötavat"</string> <string name="language_selection_title" msgid="1651299598555326750">"Syöttökielet"</string> <string name="send_feedback" msgid="1780431884109392046">"Lähetä palautetta"</string> diff --git a/java/res/values-fr-rCA/donottranslate.xml b/java/res/values-fr-rCA/config-spacing-and-punctuations.xml index 21f18d852..06254800b 100644 --- a/java/res/values-fr-rCA/donottranslate.xml +++ b/java/res/values-fr-rCA/config-spacing-and-punctuations.xml @@ -20,12 +20,12 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> <!-- This is similar to French with the exception of "!" "?" and ";" which do not take a space before in Canadian French. Note that ":" does take a space before according to Canadian rules. --> - <string name="symbols_preceded_by_space">([{&:</string> + <string name="symbols_preceded_by_space" translatable="false">([{&:</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> - <string name="symbols_followed_by_space">.,;:!?)]}&</string> + <string name="symbols_followed_by_space" translatable="false">.,;:!?)]}&</string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> - <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> + <string name="symbols_word_separators" translatable="false">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> <!-- Word connectors --> - <string name="symbols_word_connectors">\'-</string> + <string name="symbols_word_connectors" translatable="false">\'-</string> </resources> diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml index 2551ce9f8..8ff60f621 100644 --- a/java/res/values-fr-rCA/strings.xml +++ b/java/res/values-fr-rCA/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"Heure"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Touche de saisie vocale"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Sur le clavier principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Sur clavier symboles"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Désactiver"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micro sur le clavier principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro sur le clavier des symboles"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string> <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string> <string name="send_feedback" msgid="1780431884109392046">"Envoyer des commentaires"</string> diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/config-spacing-and-punctuations.xml index f0644118a..33e0236b0 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/config-spacing-and-punctuations.xml @@ -19,12 +19,12 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> - <string name="symbols_preceded_by_space">([{&;:!?</string> + <string name="symbols_preceded_by_space" translatable="false">([{&;:!?</string> <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> - <string name="symbols_followed_by_space">.,;:!?)]}&</string> + <string name="symbols_followed_by_space" translatable="false">.,;:!?)]}&</string> <!-- Symbols that separate words --> <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> - <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> + <string name="symbols_word_separators" translatable="false">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> <!-- Word connectors --> - <string name="symbols_word_connectors">\'-</string> + <string name="symbols_word_connectors" translatable="false">\'-</string> </resources> diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml index b877db014..9ab95583d 100644 --- a/java/res/values-fr/strings.xml +++ b/java/res/values-fr/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"Heure"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Touche de saisie vocale"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Sur clavier principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Sur clavier symboles"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Désactiver"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micro sur le clavier principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro sur clavier symboles"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string> <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string> <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string> <string name="send_feedback" msgid="1780431884109392046">"Envoyer des commentaires"</string> diff --git a/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml b/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml index bc7928d6a..adc3e35a9 100644 --- a/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml +++ b/java/res/values-h1200dp-port/setup-dimens-large-tablet-port.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">38sp</dimen> <dimen name="setup_step_bullet_text_size">24sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">24sp</dimen> <dimen name="setup_step_instruction_text_size">18sp</dimen> <dimen name="setup_step_action_text_size">20sp</dimen> diff --git a/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml b/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml index aebf6d2f3..1ff43ff9f 100644 --- a/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml +++ b/java/res/values-h330dp-land/setup-dimens-large-phone-land.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">22sp</dimen> <dimen name="setup_step_bullet_text_size">22sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">20sp</dimen> <dimen name="setup_step_instruction_text_size">16sp</dimen> <dimen name="setup_step_action_text_size">18sp</dimen> diff --git a/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml b/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml index aedf79fb2..a0e30cd37 100644 --- a/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml +++ b/java/res/values-h520dp-land/setup-dimens-small-tablet-land.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">32sp</dimen> <dimen name="setup_step_bullet_text_size">24sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">24sp</dimen> <dimen name="setup_step_instruction_text_size">18sp</dimen> <dimen name="setup_step_action_text_size">20sp</dimen> diff --git a/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml b/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml index 6d66f46eb..cf2a10a9e 100644 --- a/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml +++ b/java/res/values-h540dp-port/setup-dimens-large-phone-port.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">26sp</dimen> <dimen name="setup_step_bullet_text_size">22sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">20sp</dimen> <dimen name="setup_step_instruction_text_size">16sp</dimen> <dimen name="setup_step_action_text_size">18sp</dimen> diff --git a/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml b/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml index e22b741fb..a782ef8f4 100644 --- a/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml +++ b/java/res/values-h720dp-land/setup-dimens-large-tablet-land.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">38sp</dimen> <dimen name="setup_step_bullet_text_size">24sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">24sp</dimen> <dimen name="setup_step_instruction_text_size">18sp</dimen> <dimen name="setup_step_action_text_size">20sp</dimen> diff --git a/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml b/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml index 86cf3a04a..9ac0f115f 100644 --- a/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml +++ b/java/res/values-h800dp-port/setup-dimens-small-tablet-port.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">36sp</dimen> <dimen name="setup_step_bullet_text_size">24sp</dimen> <dimen name="setup_step_triangle_indicator_height">24dp</dimen> - <dimen name="setup_step_indicator_height">24dp</dimen> <dimen name="setup_step_title_text_size">24sp</dimen> <dimen name="setup_step_instruction_text_size">18sp</dimen> <dimen name="setup_step_action_text_size">20sp</dimen> diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml index d7735433d..d2ace1ac7 100644 --- a/java/res/values-hi/strings.xml +++ b/java/res/values-hi/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"समय"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"ध्वनि इनपुट कुंजी"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"मुख्य कीबोर्ड पर"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"प्रतीक कीबोर्ड पर"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"बंद"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"मुख्य कीबोर्ड पर माइक"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"प्रतीक कीबोर्ड पर माइक"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ध्वनि इनपुट अक्षम है"</string> <string name="configure_input_method" msgid="373356270290742459">"इनपुट पद्धति कॉन्फ़िगर करें"</string> <string name="language_selection_title" msgid="1651299598555326750">"इनपुट भाषा"</string> <string name="send_feedback" msgid="1780431884109392046">"सुझाव भेजें"</string> diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml index b9cfef384..e7a72f9c4 100644 --- a/java/res/values-hr/strings.xml +++ b/java/res/values-hr/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"vrijeme"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tipka za glasovni unos"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Na glavnoj tipkovnici"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Na tipkovnici simb."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Isključeno"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon na gl. tipkovnici"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. simb."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. unos onemog."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfiguriraj načine ulaza"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jezici unosa"</string> <string name="send_feedback" msgid="1780431884109392046">"Slanje povratnih informacija"</string> diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml index a61378fca..27efd0c92 100644 --- a/java/res/values-hu/strings.xml +++ b/java/res/values-hu/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"idő"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Hangbeviteli gomb"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"A fő billentyűzeten"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Szimbólumoknál"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Ki"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon a billentyűzeten"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr. a szimbólumoknál"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hangbevivel KI"</string> <string name="configure_input_method" msgid="373356270290742459">"Beviteli módok beállítása"</string> <string name="language_selection_title" msgid="1651299598555326750">"Beviteli nyelvek"</string> <string name="send_feedback" msgid="1780431884109392046">"Visszajelzés küldése"</string> diff --git a/java/res/values-hy-rAM/donottranslate.xml b/java/res/values-hy-rAM/config-spacing-and-punctuations.xml index 7b0c56655..f26a30def 100644 --- a/java/res/values-hy-rAM/donottranslate.xml +++ b/java/res/values-hy-rAM/config-spacing-and-punctuations.xml @@ -22,11 +22,11 @@ <!-- 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> + <string name="symbols_followed_by_space" translatable="false">.,;:!?)]}&։՝</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> + <string name="symbols_word_separators" translatable="false">"	 \n"()[]{}*&<>+=|.,;:!?/_\"։՝</string> <!-- The sentence separator code point, for capitalization --> <!-- U+0589: "։" ARMENIAN FULL STOP ; 589h = 1417d --> - <integer name="sentence_separator">1417</integer> + <integer name="sentence_separator" translatable="false">1417</integer> </resources> diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml index 0b8e19a76..62dc45b1f 100644 --- a/java/res/values-hy-rAM/strings.xml +++ b/java/res/values-hy-rAM/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ժամանակ"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Ձայնային մուտքագրման ստեղն"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Հիմնական ստեղնաշարի վրա"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Նշանների ստեղնաշարի վրա"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Անջատված"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Բարձրախոս հիմնական ստեղնաշարի վրա"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Բարձրախոս նշանների ստեղնաշարի վրա"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ձայնային մուտքագրումն անջատված է"</string> <string name="configure_input_method" msgid="373356270290742459">"Կարգավորել մուտքագրման մեթոդները"</string> <string name="language_selection_title" msgid="1651299598555326750">"Մուտքագրման լեզուներ"</string> <string name="send_feedback" msgid="1780431884109392046">"Արձագանքել"</string> diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml index d83a22c6c..83530f2b9 100644 --- a/java/res/values-in/strings.xml +++ b/java/res/values-in/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"waktu"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tombol masukan suara"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Pada keyboard utama"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Pada keyboard simbol"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Mati"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mik pada keyboard utama"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik pada keyboard simbol"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Masukan suara dinonaktifkan"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan metode masukan"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa masukan"</string> <string name="send_feedback" msgid="1780431884109392046">"Kirim masukan"</string> diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml index 6f685d395..1137fd09d 100644 --- a/java/res/values-is/strings.xml +++ b/java/res/values-is/strings.xml @@ -214,18 +214,6 @@ <skip /> <!-- no translation found for voice_input (3583258583521397548) --> <skip /> - <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) --> - <skip /> - <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) --> - <skip /> - <!-- no translation found for voice_input_modes_off (3745699748218082014) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_off (63875609591897607) --> - <skip /> <!-- no translation found for configure_input_method (373356270290742459) --> <skip /> <!-- no translation found for language_selection_title (1651299598555326750) --> diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml index 1111c4901..12b597ac3 100644 --- a/java/res/values-it/strings.xml +++ b/java/res/values-it/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tasto input vocale"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Su tastiera principale"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Su tastiera simboli"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"OFF"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Microfono su tastiera principale"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Microfono su tastiera simboli"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input vocale disatt."</string> <string name="configure_input_method" msgid="373356270290742459">"Configura metodi di immissione"</string> <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string> <string name="send_feedback" msgid="1780431884109392046">"Invia feedback"</string> diff --git a/java/res/values-ar/donottranslate.xml b/java/res/values-iw/config-spacing-and-punctuations.xml index 57de2538b..9e637e458 100644 --- a/java/res/values-ar/donottranslate.xml +++ b/java/res/values-iw/config-spacing-and-punctuations.xml @@ -21,5 +21,5 @@ <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <!-- Symbols that are suggested between words --> - <string name="suggested_punctuations">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> + <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(|),)|(,\',-,/,@,_</string> </resources> diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml index 8d02e685b..ab15649d0 100644 --- a/java/res/values-iw/strings.xml +++ b/java/res/values-iw/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"זמן"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"כתובות אתרים"</string> <string name="voice_input" msgid="3583258583521397548">"מקש קלט קולי"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"במקלדת הראשית"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"במקלדת סמלים"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"כבוי"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"מיקרופון במקלדת הראשית"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"מיקרופון במקלדת סמלים"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"הקלט הקולי מושבת"</string> <string name="configure_input_method" msgid="373356270290742459">"הגדרת שיטות קלט"</string> <string name="language_selection_title" msgid="1651299598555326750">"שפות קלט"</string> <string name="send_feedback" msgid="1780431884109392046">"שלח משוב"</string> diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml index fbfd3b7f7..39f14ff35 100644 --- a/java/res/values-ja/strings.xml +++ b/java/res/values-ja/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"時刻"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"音声入力キー"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"メインキーボード上"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"記号キーボード上"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"OFF"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"メインキーボードのマイク"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"記号キーボードのマイク"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"音声入力は無効です"</string> <string name="configure_input_method" msgid="373356270290742459">"入力方法を設定"</string> <string name="language_selection_title" msgid="1651299598555326750">"入力言語"</string> <string name="send_feedback" msgid="1780431884109392046">"フィードバックを送信"</string> diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index dec6b3a6b..2adf9c440 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"დრო"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"ხმოვანი შეყვანის კლავიში"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"მთავარ კლავიატურაზე"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"სიმბოლოთა კლავიატურაზე"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"გამორთვა"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"მიკროფონი მთავარ კლავიატურაზე"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"მიკროფონი სიმბოლოთა კლავიატურაზე"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ხმოვანი შეყვანა გამორთულია"</string> <string name="configure_input_method" msgid="373356270290742459">"შეყვანის მეთოდების კონფიგურაცია"</string> <string name="language_selection_title" msgid="1651299598555326750">"შეყვანის ენები"</string> <string name="send_feedback" msgid="1780431884109392046">"უკუკავშირის გაგზავნა"</string> diff --git a/java/res/values-kk/strings.xml b/java/res/values-kk/strings.xml index 947ff2fe9..80b289f4d 100644 --- a/java/res/values-kk/strings.xml +++ b/java/res/values-kk/strings.xml @@ -119,12 +119,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"уақыт"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Дауыстық енгізу пернесі"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Негізгі пернетақтада"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Таңбалар пернетақтасында"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Өшірулі"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Негізгі пернетақтадағы Mic"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Таңбалар пернетақтасындағы Mic"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Дауыстық енгізу өшірілген"</string> <string name="configure_input_method" msgid="373356270290742459">"Енгізу әдістерін теңшеу"</string> <string name="language_selection_title" msgid="1651299598555326750">"Енгізу тілдері"</string> <string name="send_feedback" msgid="1780431884109392046">"Кері байланыс жіберу"</string> diff --git a/java/res/values-km-rKH/donottranslate.xml b/java/res/values-km-rKH/config-spacing-and-punctuations.xml index a9893feec..a9893feec 100644 --- a/java/res/values-km-rKH/donottranslate.xml +++ b/java/res/values-km-rKH/config-spacing-and-punctuations.xml diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml index 86ecc5e10..0609bd26d 100644 --- a/java/res/values-km-rKH/strings.xml +++ b/java/res/values-km-rKH/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ពេលវេលា"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"គ្រាប់ចុចបញ្ចូលសំឡេង"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"នៅលើក្ដារចុចចម្បង"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"នៅលើក្ដារចុចនិមិត្តសញ្ញា"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"បិទ"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"មីក្រូហ្វូននៅលើក្ដារចុចចម្បង"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"មីក្រូហ្វូននៅលើក្ដារចុចនិមិត្តសញ្ញា"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"បានបិទការបញ្ចូលសំឡេង"</string> <string name="configure_input_method" msgid="373356270290742459">"កំណត់រចនាសម្ព័ន្ធវិធីសាស្ត្របញ្ចូល"</string> <string name="language_selection_title" msgid="1651299598555326750">"បញ្ចូលភាសា"</string> <string name="send_feedback" msgid="1780431884109392046">"ផ្ញើមតិអ្នកប្រើ"</string> diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml index ca10bdf52..678cc3ee4 100644 --- a/java/res/values-ko/strings.xml +++ b/java/res/values-ko/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"시간"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"음성 입력 키"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"기본 키보드"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"기호 키보드"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"사용 안함"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"기본 키보드의 마이크"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"기호 키보드의 마이크"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"음성 입력이 사용 중지됨"</string> <string name="configure_input_method" msgid="373356270290742459">"입력 방법 설정"</string> <string name="language_selection_title" msgid="1651299598555326750">"입력 언어"</string> <string name="send_feedback" msgid="1780431884109392046">"의견 보내기"</string> diff --git a/java/res/values-ky/strings.xml b/java/res/values-ky/strings.xml index e30c4b965..c3aab7858 100644 --- a/java/res/values-ky/strings.xml +++ b/java/res/values-ky/strings.xml @@ -168,18 +168,6 @@ <skip /> <!-- no translation found for voice_input (3583258583521397548) --> <skip /> - <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) --> - <skip /> - <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) --> - <skip /> - <!-- no translation found for voice_input_modes_off (3745699748218082014) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_off (63875609591897607) --> - <skip /> <!-- no translation found for configure_input_method (373356270290742459) --> <skip /> <!-- no translation found for language_selection_title (1651299598555326750) --> diff --git a/java/res/values-land/config.xml b/java/res/values-land/config.xml index 7d93cc2ff..b6ef4a7ad 100644 --- a/java/res/values-land/config.xml +++ b/java/res/values-land/config.xml @@ -18,6 +18,68 @@ */ --> +<!-- Configuration values for Small Phone Landscape. --> <resources> <bool name="config_use_fullscreen_mode">true</bool> + + <!-- Preferable keyboard height in absolute scale: 1.100in --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">176.0dp</dimen> + <fraction name="config_min_keyboard_height">45%p</fraction> + <!-- key_height + key_bottom_gap = config_more_keys_keyboard_key_height --> + <dimen name="config_more_keys_keyboard_key_height">44.8dp</dimen> + + <fraction name="config_keyboard_top_padding_gb">1.818%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">0.0%p</fraction> + <fraction name="config_key_vertical_gap_gb">5.941%p</fraction> + <fraction name="config_key_horizontal_gap_gb">0.997%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">2.727%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> + <fraction name="config_key_vertical_gap_holo">5.368%p</fraction> + <fraction name="config_key_horizontal_gap_holo">1.020%p</fraction> + + <!-- left or right padding of label alignment --> + <dimen name="config_key_label_horizontal_padding">8dp</dimen> + + <fraction name="config_key_letter_ratio">65%</fraction> + <fraction name="config_key_large_letter_ratio">74%</fraction> + <fraction name="config_key_label_ratio">40%</fraction> + <fraction name="config_key_hint_letter_ratio">30%</fraction> + <fraction name="config_key_hint_label_ratio">52%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">40%</fraction> + <fraction name="config_key_preview_text_ratio">90%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">40.000%</fraction> + <dimen name="config_key_preview_offset_gb">0.0dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">3.20%p</fraction> + <fraction name="config_key_letter_ratio_5row">78%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">48%</fraction> + + <dimen name="config_key_preview_offset_holo">1.6dp</dimen> + <!-- config_more_keys_keyboard_key_height x -0.5 --> + <dimen name="config_more_keys_keyboard_vertical_correction_holo">-22.4dp</dimen> + + <dimen name="config_suggestions_strip_height">36dp</dimen> + <dimen name="config_more_suggestions_row_height">36dp</dimen> + <integer name="config_max_more_suggestions_row">2</integer> + <fraction name="config_min_more_suggestions_width">60%</fraction> + <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> + <!-- config_more_keys_keyboard_key_height x 1.2 --> + <dimen name="config_more_keys_keyboard_slide_allowance">53.76dp</dimen> + <!-- config_more_keys_keyboard_key_height x -1.0 --> + <dimen name="config_more_keys_keyboard_vertical_correction_gb">-44.8dp</dimen> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">23dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">54dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">23dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">15dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">10%p</fraction> + <fraction name="config_emoji_keyboard_row_height">50%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">54%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">20</integer> </resources> diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml deleted file mode 100644 index c97e68f11..000000000 --- a/java/res/values-land/dimens.xml +++ /dev/null @@ -1,83 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2008, 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> - <!-- Preferable keyboard height in absolute scale: 1.100in --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">176.0dp</dimen> - <fraction name="minKeyboardHeight">45%p</fraction> - <!-- key_height + key_bottom_gap = popup_key_height --> - <dimen name="popup_key_height">44.8dp</dimen> - - <fraction name="keyboard_top_padding_gb">1.818%p</fraction> - <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction> - <fraction name="key_bottom_gap_gb">5.941%p</fraction> - <fraction name="key_horizontal_gap_gb">0.997%p</fraction> - - <fraction name="keyboard_top_padding_holo">2.727%p</fraction> - <fraction name="keyboard_bottom_padding_holo">0.0%p</fraction> - <fraction name="key_bottom_gap_holo">5.368%p</fraction> - <fraction name="key_horizontal_gap_holo">1.020%p</fraction> - - <!-- left or right padding of label alignment --> - <dimen name="key_label_horizontal_padding">8dp</dimen> - - <fraction name="key_letter_ratio">65%</fraction> - <fraction name="key_large_letter_ratio">74%</fraction> - <fraction name="key_label_ratio">40%</fraction> - <fraction name="key_hint_letter_ratio">30%</fraction> - <fraction name="key_hint_label_ratio">52%</fraction> - <fraction name="key_uppercase_letter_ratio">40%</fraction> - <fraction name="key_preview_text_ratio">90%</fraction> - <fraction name="spacebar_text_ratio">40.000%</fraction> - <dimen name="key_preview_offset_gb">0.0dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">3.20%p</fraction> - <fraction name="key_letter_ratio_5row">78%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">48%</fraction> - - <dimen name="key_preview_offset_holo">1.6dp</dimen> - <!-- popup_key_height x -0.5 --> - <dimen name="more_keys_keyboard_vertical_correction_holo">-22.4dp</dimen> - - <dimen name="suggestions_strip_height">36dp</dimen> - <dimen name="more_suggestions_row_height">36dp</dimen> - <integer name="max_more_suggestions_row">2</integer> - <fraction name="min_more_suggestions_width">60%</fraction> - <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> - <!-- popup_key_height x 1.2 --> - <dimen name="more_keys_keyboard_slide_allowance">53.76dp</dimen> - <!-- popup_key_height x -1.0 --> - <dimen name="more_keys_keyboard_vertical_correction_gb">-44.8dp</dimen> - - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">23dp</dimen> - <dimen name="gesture_floating_preview_text_offset">54dp</dimen> - <dimen name="gesture_floating_preview_horizontal_padding">23dp</dimen> - <dimen name="gesture_floating_preview_vertical_padding">15dp</dimen> - - <!-- Emoji keyboard --> - <fraction name="emoji_keyboard_key_width">10%p</fraction> - <fraction name="emoji_keyboard_row_height">50%p</fraction> - <fraction name="emoji_keyboard_key_letter_size">54%p</fraction> - <integer name="emoji_keyboard_max_key_count">20</integer> - -</resources> diff --git a/java/res/values-land/setup-dimens-small-phone-land.xml b/java/res/values-land/setup-dimens-small-phone-land.xml index 088e6562a..de93eee08 100644 --- a/java/res/values-land/setup-dimens-small-phone-land.xml +++ b/java/res/values-land/setup-dimens-small-phone-land.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">18sp</dimen> <dimen name="setup_step_bullet_text_size">18sp</dimen> <dimen name="setup_step_triangle_indicator_height">18dp</dimen> - <dimen name="setup_step_indicator_height">18dp</dimen> <dimen name="setup_step_title_text_size">18sp</dimen> <dimen name="setup_step_instruction_text_size">14sp</dimen> <dimen name="setup_step_action_text_size">16sp</dimen> diff --git a/java/res/values-lo-rLA/donottranslate.xml b/java/res/values-lo-rLA/config-spacing-and-punctuations.xml index a9893feec..a9893feec 100644 --- a/java/res/values-lo-rLA/donottranslate.xml +++ b/java/res/values-lo-rLA/config-spacing-and-punctuations.xml diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index a4dbc2de2..bbf1715b8 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ເວລາ"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"ປຸ່ມປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"ແປ້ນພິມຫຼັກ"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"ໃນແປ້ນພິມສັນຍາລັກ"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"ປິດ"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ໄມໃນແປ້ນພິມຫຼັກ"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ໄມໃນແປ້ນພິມສັນຍາລັກ"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງປິດນຳໃຊ້ຢູ່"</string> <string name="configure_input_method" msgid="373356270290742459">"ຕັ້ງຄ່າຮູບແບບການປ້ອນຂໍ້ມູນ"</string> <string name="language_selection_title" msgid="1651299598555326750">"ພາສາການປ້ອນຂໍ້ມູນ"</string> <string name="send_feedback" msgid="1780431884109392046">"ສົ່ງຄຳຕິຊົມ"</string> diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml index 1f943944c..bf37143db 100644 --- a/java/res/values-lt/strings.xml +++ b/java/res/values-lt/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"laiko"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Įvesties balsu klavišas"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Pagr. klaviatūroje"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Simbolių klaviatūr."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Išjungta"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrof. pagr. klav."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrof. simb. klav."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balso įv. neleidž."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigūruoti įvesties metodus"</string> <string name="language_selection_title" msgid="1651299598555326750">"Įvesties kalbos"</string> <string name="send_feedback" msgid="1780431884109392046">"Siųsti atsiliepimą"</string> diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml index 8ea24edb7..9afea26b8 100644 --- a/java/res/values-lv/strings.xml +++ b/java/res/values-lv/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"laiks"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Balss ievades atslēga"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Uz galv. tastatūras"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Uz simbolu tastat."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Izslēgts"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikr.uz galv.tastat."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikr.uz simb.tastat."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Balss iev. atspējota"</string> <string name="configure_input_method" msgid="373356270290742459">"Ievades metožu konfigurēšana"</string> <string name="language_selection_title" msgid="1651299598555326750">"Ievades valodas"</string> <string name="send_feedback" msgid="1780431884109392046">"Sūtīt atsauksmes"</string> diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml index 6f685d395..1137fd09d 100644 --- a/java/res/values-mk/strings.xml +++ b/java/res/values-mk/strings.xml @@ -214,18 +214,6 @@ <skip /> <!-- no translation found for voice_input (3583258583521397548) --> <skip /> - <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) --> - <skip /> - <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) --> - <skip /> - <!-- no translation found for voice_input_modes_off (3745699748218082014) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_off (63875609591897607) --> - <skip /> <!-- no translation found for configure_input_method (373356270290742459) --> <skip /> <!-- no translation found for language_selection_title (1651299598555326750) --> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index d4175899f..4b36f03d6 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"цаг"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Дуун оруулгын товч"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Үндсэн гар дээр"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Симбол гар дээр"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Идэвхгүй"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Мик үндсэн гар дээр"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Мик симбол гар дээр"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Дуун оруулах идэвхгүйжсэн"</string> <string name="configure_input_method" msgid="373356270290742459">"Оруулах аргуудын тохиргоо"</string> <string name="language_selection_title" msgid="1651299598555326750">"Оруулах хэл"</string> <string name="send_feedback" msgid="1780431884109392046">"Санал хүсэлт илгээх"</string> diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml index c9b4a0359..d3ab3f97c 100644 --- a/java/res/values-ms-rMY/strings.xml +++ b/java/res/values-ms-rMY/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"masa"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Kunci input suara"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Pada papan kekunci utama"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Pada papan kekunci simbol"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Dimati"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon pada papan kekunci utama"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon pada papan kekunci simbol"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Input suara dilmphkn"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan kaedah input"</string> <string name="language_selection_title" msgid="1651299598555326750">"Bahasa input"</string> <string name="send_feedback" msgid="1780431884109392046">"Hantar maklum balas"</string> diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml index 00aa10da7..6d9f64fb3 100644 --- a/java/res/values-nb/strings.xml +++ b/java/res/values-nb/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"tid"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"Nettadresse"</string> <string name="voice_input" msgid="3583258583521397548">"Tast for taleinndata"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"På hovedtastatur"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"På talltastatur"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Av"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon på hovedtast."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon på talltastatur"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Taleinndata er deaktiv."</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inndatametoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string> <string name="send_feedback" msgid="1780431884109392046">"Send tilbakemelding"</string> @@ -186,7 +180,7 @@ <string name="setup_step3_title" msgid="3154757183631490281">"Gratulerer, du er klar!"</string> <string name="setup_step3_instruction" msgid="8025981829605426000">"Nå kan du skrive inn alle favorittappene dine med <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string> <string name="setup_step3_action" msgid="600879797256942259">"Konfigurer flere språk"</string> - <string name="setup_finish_action" msgid="276559243409465389">"Ferdig"</string> + <string name="setup_finish_action" msgid="276559243409465389">"Fullført"</string> <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Vis app-ikonet"</string> <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Vis app-ikonet i appvelgeren"</string> <string name="app_name" msgid="6320102637491234792">"Ordlisteleverandør"</string> diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml index dcbf2c09c..f99c34520 100644 --- a/java/res/values-nl/strings.xml +++ b/java/res/values-nl/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"tijd"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Toets voor spraakinvoer"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Op hoofdtoetsenbord"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Op symbooltoetsenb."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Uitgeschakeld"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Microfoon op hoofdtoetsenbord"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic op symb.toetsb."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Spraakinvoer is uit"</string> <string name="configure_input_method" msgid="373356270290742459">"Invoermethoden configureren"</string> <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string> <string name="send_feedback" msgid="1780431884109392046">"Feedback verzenden"</string> diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml index c78674a9b..558415d89 100644 --- a/java/res/values-pl/strings.xml +++ b/java/res/values-pl/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"godzina"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Klawisz rozpoznawania mowy"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Na klawiaturze głównej"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Na klawiaturze z symbolami"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Wyłącz"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon na klawiaturze głównej"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon na klawiaturze z symbolami"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Rozpoznawanie mowy jest wyłączone"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfiguruj metody wprowadzania"</string> <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string> <string name="send_feedback" msgid="1780431884109392046">"Prześlij opinię"</string> diff --git a/java/res/values-port/setup-dimens-small-phone-port.xml b/java/res/values-port/setup-dimens-small-phone-port.xml index 8ac72ea7c..cf2751f1b 100644 --- a/java/res/values-port/setup-dimens-small-phone-port.xml +++ b/java/res/values-port/setup-dimens-small-phone-port.xml @@ -20,7 +20,6 @@ <dimen name="setup_welcome_description_text_size">20sp</dimen> <dimen name="setup_step_bullet_text_size">18sp</dimen> <dimen name="setup_step_triangle_indicator_height">18dp</dimen> - <dimen name="setup_step_indicator_height">18dp</dimen> <dimen name="setup_step_title_text_size">18sp</dimen> <dimen name="setup_step_instruction_text_size">14sp</dimen> <dimen name="setup_step_action_text_size">16sp</dimen> diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml index c27758116..36425e174 100644 --- a/java/res/values-pt-rPT/strings.xml +++ b/java/res/values-pt-rPT/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URLs"</string> <string name="voice_input" msgid="3583258583521397548">"Chave de entrada de voz"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"No teclado principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"No teclado símbolos"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Desligar"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic. tecl. principal"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. tecl. símbolos"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. voz desact."</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introdução"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> <string name="send_feedback" msgid="1780431884109392046">"Enviar comentários"</string> diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml index f98ef8cd6..925adf0b0 100644 --- a/java/res/values-pt/strings.xml +++ b/java/res/values-pt/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Chave de entrada de texto por voz"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"No teclado principal"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"No teclado de símb."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Desativado"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic. no teclado"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic. no teclado"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Texto por voz desat."</string> <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string> <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string> <string name="send_feedback" msgid="1780431884109392046">"Enviar comentários"</string> diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml index 3f0bab963..378c25426 100644 --- a/java/res/values-rm/strings.xml +++ b/java/res/values-rm/strings.xml @@ -209,18 +209,6 @@ <skip /> <!-- no translation found for voice_input (3583258583521397548) --> <skip /> - <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) --> - <skip /> - <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) --> - <skip /> - <!-- no translation found for voice_input_modes_off (3745699748218082014) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) --> - <skip /> - <!-- no translation found for voice_input_modes_summary_off (63875609591897607) --> - <skip /> <!-- no translation found for configure_input_method (373356270290742459) --> <skip /> <string name="language_selection_title" msgid="1651299598555326750">"Linguas da cumonds vocals"</string> diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml index 147f83e61..9758a0b62 100644 --- a/java/res/values-ro/strings.xml +++ b/java/res/values-ro/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ore"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"adrese URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tastă pentru intrarea vocală"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Pe tastat. princip."</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Pe tastat. simbol."</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Dezactivată"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic. pe tast. princ."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micr. pe tast. simb."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Intr. vocală dezact."</string> <string name="configure_input_method" msgid="373356270290742459">"Configuraţi metodele de intrare"</string> <string name="language_selection_title" msgid="1651299598555326750">"Selectaţi limba"</string> <string name="send_feedback" msgid="1780431884109392046">"Trimiteți feedback"</string> diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml index 8bbaead0a..36990fe41 100644 --- a/java/res/values-ru/strings.xml +++ b/java/res/values-ru/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ввода времени"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"ввода URL"</string> <string name="voice_input" msgid="3583258583521397548">"Кнопка голосового ввода"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Значок на основной клавиатуре"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Значок на клавиатуре символов"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Выкл."</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Значок на основной клавиатуре"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Значок на клавиатуре символов"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голосовой ввод откл."</string> <string name="configure_input_method" msgid="373356270290742459">"Настройка способов ввода"</string> <string name="language_selection_title" msgid="1651299598555326750">"Языки ввода"</string> <string name="send_feedback" msgid="1780431884109392046">"Отправить отзыв"</string> diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml index d1f966cea..42e55fa1f 100644 --- a/java/res/values-sk/strings.xml +++ b/java/res/values-sk/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"čas"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"Adresa URL"</string> <string name="voice_input" msgid="3583258583521397548">"Kľúč hlasového vstupu"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Na hlavnej klávesnici"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Na klávesnici so symbolmi"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Vypnuté"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofón na hlavnej klávesnici"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofón na klávesnici so symbolmi"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hlasový vstup je zakázaný"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurovať metódy vstupu"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jazyky vstupu"</string> <string name="send_feedback" msgid="1780431884109392046">"Odoslať spätnú väzbu"</string> diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml index a0f83c12f..59113e30b 100644 --- a/java/res/values-sl/strings.xml +++ b/java/res/values-sl/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"ura"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Tipka za glasovni vnos"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Na glavni tipkovnici"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Na tipk. s simboli"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Izklopljeno"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mik. na glavni tipk."</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mik. na tipk. s sim."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Glas. vnos je onem."</string> <string name="configure_input_method" msgid="373356270290742459">"Nastavitev načinov vnosa"</string> <string name="language_selection_title" msgid="1651299598555326750">"Jeziki vnosa"</string> <string name="send_feedback" msgid="1780431884109392046">"Pošljite povratne informacije"</string> diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml index ce4978ff5..663cb327d 100644 --- a/java/res/values-sr/strings.xml +++ b/java/res/values-sr/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"време"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Тастер за гласовни унос"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"На главној тастатури"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"На тастатури са симболима"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Искључи"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Микрофон на главној тастатури"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Микрофон на тастатури са симболима"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Гласовни унос је онемогућен"</string> <string name="configure_input_method" msgid="373356270290742459">"Конфигурисање метода уноса"</string> <string name="language_selection_title" msgid="1651299598555326750">"Језици за унос"</string> <string name="send_feedback" msgid="1780431884109392046">"Пошаљи повратне информације"</string> diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml index afe349afd..f5e129af9 100644 --- a/java/res/values-sv/strings.xml +++ b/java/res/values-sv/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"klockslag"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"webbadresser"</string> <string name="voice_input" msgid="3583258583521397548">"Röstinmatningsknapp"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"På huvudtangentbord"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"På symboltangentbord"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Av"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mick huvudtangentbord"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mick bland symboler"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Röstinmatning inaktiv"</string> <string name="configure_input_method" msgid="373356270290742459">"Konfigurera inmatningsmetoder"</string> <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string> <string name="send_feedback" msgid="1780431884109392046">"Skicka feedback"</string> diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml index 191ad977c..8e23496d1 100644 --- a/java/res/values-sw/strings.xml +++ b/java/res/values-sw/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"wakati"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Kibao cha kuweka data kwa kutamka"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Kwenye kibodi kuu"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Kwenye kibodi ya ishara"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Zima"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Maikrofoni kwenye kibodi kuu"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Maikrofoni kwenye kibodi ya ishara"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Kipengele cha kuweka data kwa kutamka kimezimwa"</string> <string name="configure_input_method" msgid="373356270290742459">"Sanidi mbinu za uingizaji"</string> <string name="language_selection_title" msgid="1651299598555326750">"Lugha za uingizaji"</string> <string name="send_feedback" msgid="1780431884109392046">"Tuma maoni"</string> diff --git a/java/res/values-sw540dp-land/config.xml b/java/res/values-sw540dp-land/config.xml index b3cd7278d..55dcd5145 100644 --- a/java/res/values-sw540dp-land/config.xml +++ b/java/res/values-sw540dp-land/config.xml @@ -18,6 +18,57 @@ */ --> +<!-- Configuration values for Large Phone and Small Tablet Landscape. --> <resources> <bool name="config_use_fullscreen_mode">false</bool> + + <!-- Preferable keyboard height in absolute scale: 45.0mm --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">283.5dp</dimen> + <fraction name="config_min_keyboard_height">45%p</fraction> + + <fraction name="config_keyboard_top_padding_gb">2.444%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">0.0%p</fraction> + <fraction name="config_key_vertical_gap_gb">5.200%p</fraction> + <fraction name="config_key_horizontal_gap_gb">1.447%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">2.727%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> + <fraction name="config_key_vertical_gap_holo">4.5%p</fraction> + <fraction name="config_key_horizontal_gap_holo">0.9%p</fraction> + + <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen> + + <!-- left or right padding of label alignment --> + <dimen name="config_key_label_horizontal_padding">18dp</dimen> + + <fraction name="config_key_letter_ratio">50%</fraction> + <fraction name="config_key_large_letter_ratio">48%</fraction> + <fraction name="config_key_label_ratio">32%</fraction> + <fraction name="config_key_hint_letter_ratio">23%</fraction> + <fraction name="config_key_hint_label_ratio">34%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">29%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">30.0%</fraction> + <dimen name="config_key_shifted_letter_hint_padding">4dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">3.20%p</fraction> + <fraction name="config_key_letter_ratio_5row">62%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">36%</fraction> + + <dimen name="config_suggestions_strip_horizontal_padding">252.0dp</dimen> + <integer name="config_max_more_suggestions_row">5</integer> + <fraction name="config_min_more_suggestions_width">50%</fraction> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">26dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">76dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">26dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">17dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">10%p</fraction> + <fraction name="config_emoji_keyboard_row_height">33%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">70%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">30</integer> </resources> diff --git a/java/res/values-sw540dp-land/dimens.xml b/java/res/values-sw540dp-land/dimens.xml deleted file mode 100644 index 002493798..000000000 --- a/java/res/values-sw540dp-land/dimens.xml +++ /dev/null @@ -1,72 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2011, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources> - <!-- Preferable keyboard height in absolute scale: 45.0mm --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">283.5dp</dimen> - <fraction name="minKeyboardHeight">45%p</fraction> - - <fraction name="keyboard_top_padding_gb">2.444%p</fraction> - <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction> - <fraction name="key_bottom_gap_gb">5.200%p</fraction> - <fraction name="key_horizontal_gap_gb">1.447%p</fraction> - - <fraction name="keyboard_top_padding_holo">2.727%p</fraction> - <fraction name="keyboard_bottom_padding_holo">0.0%p</fraction> - <fraction name="key_bottom_gap_holo">4.5%p</fraction> - <fraction name="key_horizontal_gap_holo">0.9%p</fraction> - - <dimen name="popup_key_height">81.9dp</dimen> - - <!-- left or right padding of label alignment --> - <dimen name="key_label_horizontal_padding">18dp</dimen> - - <fraction name="key_letter_ratio">50%</fraction> - <fraction name="key_large_letter_ratio">48%</fraction> - <fraction name="key_label_ratio">32%</fraction> - <fraction name="key_hint_letter_ratio">23%</fraction> - <fraction name="key_hint_label_ratio">34%</fraction> - <fraction name="key_uppercase_letter_ratio">29%</fraction> - <fraction name="spacebar_text_ratio">30.0%</fraction> - <dimen name="key_uppercase_letter_padding">4dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">3.20%p</fraction> - <fraction name="key_letter_ratio_5row">62%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">36%</fraction> - - <dimen name="suggestions_strip_padding">252.0dp</dimen> - <integer name="max_more_suggestions_row">5</integer> - <fraction name="min_more_suggestions_width">50%</fraction> - - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">26dp</dimen> - <dimen name="gesture_floating_preview_text_offset">76dp</dimen> - <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen> - <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen> - - <!-- Emoji keyboard --> - <fraction name="emoji_keyboard_key_width">10%p</fraction> - <fraction name="emoji_keyboard_row_height">33%p</fraction> - <fraction name="emoji_keyboard_key_letter_size">70%p</fraction> - <integer name="emoji_keyboard_max_key_count">30</integer> - -</resources> diff --git a/java/res/values-hdpi/config.xml b/java/res/values-sw540dp/config-screen-metrics.xml index 4cf3562fe..1205aaa1e 100644 --- a/java/res/values-hdpi/config.xml +++ b/java/res/values-sw540dp/config-screen-metrics.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2011, The Android Open Source Project +** 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. @@ -20,11 +20,10 @@ <resources> <!-- Screen metrics for logging. - 0 = "mdpi phone screen" - 1 = "hdpi phone screen" - 2 = "mdpi 11 inch tablet screen" - 3 = "xhdpi phone screen?" - 4 = ? + 0 = "small phone screen" + 1 = "large phone screen" + 2 = "large tablet screen" + 3 = "small tablet screen" --> <integer name="log_screen_metrics">1</integer> </resources> diff --git a/java/res/values-sw540dp/config.xml b/java/res/values-sw540dp/config.xml index 027780ce3..09fe7d3bf 100644 --- a/java/res/values-sw540dp/config.xml +++ b/java/res/values-sw540dp/config.xml @@ -18,22 +18,90 @@ */ --> +<!-- Configuration values for Large Phone and Small Tablet Portrait. --> <resources> - <bool name="config_enable_show_option_of_key_preview_popup">false</bool> - <bool name="config_enable_bigram_suggestions_option">false</bool> + <bool name="config_enable_show_key_preview_popup_option">false</bool> <!-- Whether or not Popup on key press is enabled by default --> <bool name="config_default_key_preview_popup">false</bool> <bool name="config_default_sound_enabled">true</bool> - <bool name="config_auto_correction_spacebar_led_enabled">false</bool> - <!-- The language is never displayed if == 0, always displayed if < 0 --> - <integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer> - <integer name="config_max_more_keys_column">5</integer> - <!-- - Configuration for MainKeyboardView - --> + <dimen name="config_key_hysteresis_distance">40.0dp</dimen> - <bool name="config_sliding_key_input_enabled">false</bool> + <bool name="config_key_selection_by_dragging_finger">false</bool> <!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if false --> - <bool name="config_show_more_keys_keyboard_at_touched_point">true</bool> + <bool name="config_show_more_keys_keyboard_at_touched_point">false</bool> + + <!-- Preferable keyboard height in absolute scale: 48.0mm --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">302.4dp</dimen> + <fraction name="config_max_keyboard_height">46%p</fraction> + <fraction name="config_min_keyboard_height">-35.0%p</fraction> + + <dimen name="config_more_keys_keyboard_key_height">63.0dp</dimen> + + <fraction name="config_keyboard_top_padding_gb">2.291%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">0.0%p</fraction> + <fraction name="config_key_vertical_gap_gb">4.625%p</fraction> + <fraction name="config_key_horizontal_gap_gb">2.113%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">2.335%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">4.0%p</fraction> + <fraction name="config_key_vertical_gap_holo">4.5%p</fraction> + <fraction name="config_key_horizontal_gap_holo">1.565%p</fraction> + + <dimen name="config_more_keys_keyboard_key_horizontal_padding">6dp</dimen> + <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> + <!-- config_more_keys_keyboard_key_height x 1.2 --> + <dimen name="config_more_keys_keyboard_slide_allowance">98.3dp</dimen> + <!-- config_more_keys_keyboard_key_height x -1.0 --> + <dimen name="config_more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen> + + <!-- left or right padding of label alignment --> + <dimen name="config_key_label_horizontal_padding">6dp</dimen> + <dimen name="config_key_hint_letter_padding">3dp</dimen> + <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen> + + <fraction name="config_key_letter_ratio">42%</fraction> + <fraction name="config_key_large_letter_ratio">45%</fraction> + <fraction name="config_key_label_ratio">25%</fraction> + <fraction name="config_key_large_label_ratio">32%</fraction> + <fraction name="config_key_hint_letter_ratio">23%</fraction> + <fraction name="config_key_hint_label_ratio">28%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">22%</fraction> + <fraction name="config_key_preview_text_ratio">50%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">28.0%</fraction> + <dimen name="config_key_preview_height">94.5dp</dimen> + <dimen name="config_key_preview_offset_gb">16.0dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">3.20%p</fraction> + <fraction name="config_key_letter_ratio_5row">52%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">27%</fraction> + + <dimen name="config_key_preview_offset_holo">8.0dp</dimen> + <!-- config_more_keys_keyboard_key_height x -0.5 --> + <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> + + <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_more_suggestions_row_height">44dp</dimen> + <integer name="config_max_more_suggestions_row">6</integer> + <fraction name="config_min_more_suggestions_width">90%</fraction> + <dimen name="config_suggestions_strip_horizontal_padding">94.5dp</dimen> + <dimen name="config_suggestion_min_width">48.0dp</dimen> + <dimen name="config_suggestion_text_horizontal_padding">12dp</dimen> + <dimen name="config_suggestion_text_size">22dp</dimen> + <dimen name="config_more_suggestions_hint_text_size">33dp</dimen> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">28dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">87dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">28dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">19dp</dimen> + <dimen name="config_gesture_floating_preview_round_radius">3dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">12.5%p</fraction> + <fraction name="config_emoji_keyboard_row_height">33%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">60%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">24</integer> </resources> diff --git a/java/res/values-sw540dp/dimens.xml b/java/res/values-sw540dp/dimens.xml deleted file mode 100644 index 801b7acb5..000000000 --- a/java/res/values-sw540dp/dimens.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2011, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<resources> - <!-- Preferable keyboard height in absolute scale: 48.0mm --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">302.4dp</dimen> - <fraction name="maxKeyboardHeight">46%p</fraction> - <fraction name="minKeyboardHeight">-35.0%p</fraction> - - <dimen name="popup_key_height">63.0dp</dimen> - - <fraction name="keyboard_top_padding_gb">2.291%p</fraction> - <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction> - <fraction name="key_bottom_gap_gb">4.625%p</fraction> - <fraction name="key_horizontal_gap_gb">2.113%p</fraction> - - <fraction name="keyboard_top_padding_holo">2.335%p</fraction> - <fraction name="keyboard_bottom_padding_holo">4.0%p</fraction> - <fraction name="key_bottom_gap_holo">4.5%p</fraction> - <fraction name="key_horizontal_gap_holo">1.565%p</fraction> - - <dimen name="more_keys_keyboard_key_horizontal_padding">6dp</dimen> - <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> - <!-- popup_key_height x 1.2 --> - <dimen name="more_keys_keyboard_slide_allowance">98.3dp</dimen> - <!-- popup_key_height x -1.0 --> - <dimen name="more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen> - - <!-- left or right padding of label alignment --> - <dimen name="key_label_horizontal_padding">6dp</dimen> - <dimen name="key_hint_letter_padding">3dp</dimen> - <dimen name="key_uppercase_letter_padding">3dp</dimen> - - <fraction name="key_letter_ratio">42%</fraction> - <fraction name="key_large_letter_ratio">45%</fraction> - <fraction name="key_label_ratio">25%</fraction> - <fraction name="key_large_label_ratio">32%</fraction> - <fraction name="key_hint_letter_ratio">23%</fraction> - <fraction name="key_hint_label_ratio">28%</fraction> - <fraction name="key_uppercase_letter_ratio">22%</fraction> - <fraction name="key_preview_text_ratio">50%</fraction> - <fraction name="spacebar_text_ratio">28.0%</fraction> - <dimen name="key_preview_height">94.5dp</dimen> - <dimen name="key_preview_offset_gb">16.0dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">3.20%p</fraction> - <fraction name="key_letter_ratio_5row">52%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">27%</fraction> - - <dimen name="key_preview_offset_holo">8.0dp</dimen> - <!-- popup_key_height x -0.5 --> - <dimen name="more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> - - <dimen name="suggestions_strip_height">44dp</dimen> - <dimen name="more_suggestions_row_height">44dp</dimen> - <integer name="max_more_suggestions_row">6</integer> - <fraction name="min_more_suggestions_width">90%</fraction> - <dimen name="suggestions_strip_padding">94.5dp</dimen> - <dimen name="suggestion_min_width">48.0dp</dimen> - <dimen name="suggestion_padding">12dp</dimen> - <dimen name="suggestion_text_size">22dp</dimen> - <dimen name="more_suggestions_hint_text_size">33dp</dimen> - - <!-- Gesture trail parameters --> - <dimen name="gesture_trail_width">2.5dp</dimen> - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">28dp</dimen> - <dimen name="gesture_floating_preview_text_offset">87dp</dimen> - <dimen name="gesture_floating_preview_horizontal_padding">28dp</dimen> - <dimen name="gesture_floating_preview_vertical_padding">19dp</dimen> - <dimen name="gesture_floating_preview_round_radius">3dp</dimen> - - <!-- Emoji keyboard --> - <fraction name="emoji_keyboard_key_width">12.5%p</fraction> - <fraction name="emoji_keyboard_row_height">33%p</fraction> - <fraction name="emoji_keyboard_key_letter_size">60%p</fraction> - <integer name="emoji_keyboard_max_key_count">24</integer> - -</resources> diff --git a/java/res/values-sw600dp/config-screen-metrics.xml b/java/res/values-sw600dp/config-screen-metrics.xml new file mode 100644 index 000000000..e939c068f --- /dev/null +++ b/java/res/values-sw600dp/config-screen-metrics.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> + <!-- Screen metrics for logging. + 0 = "small phone screen" + 1 = "large phone screen" + 2 = "large tablet screen" + 3 = "small tablet screen" + --> + <integer name="log_screen_metrics">3</integer> +</resources> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index e72e4941e..c2419b639 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -18,6 +18,7 @@ */ --> +<!-- Configuration values for Small Tablet Portrait. --> <resources> <bool name="config_enable_show_voice_key_option">false</bool> </resources> diff --git a/java/res/values-sw768dp-land/config.xml b/java/res/values-sw768dp-land/config.xml index b3cd7278d..b27d4c057 100644 --- a/java/res/values-sw768dp-land/config.xml +++ b/java/res/values-sw768dp-land/config.xml @@ -18,6 +18,58 @@ */ --> +<!-- Configuration values for Large Tablet Landscape. --> <resources> <bool name="config_use_fullscreen_mode">false</bool> + + <!-- Preferable keyboard height in absolute scale: 58.0mm --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">365.4dp</dimen> + <fraction name="config_min_keyboard_height">45%p</fraction> + + <fraction name="config_keyboard_top_padding_gb">1.896%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">0.0%p</fraction> + <fraction name="config_key_vertical_gap_gb">3.896%p</fraction> + <fraction name="config_key_horizontal_gap_gb">1.195%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">1.896%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> + <fraction name="config_key_vertical_gap_holo">3.690%p</fraction> + <fraction name="config_key_horizontal_gap_holo">1.030%p</fraction> + + <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen> + + <!-- left or right padding of label alignment --> + <dimen name="config_key_label_horizontal_padding">18dp</dimen> + + <fraction name="config_key_letter_ratio">43%</fraction> + <fraction name="config_key_large_letter_ratio">42%</fraction> + <fraction name="config_key_label_ratio">28%</fraction> + <fraction name="config_key_hint_letter_ratio">23%</fraction> + <fraction name="config_key_hint_label_ratio">28%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">24%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">24.00%</fraction> + <dimen name="config_key_preview_height">107.1dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">2.65%p</fraction> + <fraction name="config_key_letter_ratio_5row">53%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">30%</fraction> + + <dimen name="config_key_preview_offset_holo">8.0dp</dimen> + + <dimen name="config_suggestions_strip_horizontal_padding">252.0dp</dimen> + <fraction name="config_min_more_suggestions_width">50%</fraction> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">32dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">100dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">32dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">21dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">7.69%p</fraction> + <fraction name="config_emoji_keyboard_row_height">33%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">60%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">39</integer> </resources> diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml deleted file mode 100644 index 653f5e7d5..000000000 --- a/java/res/values-sw768dp-land/dimens.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2010, 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> - <!-- Preferable keyboard height in absolute scale: 58.0mm --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">365.4dp</dimen> - <fraction name="minKeyboardHeight">45%p</fraction> - - <fraction name="keyboard_top_padding_gb">1.896%p</fraction> - <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction> - <fraction name="key_bottom_gap_gb">3.896%p</fraction> - <fraction name="key_horizontal_gap_gb">1.195%p</fraction> - - <fraction name="keyboard_top_padding_holo">1.896%p</fraction> - <fraction name="keyboard_bottom_padding_holo">0.0%p</fraction> - <fraction name="key_bottom_gap_holo">3.690%p</fraction> - <fraction name="key_horizontal_gap_holo">1.030%p</fraction> - - <dimen name="popup_key_height">81.9dp</dimen> - - <!-- left or right padding of label alignment --> - <dimen name="key_label_horizontal_padding">18dp</dimen> - - <fraction name="key_letter_ratio">43%</fraction> - <fraction name="key_large_letter_ratio">42%</fraction> - <fraction name="key_label_ratio">28%</fraction> - <fraction name="key_hint_letter_ratio">23%</fraction> - <fraction name="key_hint_label_ratio">28%</fraction> - <fraction name="key_uppercase_letter_ratio">24%</fraction> - <fraction name="spacebar_text_ratio">24.00%</fraction> - <dimen name="key_preview_height">107.1dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">2.65%p</fraction> - <fraction name="key_letter_ratio_5row">53%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">30%</fraction> - - <dimen name="key_preview_offset_holo">8.0dp</dimen> - - <dimen name="suggestions_strip_padding">252.0dp</dimen> - <fraction name="min_more_suggestions_width">50%</fraction> - - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">32dp</dimen> - <dimen name="gesture_floating_preview_text_offset">100dp</dimen> - <dimen name="gesture_floating_preview_horizontal_padding">32dp</dimen> - <dimen name="gesture_floating_preview_vertical_padding">21dp</dimen> - - <!-- Emoji keyboard --> - <fraction name="emoji_keyboard_key_width">7.69%p</fraction> - <fraction name="emoji_keyboard_row_height">33%p</fraction> - <fraction name="emoji_keyboard_key_letter_size">60%p</fraction> - <integer name="emoji_keyboard_max_key_count">39</integer> - -</resources> diff --git a/java/res/values-sw768dp/config-screen-metrics.xml b/java/res/values-sw768dp/config-screen-metrics.xml new file mode 100644 index 000000000..c555cbb9f --- /dev/null +++ b/java/res/values-sw768dp/config-screen-metrics.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> + <!-- Screen metrics for logging. + 0 = "small phone screen" + 1 = "large phone screen" + 2 = "large tablet screen" + 3 = "small tablet screen" + --> + <integer name="log_screen_metrics">2</integer> +</resources> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index e1c07d6f8..b63b509d5 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -18,28 +18,90 @@ */ --> +<!-- Configuration values for Large Tablet Portrait. --> <resources> <bool name="config_enable_show_voice_key_option">false</bool> - <bool name="config_enable_show_option_of_key_preview_popup">false</bool> - <bool name="config_enable_bigram_suggestions_option">false</bool> + <bool name="config_enable_show_key_preview_popup_option">false</bool> <!-- Whether or not Popup on key press is enabled by default --> <bool name="config_default_key_preview_popup">false</bool> <bool name="config_default_sound_enabled">true</bool> - <bool name="config_auto_correction_spacebar_led_enabled">false</bool> - <integer name="config_max_more_keys_column">5</integer> - <!-- - Configuration for MainKeyboardView - --> - <bool name="config_sliding_key_input_enabled">false</bool> + + <bool name="config_key_selection_by_dragging_finger">false</bool> <!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if false --> <bool name="config_show_more_keys_keyboard_at_touched_point">true</bool> - <!-- Screen metrics for logging. - 0 = "mdpi phone screen" - 1 = "hdpi phone screen" - 2 = "mdpi 11 inch tablet screen" - 3 = "xhdpi phone screen?" - 4 = ? - --> - <integer name="log_screen_metrics">2</integer> + + <!-- Preferable keyboard height in absolute scale: 48.0mm --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">302.4dp</dimen> + <fraction name="config_max_keyboard_height">46%p</fraction> + <fraction name="config_min_keyboard_height">-35.0%p</fraction> + + <fraction name="config_keyboard_top_padding_gb">2.291%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">0.0%p</fraction> + <fraction name="config_key_vertical_gap_gb">4.687%p</fraction> + <fraction name="config_key_horizontal_gap_gb">1.272%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">2.335%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> + <fraction name="config_key_vertical_gap_holo">3.312%p</fraction> + <fraction name="config_key_horizontal_gap_holo">1.066%p</fraction> + + <dimen name="config_more_keys_keyboard_key_height">63.0dp</dimen> + + <dimen name="config_more_keys_keyboard_key_horizontal_padding">12dp</dimen> + <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> + <!-- config_more_keys_keyboard_key_height x 1.2 --> + <dimen name="config_more_keys_keyboard_slide_allowance">98.3dp</dimen> + <!-- config_more_keys_keyboard_key_height x -1.0 --> + <dimen name="config_more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen> + + <!-- left or right padding of label alignment --> + <dimen name="config_key_label_horizontal_padding">6dp</dimen> + <dimen name="config_key_hint_letter_padding">3dp</dimen> + <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen> + + <fraction name="config_key_letter_ratio">40%</fraction> + <fraction name="config_key_large_letter_ratio">42%</fraction> + <fraction name="config_key_label_ratio">28%</fraction> + <fraction name="config_key_large_label_ratio">28%</fraction> + <fraction name="config_key_hint_letter_ratio">23%</fraction> + <fraction name="config_key_hint_label_ratio">28%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">26%</fraction> + <fraction name="config_key_preview_text_ratio">50%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">29.03%</fraction> + <dimen name="config_key_preview_height">94.5dp</dimen> + <dimen name="config_key_preview_offset_gb">16.0dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">2.95%p</fraction> + <fraction name="config_key_letter_ratio_5row">51%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">33%</fraction> + + <dimen name="config_key_preview_offset_holo">8.0dp</dimen> + <!-- config_more_keys_keyboard_key_height x -0.5 --> + <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> + + <dimen name="config_suggestions_strip_height">44dp</dimen> + <dimen name="config_more_suggestions_row_height">44dp</dimen> + <integer name="config_max_more_suggestions_row">6</integer> + <fraction name="config_min_more_suggestions_width">90%</fraction> + <dimen name="config_suggestions_strip_horizontal_padding">94.5dp</dimen> + <dimen name="config_suggestion_min_width">46dp</dimen> + <dimen name="config_suggestion_text_horizontal_padding">8dp</dimen> + <dimen name="config_suggestion_text_size">22dp</dimen> + <dimen name="config_more_suggestions_hint_text_size">33dp</dimen> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">26dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">86dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">26dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">17dp</dimen> + <dimen name="config_gesture_floating_preview_round_radius">3dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">10%p</fraction> + <fraction name="config_emoji_keyboard_row_height">33%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">68%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">30</integer> </resources> diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml deleted file mode 100644 index 4671aa28b..000000000 --- a/java/res/values-sw768dp/dimens.xml +++ /dev/null @@ -1,98 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2010, 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> - <!-- Preferable keyboard height in absolute scale: 48.0mm --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">302.4dp</dimen> - <fraction name="maxKeyboardHeight">46%p</fraction> - <fraction name="minKeyboardHeight">-35.0%p</fraction> - - <fraction name="keyboard_top_padding_gb">2.291%p</fraction> - <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction> - <fraction name="key_bottom_gap_gb">4.687%p</fraction> - <fraction name="key_horizontal_gap_gb">1.272%p</fraction> - - <fraction name="keyboard_top_padding_holo">2.335%p</fraction> - <fraction name="keyboard_bottom_padding_holo">0.0%p</fraction> - <fraction name="key_bottom_gap_holo">3.312%p</fraction> - <fraction name="key_horizontal_gap_holo">1.066%p</fraction> - - <dimen name="popup_key_height">63.0dp</dimen> - - <dimen name="more_keys_keyboard_key_horizontal_padding">12dp</dimen> - <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> - <!-- popup_key_height x 1.2 --> - <dimen name="more_keys_keyboard_slide_allowance">98.3dp</dimen> - <!-- popup_key_height x -1.0 --> - <dimen name="more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen> - - <!-- left or right padding of label alignment --> - <dimen name="key_label_horizontal_padding">6dp</dimen> - <dimen name="key_hint_letter_padding">3dp</dimen> - <dimen name="key_uppercase_letter_padding">3dp</dimen> - - <fraction name="key_letter_ratio">40%</fraction> - <fraction name="key_large_letter_ratio">42%</fraction> - <fraction name="key_label_ratio">28%</fraction> - <fraction name="key_large_label_ratio">28%</fraction> - <fraction name="key_hint_letter_ratio">23%</fraction> - <fraction name="key_hint_label_ratio">28%</fraction> - <fraction name="key_uppercase_letter_ratio">26%</fraction> - <fraction name="key_preview_text_ratio">50%</fraction> - <fraction name="spacebar_text_ratio">29.03%</fraction> - <dimen name="key_preview_height">94.5dp</dimen> - <dimen name="key_preview_offset_gb">16.0dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">2.95%p</fraction> - <fraction name="key_letter_ratio_5row">51%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">33%</fraction> - - <dimen name="key_preview_offset_holo">8.0dp</dimen> - <!-- popup_key_height x -0.5 --> - <dimen name="more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> - - <dimen name="suggestions_strip_height">44dp</dimen> - <dimen name="more_suggestions_row_height">44dp</dimen> - <integer name="max_more_suggestions_row">6</integer> - <fraction name="min_more_suggestions_width">90%</fraction> - <dimen name="suggestions_strip_padding">94.5dp</dimen> - <dimen name="suggestion_min_width">46dp</dimen> - <dimen name="suggestion_padding">8dp</dimen> - <dimen name="suggestion_text_size">22dp</dimen> - <dimen name="more_suggestions_hint_text_size">33dp</dimen> - - <!-- Gesture trail parameters --> - <dimen name="gesture_trail_width">2.5dp</dimen> - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">26dp</dimen> - <dimen name="gesture_floating_preview_text_offset">86dp</dimen> - <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen> - <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen> - <dimen name="gesture_floating_preview_round_radius">3dp</dimen> - - <!-- Emoji keyboard --> - <fraction name="emoji_keyboard_key_width">10%p</fraction> - <fraction name="emoji_keyboard_row_height">33%p</fraction> - <fraction name="emoji_keyboard_key_letter_size">68%p</fraction> - <integer name="emoji_keyboard_max_key_count">30</integer> - -</resources> diff --git a/java/res/values-th/donottranslate.xml b/java/res/values-th/config-spacing-and-punctuations.xml index a9893feec..a9893feec 100644 --- a/java/res/values-th/donottranslate.xml +++ b/java/res/values-th/config-spacing-and-punctuations.xml diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml index 9249c05d5..8b1567679 100644 --- a/java/res/values-th/strings.xml +++ b/java/res/values-th/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"เวลา"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"แป้นการป้อนข้อมูลด้วยเสียง"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"บนแป้นพิมพ์หลัก"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"บนแป้นพิมพ์สัญลักษณ์"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"ปิด"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ไมค์บนแป้นพิมพ์หลัก"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ไมค์บนแป้นพิมพ์สัญลักษณ์"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ปิดใช้งานป้อนข้อมูลด้วยเสียง"</string> <string name="configure_input_method" msgid="373356270290742459">"กำหนดค่าวิธีการป้อนข้อมูล"</string> <string name="language_selection_title" msgid="1651299598555326750">"ภาษาในการป้อนข้อมูล"</string> <string name="send_feedback" msgid="1780431884109392046">"ส่งข้อเสนอแนะ"</string> diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml index df6bda09b..5da0861a9 100644 --- a/java/res/values-tl/strings.xml +++ b/java/res/values-tl/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"oras"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Sa pangunahing keyboard"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Sa keyboard ng mga simbolo"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Naka-off"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic sa pangunahing keyboard"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic sa keyboard ng mga simbolo"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Hindi pinagana ang voice input"</string> <string name="configure_input_method" msgid="373356270290742459">"I-configure ang mga pamamaraan ng pag-input"</string> <string name="language_selection_title" msgid="1651299598555326750">"Mag-input ng mga wika"</string> <string name="send_feedback" msgid="1780431884109392046">"Magpadala ng feedback"</string> diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml index a14295153..7304f464b 100644 --- a/java/res/values-tr/strings.xml +++ b/java/res/values-tr/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"saat"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Ses girişi tuşu"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Ana klavyede"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Simge klavyesinde"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Kapalı"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Ana klavyedeki mikrofon"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Simge klavysnd mikrf"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Sesle grş devre dışı"</string> <string name="configure_input_method" msgid="373356270290742459">"Giriş yöntemlerini yapılandır"</string> <string name="language_selection_title" msgid="1651299598555326750">"Giriş dilleri"</string> <string name="send_feedback" msgid="1780431884109392046">"Geri bildirim gönder"</string> diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml index da26d5005..ab952912d 100644 --- a/java/res/values-uk/strings.xml +++ b/java/res/values-uk/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"час"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL-адреси"</string> <string name="voice_input" msgid="3583258583521397548">"Ключ голосового вводу"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"На основ. клавіатурі"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Символьна клавіатура"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Вимк."</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Мікрофон на основній клавіатурі"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Miкр. на симв. клавіат."</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Голос. ввід вимкнено"</string> <string name="configure_input_method" msgid="373356270290742459">"Налаштування методів введення"</string> <string name="language_selection_title" msgid="1651299598555326750">"Мови вводу"</string> <string name="send_feedback" msgid="1780431884109392046">"Надіслати відгук"</string> diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml index 81cd373e4..d75016a0a 100644 --- a/java/res/values-vi/strings.xml +++ b/java/res/values-vi/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"giờ"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string> <string name="voice_input" msgid="3583258583521397548">"Khóa nhập giọng nói"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Trên bàn phím chính"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Trên bàn phím biểu tượng"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"Tắt"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micrô trên bàn phím chính"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micrô trên bàn phím biểu tượng"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Nhập liệu bằng giọng nói đã bị tắt"</string> <string name="configure_input_method" msgid="373356270290742459">"Định cấu hình phương thức nhập"</string> <string name="language_selection_title" msgid="1651299598555326750">"Ngôn ngữ nhập"</string> <string name="send_feedback" msgid="1780431884109392046">"Gửi phản hồi"</string> diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml index d347c9ce8..1b59935a2 100644 --- a/java/res/values-zh-rCN/strings.xml +++ b/java/res/values-zh-rCN/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"时间"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"网址"</string> <string name="voice_input" msgid="3583258583521397548">"语音输入键"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"主键盘上"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"符号键盘上"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"关闭"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"主键盘上的麦克风"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符号键盘上的麦克风"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"语音输入功能已停用"</string> <string name="configure_input_method" msgid="373356270290742459">"配置输入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string> <string name="send_feedback" msgid="1780431884109392046">"发送反馈"</string> diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml index 306045591..665728db7 100644 --- a/java/res/values-zh-rHK/strings.xml +++ b/java/res/values-zh-rHK/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"時間"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"網址"</string> <string name="voice_input" msgid="3583258583521397548">"語音輸入鍵"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"於主鍵盤"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"符號鍵盤上"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"關閉"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"主鍵盤上的麥克風"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符號鍵盤上的麥克風"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"語音輸入已停用"</string> <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string> <string name="send_feedback" msgid="1780431884109392046">"傳送意見"</string> diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml index 2c474b79e..d082fc744 100644 --- a/java/res/values-zh-rTW/strings.xml +++ b/java/res/values-zh-rTW/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"時間"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"網址"</string> <string name="voice_input" msgid="3583258583521397548">"語音輸入按鍵"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"主鍵盤上"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"符號鍵盤上"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"關閉"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"主鍵盤上的麥克風"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符號鍵盤上的麥克風"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"語音輸入已停用"</string> <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string> <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string> <string name="send_feedback" msgid="1780431884109392046">"提供意見"</string> diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml index 27d11316c..fb2cd6e34 100644 --- a/java/res/values-zu/strings.xml +++ b/java/res/values-zu/strings.xml @@ -117,12 +117,6 @@ <string name="keyboard_mode_time" msgid="4381856885582143277">"isikhathi"</string> <string name="keyboard_mode_url" msgid="1519819835514911218">"I-URL"</string> <string name="voice_input" msgid="3583258583521397548">"Inkinobho yokufaka izwi"</string> - <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Kwikhibhodi eyisisekelo"</string> - <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Ikhibhodi yezimpawu"</string> - <string name="voice_input_modes_off" msgid="3745699748218082014">"VALIWE"</string> - <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"I-mic kwikhibhodi eyisisekelo"</string> - <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Ikhibhodi yezimpawu ze-mic"</string> - <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Okufakwayo ngezwi kuvinjelwe"</string> <string name="configure_input_method" msgid="373356270290742459">"Misa izindlela zokufakwayo"</string> <string name="language_selection_title" msgid="1651299598555326750">"Izilimi zokufakwayo"</string> <string name="send_feedback" msgid="1780431884109392046">"Thumela impendulo"</string> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 31945d020..a9474a03a 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -26,14 +26,14 @@ <attr name="keyboardViewStyle" format="reference" /> <!-- MainKeyboardView style --> <attr name="mainKeyboardViewStyle" format="reference" /> + <!-- Key preview text view style --> + <attr name="keyPreviewTextViewStyle" format="reference"/> <!-- EmojiPalettesView style --> <attr name="emojiPalettesViewStyle" format="reference" /> <!-- MoreKeysKeyboard style --> <attr name="moreKeysKeyboardStyle" format="reference" /> <!-- MoreKeysKeyboardView style --> <attr name="moreKeysKeyboardViewStyle" format="reference" /> - <!-- MoreKeysKeyboardView container style --> - <attr name="moreKeysKeyboardContainerStyle" format="reference" /> <!-- Suggestions strip style --> <attr name="suggestionStripViewStyle" format="reference" /> <!-- Suggestion word style --> @@ -41,9 +41,9 @@ </declare-styleable> <declare-styleable name="KeyboardView"> - <!-- Image for the key. This image needs to be a StateListDrawable, with the following - possible states: normal, pressed, checkable, checkable+pressed, checkable+checked, - checkable+checked+pressed. --> + <!-- Image for the key. This image needs to be a {@link StateListDrawable}, with the + following possible states: normal, pressed, checkable, checkable+pressed, + checkable+checked, checkable+checked+pressed. --> <attr name="keyBackground" format="reference" /> <!-- Image for the functional key used in Emoji layout. --> <attr name="keyBackgroundEmojiFunctional" format="reference" /> @@ -72,9 +72,11 @@ <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" /> <attr name="autoCorrectionSpacebarLedIcon" format="reference" /> <!-- Size of the text for spacebar language label, in the proportion of key height. --> - <attr name="spacebarTextRatio" format="fraction" /> - <attr name="spacebarTextColor" format="color" /> - <attr name="spacebarTextShadowColor" format="color" /> + <attr name="languageOnSpacebarTextRatio" format="fraction" /> + <attr name="languageOnSpacebarTextColor" format="color" /> + <attr name="languageOnSpacebarTextShadowColor" format="color" /> + <!-- Background image for the spacebar. --> + <attr name="spacebarBackground" format="reference" /> <!-- Fadeout animator for spacebar language label. --> <attr name="languageOnSpacebarFinalAlpha" format="integer" /> <attr name="languageOnSpacebarFadeoutAnimator" format="reference" /> @@ -89,8 +91,8 @@ <attr name="touchNoiseThresholdTime" format="integer" /> <!-- Touch noise threshold distance in millimeter --> <attr name="touchNoiseThresholdDistance" format="dimension" /> - <!-- Sliding key input enable --> - <attr name="slidingKeyInputEnable" format="boolean" /> + <!-- Enable key selection by dragging finger --> + <attr name="keySelectionByDraggingFinger" format="boolean" /> <attr name="slidingKeyInputPreviewColor" format="color" /> <attr name="slidingKeyInputPreviewWidth" format="dimension" /> <attr name="slidingKeyInputPreviewBodyRatio" format="integer" /> @@ -109,6 +111,10 @@ <attr name="keyPreviewOffset" format="dimension" /> <!-- Height of the key press feedback popup. --> <attr name="keyPreviewHeight" format="dimension" /> + <!-- Duration of key preview popup zoom in animation in millisecond --> + <attr name="keyPreviewZoomInDuration" format="integer" /> + <!-- Duration of key preview popup zoom out animation in millisecond --> + <attr name="keyPreviewZoomOutDuration" format="integer" /> <!-- Delay after key releasing and key press feedback dismissing in millisecond --> <attr name="keyPreviewLingerTimeout" format="integer" /> <!-- Layout resource for more keys keyboard --> @@ -217,7 +223,6 @@ <attr name="iconSearchKey" format="reference" /> <attr name="iconTabKey" format="reference" /> <attr name="iconShortcutKey" format="reference" /> - <attr name="iconShortcutForLabel" format="reference" /> <attr name="iconSpaceKeyForNumberLayout" format="reference" /> <attr name="iconShiftKeyShifted" format="reference" /> <attr name="iconShortcutKeyDisabled" format="reference" /> @@ -276,6 +281,8 @@ <attr name="keyLabel" format="string" /> <!-- The hint label to display on the key in conjunction with the label. --> <attr name="keyHintLabel" format="string" /> + <!-- The vertical adjustment of key hint label in proportion to its height. --> + <attr name="keyHintLabelVerticalAdjustment" format="fraction" /> <!-- The key label flags. --> <attr name="keyLabelFlags" format="integer"> <!-- This should be aligned with Key.LABEL_FLAGS__* --> @@ -292,17 +299,20 @@ <flag name="hasPopupHint" value="0x200" /> <flag name="hasShiftedLetterHint" value="0x400" /> <flag name="hasHintLabel" value="0x800" /> + <!-- These two flags are currently unused. Leave these for possible future use. --> <flag name="withIconLeft" value="0x1000" /> <flag name="withIconRight" value="0x2000" /> <flag name="autoXScale" value="0x4000" /> + <!-- The autoScale value implies autoXScale bit on to optimize scaling code path. --> + <flag name="autoScale" value="0xc000" /> <!-- If true, character case of code, altCode, moreKeys, keyOutputText, keyLabel, or keyHintLabel will never be subject to change. --> - <flag name="preserveCase" value="0x8000" /> + <flag name="preserveCase" value="0x10000" /> <!-- If true, use keyShiftedLetterHintActivatedColor for the shifted letter hint and keyTextInactivatedColor for the primary key top label. --> - <flag name="shiftedLetterActivated" value="0x10000" /> + <flag name="shiftedLetterActivated" value="0x20000" /> <!-- If true, use EditorInfo.actionLabel for the key label. --> - <flag name="fromCustomActionLabel" value="0x20000" /> + <flag name="fromCustomActionLabel" value="0x40000" /> <!-- If true, disable keyHintLabel. --> <flag name="disableKeyHintLabel" value="0x40000000" /> <!-- If true, disable additionalMoreKeys. --> @@ -415,8 +425,7 @@ <attr name="navigatePrevious" format="boolean" /> <attr name="passwordInput" format="boolean" /> <attr name="clobberSettingsKey" format="boolean" /> - <attr name="shortcutKeyEnabled" format="boolean" /> - <attr name="shortcutKeyOnSymbols" format="boolean" /> + <attr name="supportsSwitchingToShortcutIme" format="boolean" /> <attr name="hasShortcutKey" format="boolean" /> <attr name="languageSwitchKeyEnabled" format="boolean" /> <attr name="isMultiLine" format="boolean" /> diff --git a/java/res/values/config-auto-correction-thresholds.xml b/java/res/values/config-auto-correction-thresholds.xml new file mode 100644 index 000000000..7d94a42a4 --- /dev/null +++ b/java/res/values/config-auto-correction-thresholds.xml @@ -0,0 +1,57 @@ +<?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> + <!-- The array of auto correction threshold values. --> + <string-array name="auto_correction_threshold_values" translatable="false"> + <!-- Off, When auto correction setting is Off, this value is not used. --> + <item>floatMaxValue</item> + <!-- Modest : Suggestion whose normalized score is greater than this value + will be subject to auto-correction. --> + <item>0.185</item> + <!-- Aggressive --> + <item>0.067</item> + <!-- Very Aggressive : Suggestion whose normalized score is greater than this value + will be subject to auto-correction. "floatNegativeInfinity" is a special marker + string for Float.NEGATIVE_INFINITY --> + <item>floatNegativeInfinity</item> + </string-array> + + <!-- The index of the auto correction threshold values array. --> + <string name="auto_correction_threshold_mode_index_off" translatable="false">0</string> + <string name="auto_correction_threshold_mode_index_modest" translatable="false">1</string> + <string name="auto_correction_threshold_mode_index_aggressive" translatable="false">2</string> + <string name="auto_correction_threshold_mode_index_very_aggressive" translatable="false">3</string> + + <!-- The array of the auto correction threshold settings values. --> + <string-array name="auto_correction_threshold_mode_indexes" translatable="false"> + <item>@string/auto_correction_threshold_mode_index_off</item> + <item>@string/auto_correction_threshold_mode_index_modest</item> + <item>@string/auto_correction_threshold_mode_index_aggressive</item> + <item>@string/auto_correction_threshold_mode_index_very_aggressive</item> + </string-array> + <!-- The array of the human readable auto correction threshold settings entries. --> + <string-array name="auto_correction_threshold_modes" translatable="false"> + <item>@string/auto_correction_threshold_mode_off</item> + <item>@string/auto_correction_threshold_mode_modest</item> + <item>@string/auto_correction_threshold_mode_aggressive</item> + <item>@string/auto_correction_threshold_mode_very_aggressive</item> + </string-array> +</resources> diff --git a/java/res/values/config-common.xml b/java/res/values/config-common.xml new file mode 100644 index 000000000..ab16a9045 --- /dev/null +++ b/java/res/values/config-common.xml @@ -0,0 +1,142 @@ +<?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> + <bool name="config_block_potentially_offensive">true</bool> + <!-- Default value for next word prediction: after entering a word and a space only, should we look + at input history to suggest a hopefully helpful suggestions for the next word? --> + <bool name="config_default_next_word_prediction">true</bool> + + <!-- This configuration is an index of {@link KeyboardSwitcher#KEYBOARD_THEMES[]}. --> + <string name="config_default_keyboard_theme_index" translatable="false">2</string> + + <integer name="config_delay_update_shift_state">100</integer> + <integer name="config_double_space_period_timeout">1100</integer> + + <integer name="config_key_repeat_start_timeout">400</integer> + <integer name="config_key_repeat_interval">50</integer> + + <integer name="config_ignore_alt_code_key_timeout">350</integer> + + <integer name="config_key_preview_zoom_in_duration">35</integer> + <integer name="config_key_preview_zoom_out_duration">40</integer> + <integer name="config_key_preview_linger_timeout">70</integer> + <!-- Suppress showing key preview duration after batch input in millisecond --> + <integer name="config_suppress_key_preview_after_batch_input_duration">1000</integer> + + <bool name="config_default_vibration_enabled">true</bool> + <integer name="config_max_vibration_duration">100</integer> + + <integer name="config_default_longpress_key_timeout">300</integer> + <integer name="config_max_longpress_timeout">700</integer> + <integer name="config_min_longpress_timeout">100</integer> + <integer name="config_longpress_timeout_step">10</integer> + <integer name="config_max_more_keys_column">5</integer> + <integer name="config_more_keys_keyboard_fadein_anim_time">0</integer> + <integer name="config_more_keys_keyboard_fadeout_anim_time">100</integer> + + <!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 --> + <integer name="config_longpress_shift_lock_timeout">1200</integer> + + <!-- Sliding key input preview parameters --> + <dimen name="config_sliding_key_input_preview_width">8.0dp</dimen> + <!-- Percentages of sliding key input preview body and shadow, in proportion to the width. + A negative value of the shadow ratio disables drawing shadow. --> + <!-- TODO: May use the shadow to alleviate rugged trail drawing. --> + <integer name="config_sliding_key_input_preview_body_ratio">100</integer> + <integer name="config_sliding_key_input_preview_shadow_ratio">-1</integer> + <dimen name="config_key_hysteresis_distance_for_sliding_modifier">8.0dp</dimen> + + <integer name="config_language_on_spacebar_final_alpha">128</integer> + <dimen name="config_language_on_spacebar_horizontal_margin">1dp</dimen> + + <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer> + <integer name="config_gesture_trail_fadeout_start_delay">100</integer> + <integer name="config_gesture_trail_fadeout_duration">800</integer> + <integer name="config_gesture_trail_update_interval">20</integer> + <!-- Static threshold for gesture after fast typing (msec) --> + <integer name="config_gesture_static_time_threshold_after_fast_typing">500</integer> + <!-- Static threshold for starting gesture detection (keyWidth%/sec) --> + <fraction name="config_gesture_detect_fast_move_speed_threshold">150%</fraction> + <!-- Dynamic threshold for gesture after fast typing (msec) --> + <integer name="config_gesture_dynamic_threshold_decay_duration">450</integer> + <!-- Time based threshold values for gesture detection (msec) --> + <integer name="config_gesture_dynamic_time_threshold_from">300</integer> + <integer name="config_gesture_dynamic_time_threshold_to">20</integer> + <!-- Distance based threshold values for gesture detection (keyWidth%/sec) --> + <fraction name="config_gesture_dynamic_distance_threshold_from">600%</fraction> + <fraction name="config_gesture_dynamic_distance_threshold_to">50%</fraction> + <!-- Parameter for gesture sampling (keyWidth%/sec) --> + <fraction name="config_gesture_sampling_minimum_distance">16.6666%</fraction> + <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) --> + <integer name="config_gesture_recognition_minimum_time">100</integer> + <integer name="config_gesture_recognition_update_time">100</integer> + <fraction name="config_gesture_recognition_speed_threshold">550%</fraction> + + <integer name="config_keyboard_grid_width">32</integer> + <integer name="config_keyboard_grid_height">16</integer> + <dimen name="config_touch_noise_threshold_distance">12.6dp</dimen> + <integer name="config_touch_noise_threshold_time">40</integer> + + <!-- Common keyboard configuration. --> + <fraction name="config_keyboard_left_padding">0%p</fraction> + <fraction name="config_keyboard_right_padding">0%p</fraction> + <dimen name="config_keyboard_vertical_correction">0.0dp</dimen> + + <!-- Common key top visual configuration. --> + <dimen name="config_key_popup_hint_letter_padding">2dp</dimen> + + <!-- Common suggestion strip configuration. --> + <integer name="config_suggestions_count_in_strip">3</integer> + <fraction name="config_center_suggestion_percentile">36%</fraction> + <integer name="config_delay_update_suggestions">100</integer> + <integer name="config_delay_update_old_suggestions">300</integer> + + <!-- Common more suggestions configuraion. --> + <dimen name="config_more_suggestions_key_horizontal_padding">12dp</dimen> + <dimen name="config_more_suggestions_bottom_gap">6dp</dimen> + <dimen name="config_more_suggestions_modal_tolerance">32.0dp</dimen> + <fraction name="config_more_suggestions_info_ratio">18%</fraction> + + <!-- Common gesture trail parameters --> + <!-- Minimum distance between gesture trail sampling points. --> + <dimen name="config_gesture_trail_min_sampling_distance">9.6dp</dimen> + <!-- Maximum angular threshold between gesture trails interpolation segments in degree. --> + <integer name="config_gesture_trail_max_interpolation_angular_threshold">15</integer> + <!-- Maximum distance threshold between gesture trails interpolation segments. --> + <dimen name="config_gesture_trail_max_interpolation_distance_threshold">16.0dp</dimen> + <!-- Maximum number of gesture trail interpolation segments. --> + <integer name="config_gesture_trail_max_interpolation_segments">6</integer> + <dimen name="config_gesture_trail_start_width">10.0dp</dimen> + <dimen name="config_gesture_trail_end_width">2.5dp</dimen> + <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width. + A negative value of the shadow ratio disables drawing shadow. --> + <!-- TODO: May use the shadow to alleviate rugged trail drawing. --> + <integer name="config_gesture_trail_body_ratio">100</integer> + <integer name="config_gesture_trail_shadow_ratio">-1</integer> + + <!-- Common configuration of Emoji keyboard --> + <dimen name="config_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="config_accessibility_edge_slop">8dp</dimen> + + <integer name="config_user_dictionary_max_word_length">48</integer> +</resources> diff --git a/java/res/values/config-dictionary-pack.xml b/java/res/values/config-dictionary-pack.xml new file mode 100644 index 000000000..d076af452 --- /dev/null +++ b/java/res/values/config-dictionary-pack.xml @@ -0,0 +1,30 @@ +<?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. +*/ +--> + +<!-- Configuration values for Dictionary pack. --> +<resources> + <!-- Settings for the dictionary pack --> + <bool name="allow_over_metered">false</bool> + <bool name="allow_over_roaming">false</bool> + <bool name="dict_downloads_visible_in_download_UI">false</bool> + <bool name="metadata_downloads_visible_in_download_UI">false</bool> + <bool name="display_notification_for_auto_update">false</bool> + <bool name="display_notification_for_user_requested_update">false</bool> +</resources> diff --git a/java/res/values/config-screen-metrics.xml b/java/res/values/config-screen-metrics.xml new file mode 100644 index 000000000..7b9870602 --- /dev/null +++ b/java/res/values/config-screen-metrics.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> + <!-- Screen metrics for logging. + 0 = "small phone screen" + 1 = "large phone screen" + 2 = "large tablet screen" + 3 = "small tablet screen" + --> + <integer name="log_screen_metrics">0</integer> +</resources> diff --git a/java/res/values/config-spacing-and-punctuations.xml b/java/res/values/config-spacing-and-punctuations.xml new file mode 100644 index 000000000..f10f810dc --- /dev/null +++ b/java/res/values/config-spacing-and-punctuations.xml @@ -0,0 +1,39 @@ +<?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"> + <!-- TODO: these settings depend on the language. They should be put either in the dictionary + header, or in the subtype maybe? --> + <!-- Symbols that are suggested between words --> + <string name="suggested_punctuations" translatable="false">!,?,\\,,:,;,\",(,),\',-,/,@,_</string> + <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> + <string name="symbols_preceded_by_space" translatable="false">([{&</string> + <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> + <string name="symbols_followed_by_space" translatable="false">.,;:!?)]}&</string> + <!-- Symbols that separate words --> + <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> + <string name="symbols_word_separators" translatable="false">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> + <!-- Word connectors --> + <string name="symbols_word_connectors" translatable="false">\'-</string> + <!-- The sentence separator code point, for capitalization --> + <!-- U+002E: "." FULL STOP ; 2Eh = 46d --> + <integer name="sentence_separator" translatable="false">46</integer> + <!-- Whether this language uses spaces between words --> + <bool name="current_language_has_spaces">true</bool> +</resources> diff --git a/java/res/layout/key_preview_ics.xml b/java/res/values/config-spellchecker-thresholds.xml index 33b6947ef..e99ba6621 100644 --- a/java/res/layout/key_preview_ics.xml +++ b/java/res/values/config-spellchecker-thresholds.xml @@ -18,10 +18,8 @@ */ --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@drawable/keyboard_key_feedback_ics" - android:minWidth="32dp" - android:gravity="center" -/> +<resources> + <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare + a word to be "recommended" --> + <string name="spellchecker_recommended_threshold_value" translatable="false">0.11</string> +</resources> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 61779d4b5..698efa923 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -18,120 +18,92 @@ */ --> +<!-- Configuration values for Small Phone Portrait. --> <resources> <bool name="config_use_fullscreen_mode">false</bool> <bool name="config_enable_show_voice_key_option">true</bool> - <bool name="config_enable_show_option_of_key_preview_popup">true</bool> - <!-- TODO: Disable the following configuration for production. --> - <bool name="config_enable_usability_study_mode_option">true</bool> + <bool name="config_enable_show_key_preview_popup_option">true</bool> <!-- Whether or not Popup on key press is enabled by default --> <bool name="config_default_key_preview_popup">true</bool> - <!-- Default value for next word prediction: after entering a word and a space only, should we look - at input history to suggest a hopefully helpful suggestions for the next word? --> - <bool name="config_default_next_word_prediction">true</bool> <bool name="config_default_sound_enabled">false</bool> - <bool name="config_default_vibration_enabled">true</bool> - <integer name="config_max_vibration_duration">100</integer> <!-- milliseconds --> - <integer name="config_delay_update_suggestions">100</integer> - <integer name="config_delay_update_old_suggestions">300</integer> - <integer name="config_delay_update_shift_state">100</integer> - <integer name="config_language_on_spacebar_final_alpha">128</integer> - <integer name="config_more_keys_keyboard_fadein_anim_time">0</integer> - <integer name="config_more_keys_keyboard_fadeout_anim_time">100</integer> - <integer name="config_keyboard_grid_width">32</integer> - <integer name="config_keyboard_grid_height">16</integer> - <integer name="config_double_space_period_timeout">1100</integer> - <!-- This configuration is an index of {@link KeyboardSwitcher#KEYBOARD_THEMES[]}. --> - <string name="config_default_keyboard_theme_index" translatable="false">2</string> - <integer name="config_max_more_keys_column">5</integer> - <!-- - Configuration for MainKeyboardView - --> <dimen name="config_key_hysteresis_distance">8.0dp</dimen> - <dimen name="config_key_hysteresis_distance_for_sliding_modifier">8.0dp</dimen> - <integer name="config_touch_noise_threshold_time">40</integer> - <dimen name="config_touch_noise_threshold_distance">12.6dp</dimen> - <integer name="config_key_preview_linger_timeout">70</integer> - <bool name="config_sliding_key_input_enabled">true</bool> - <!-- Sliding key input preview parameters --> - <dimen name="config_sliding_key_input_preview_width">8.0dp</dimen> - <!-- Percentages of sliding key input preview body and shadow, in proportion to the width. - A negative value of the shadow ratio disables drawing shadow. --> - <!-- TODO: May use the shadow to alleviate rugged trail drawing. --> - <integer name="config_sliding_key_input_preview_body_ratio">100</integer> - <integer name="config_sliding_key_input_preview_shadow_ratio">-1</integer> - <integer name="config_key_repeat_start_timeout">400</integer> - <integer name="config_key_repeat_interval">50</integer> - <integer name="config_default_longpress_key_timeout">300</integer> <!-- milliseconds --> - <integer name="config_longpress_timeout_step">10</integer> <!-- milliseconds --> - <integer name="config_min_longpress_timeout">100</integer> <!-- milliseconds --> - <integer name="config_max_longpress_timeout">700</integer> <!-- milliseconds --> - <!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 --> - <integer name="config_longpress_shift_lock_timeout">1200</integer> <!-- milliseconds --> - <integer name="config_ignore_alt_code_key_timeout">350</integer> <!-- milliseconds --> + <bool name="config_key_selection_by_dragging_finger">true</bool> <!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if false --> <bool name="config_show_more_keys_keyboard_at_touched_point">false</bool> - <bool name="config_block_potentially_offensive">true</bool> - <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer> - <integer name="config_gesture_trail_fadeout_start_delay">100</integer> - <integer name="config_gesture_trail_fadeout_duration">800</integer> - <integer name="config_gesture_trail_update_interval">20</integer> - <!-- Static threshold for gesture after fast typing (msec) --> - <integer name="config_gesture_static_time_threshold_after_fast_typing">500</integer> - <!-- Static threshold for starting gesture detection (keyWidth%/sec) --> - <fraction name="config_gesture_detect_fast_move_speed_threshold">150%</fraction> - <!-- Dynamic threshold for gesture after fast typing (msec) --> - <integer name="config_gesture_dynamic_threshold_decay_duration">450</integer> - <!-- Time based threshold values for gesture detection (msec) --> - <integer name="config_gesture_dynamic_time_threshold_from">300</integer> - <integer name="config_gesture_dynamic_time_threshold_to">20</integer> - <!-- Distance based threshold values for gesture detection (keyWidth%/sec) --> - <fraction name="config_gesture_dynamic_distance_threshold_from">600%</fraction> - <fraction name="config_gesture_dynamic_distance_threshold_to">50%</fraction> - <!-- Parameter for gesture sampling (keyWidth%/sec) --> - <fraction name="config_gesture_sampling_minimum_distance">16.6666%</fraction> - <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) --> - <integer name="config_gesture_recognition_minimum_time">100</integer> - <integer name="config_gesture_recognition_update_time">100</integer> - <fraction name="config_gesture_recognition_speed_threshold">550%</fraction> - <!-- Suppress showing key preview duration after batch input in millisecond --> - <integer name="config_suppress_key_preview_after_batch_input_duration">1000</integer> - <!-- - Configuration for auto correction - --> - <string-array name="auto_correction_threshold_values" translatable="false"> - <!-- Off, When auto correction setting is Off, this value is not used. --> - <item>floatMaxValue</item> - <!-- Modest : Suggestion whose normalized score is greater than this value - will be subject to auto-correction. --> - <item>0.185</item> - <!-- Aggressive --> - <item>0.067</item> - <!-- Very Aggressive : Suggestion whose normalized score is greater than this value - will be subject to auto-correction. "floatNegativeInfinity" is a special marker - string for Float.NEGATIVE_INFINITY --> - <item>floatNegativeInfinity</item> - </string-array> - <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare - a word to be "recommended" --> - <string name="spellchecker_recommended_threshold_value" translatable="false">0.11</string> - <!-- Screen metrics for logging. - 0 = "mdpi phone screen" - 1 = "hdpi phone screen" - 2 = "mdpi 11 inch tablet screen" - 3 = "xhdpi phone screen?" - 4 = ? - --> - <integer name="log_screen_metrics">0</integer> - <!-- Settings for the dictionary pack --> - <bool name="allow_over_metered">false</bool> - <bool name="allow_over_roaming">false</bool> - <bool name="dict_downloads_visible_in_download_UI">false</bool> - <bool name="metadata_downloads_visible_in_download_UI">false</bool> - <bool name="display_notification_for_auto_update">false</bool> - <bool name="display_notification_for_user_requested_update">false</bool> + <!-- Preferable keyboard height in absolute scale: 1.285in --> + <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> + <dimen name="config_default_keyboard_height">205.6dp</dimen> + <fraction name="config_max_keyboard_height">46%p</fraction> + <fraction name="config_min_keyboard_height">-61.8%p</fraction> + <dimen name="config_more_keys_keyboard_key_height">52.8dp</dimen> + + <dimen name="config_more_keys_keyboard_key_horizontal_padding">8dp</dimen> + + <fraction name="config_keyboard_top_padding_gb">1.556%p</fraction> + <fraction name="config_keyboard_bottom_padding_gb">4.669%p</fraction> + <fraction name="config_key_vertical_gap_gb">6.495%p</fraction> + <fraction name="config_key_horizontal_gap_gb">1.971%p</fraction> + + <fraction name="config_keyboard_top_padding_holo">2.335%p</fraction> + <fraction name="config_keyboard_bottom_padding_holo">4.669%p</fraction> + <fraction name="config_key_vertical_gap_holo">6.127%p</fraction> + <fraction name="config_key_horizontal_gap_holo">1.739%p</fraction> + + <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> + <!-- config_more_keys_keyboard_key_height x 1.2 --> + <dimen name="config_more_keys_keyboard_slide_allowance">63.36dp</dimen> + <!-- config_more_keys_keyboard_key_height x -1.0 --> + <dimen name="config_more_keys_keyboard_vertical_correction_gb">-52.8dp</dimen> + + <fraction name="config_key_letter_ratio">55%</fraction> + <fraction name="config_key_large_letter_ratio">65%</fraction> + <fraction name="config_key_label_ratio">34%</fraction> + <fraction name="config_key_large_label_ratio">40%</fraction> + <fraction name="config_key_hint_letter_ratio">25%</fraction> + <fraction name="config_key_hint_label_ratio">44%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio">35%</fraction> + <fraction name="config_key_preview_text_ratio">82%</fraction> + <fraction name="config_language_on_spacebar_text_ratio">33.735%</fraction> + <dimen name="config_key_preview_height">80dp</dimen> + <dimen name="config_key_preview_offset_gb">-8.0dp</dimen> + + <dimen name="config_key_label_horizontal_padding">4dp</dimen> + <dimen name="config_key_hint_letter_padding">1dp</dimen> + <dimen name="config_key_shifted_letter_hint_padding">2dp</dimen> + + <!-- For 5-row keyboard --> + <fraction name="config_key_vertical_gap_5row">3.20%p</fraction> + <fraction name="config_key_letter_ratio_5row">64%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_5row">41%</fraction> + + <dimen name="config_key_preview_offset_holo">8.0dp</dimen> + <!-- config_more_keys_keyboard_key_height x -0.5 --> + <dimen name="config_more_keys_keyboard_vertical_correction_holo">-26.4dp</dimen> + + <dimen name="config_suggestions_strip_height">40dp</dimen> + <dimen name="config_more_suggestions_row_height">40dp</dimen> + <integer name="config_max_more_suggestions_row">6</integer> + <fraction name="config_min_more_suggestions_width">90%</fraction> + <dimen name="config_suggestions_strip_horizontal_padding">0dp</dimen> + <dimen name="config_suggestion_min_width">44dp</dimen> + <dimen name="config_suggestion_text_horizontal_padding">6dp</dimen> + <dimen name="config_suggestion_text_size">18dp</dimen> + <dimen name="config_more_suggestions_hint_text_size">27dp</dimen> + + <!-- Gesture floating preview text parameters --> + <dimen name="config_gesture_floating_preview_text_size">24dp</dimen> + <dimen name="config_gesture_floating_preview_text_offset">73dp</dimen> + <dimen name="config_gesture_floating_preview_horizontal_padding">24dp</dimen> + <dimen name="config_gesture_floating_preview_vertical_padding">16dp</dimen> + <dimen name="config_gesture_floating_preview_round_radius">2dp</dimen> + + <!-- Emoji keyboard --> + <fraction name="config_emoji_keyboard_key_width">14.2857%p</fraction> + <fraction name="config_emoji_keyboard_row_height">33%p</fraction> + <fraction name="config_emoji_keyboard_key_letter_size">68%p</fraction> + <integer name="config_emoji_keyboard_max_page_key_count">21</integer> </resources> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml deleted file mode 100644 index 4588b10eb..000000000 --- a/java/res/values/dimens.xml +++ /dev/null @@ -1,132 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2008, 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> - <!-- Preferable keyboard height in absolute scale: 1.285in --> - <!-- This keyboardHeight value should match with keyboard-heights.xml --> - <dimen name="keyboardHeight">205.6dp</dimen> - <fraction name="maxKeyboardHeight">46%p</fraction> - <fraction name="minKeyboardHeight">-61.8%p</fraction> - - <dimen name="popup_key_height">52.8dp</dimen> - - <dimen name="more_keys_keyboard_key_horizontal_padding">8dp</dimen> - - <fraction name="keyboard_left_padding">0%p</fraction> - <fraction name="keyboard_right_padding">0%p</fraction> - - <fraction name="keyboard_top_padding_gb">1.556%p</fraction> - <fraction name="keyboard_bottom_padding_gb">4.669%p</fraction> - <fraction name="key_bottom_gap_gb">6.495%p</fraction> - <fraction name="key_horizontal_gap_gb">1.971%p</fraction> - - <fraction name="keyboard_top_padding_holo">2.335%p</fraction> - <fraction name="keyboard_bottom_padding_holo">4.669%p</fraction> - <fraction name="key_bottom_gap_holo">6.127%p</fraction> - <fraction name="key_horizontal_gap_holo">1.739%p</fraction> - - <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. --> - <!-- popup_key_height x 1.2 --> - <dimen name="more_keys_keyboard_slide_allowance">63.36dp</dimen> - <!-- popup_key_height x -1.0 --> - <dimen name="more_keys_keyboard_vertical_correction_gb">-52.8dp</dimen> - <dimen name="keyboard_vertical_correction">0.0dp</dimen> - - <fraction name="key_letter_ratio">55%</fraction> - <fraction name="key_large_letter_ratio">65%</fraction> - <fraction name="key_label_ratio">34%</fraction> - <fraction name="key_large_label_ratio">40%</fraction> - <fraction name="key_hint_letter_ratio">25%</fraction> - <fraction name="key_hint_label_ratio">44%</fraction> - <fraction name="key_uppercase_letter_ratio">35%</fraction> - <fraction name="key_preview_text_ratio">82%</fraction> - <fraction name="spacebar_text_ratio">33.735%</fraction> - <dimen name="key_preview_height">80dp</dimen> - <dimen name="key_preview_offset_gb">-8.0dp</dimen> - - <dimen name="key_label_horizontal_padding">4dp</dimen> - <dimen name="key_hint_letter_padding">1dp</dimen> - <dimen name="key_popup_hint_letter_padding">2dp</dimen> - <dimen name="key_uppercase_letter_padding">2dp</dimen> - - <!-- For 5-row keyboard --> - <fraction name="key_bottom_gap_5row">3.20%p</fraction> - <fraction name="key_letter_ratio_5row">64%</fraction> - <fraction name="key_uppercase_letter_ratio_5row">41%</fraction> - - <dimen name="key_preview_offset_holo">8.0dp</dimen> - <!-- popup_key_height x -0.5 --> - <dimen name="more_keys_keyboard_vertical_correction_holo">-26.4dp</dimen> - - <dimen name="suggestions_strip_height">40dp</dimen> - <dimen name="more_suggestions_key_horizontal_padding">12dp</dimen> - <dimen name="more_suggestions_row_height">40dp</dimen> - <dimen name="more_suggestions_bottom_gap">6dp</dimen> - <dimen name="more_suggestions_modal_tolerance">32.0dp</dimen> - <dimen name="more_suggestions_slide_allowance">16.0dp</dimen> - <integer name="max_more_suggestions_row">6</integer> - <fraction name="min_more_suggestions_width">90%</fraction> - <fraction name="more_suggestions_info_ratio">18%</fraction> - <dimen name="suggestions_strip_padding">0dp</dimen> - <dimen name="suggestion_min_width">44dp</dimen> - <dimen name="suggestion_padding">6dp</dimen> - <dimen name="suggestion_text_size">18dp</dimen> - <dimen name="more_suggestions_hint_text_size">27dp</dimen> - <integer name="suggestions_count_in_strip">3</integer> - <fraction name="center_suggestion_percentile">36%</fraction> - - <!-- Gesture trail parameters --> - <!-- Minimum distance between gesture trail sampling points. --> - <dimen name="gesture_trail_min_sampling_distance">9.6dp</dimen> - <!-- Maximum angular threshold between gesture trails interpolation segments in degree. --> - <integer name="gesture_trail_max_interpolation_angular_threshold">15</integer> - <!-- Maximum distance threshold between gesture trails interpolation segments. --> - <dimen name="gesture_trail_max_interpolation_distance_threshold">16.0dp</dimen> - <!-- Maximum number of gesture trail interpolation segments. --> - <integer name="gesture_trail_max_interpolation_segments">6</integer> - <dimen name="gesture_trail_start_width">10.0dp</dimen> - <dimen name="gesture_trail_end_width">2.5dp</dimen> - <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width. - A negative value of the shadow ratio disables drawing shadow. --> - <!-- TODO: May use the shadow to alleviate rugged trail drawing. --> - <integer name="gesture_trail_body_ratio">100</integer> - <integer name="gesture_trail_shadow_ratio">-1</integer> - <!-- Gesture floating preview text parameters --> - <dimen name="gesture_floating_preview_text_size">24dp</dimen> - <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">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">68%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> - - <integer name="user_dictionary_max_word_length" translatable="false">48</integer> - - <dimen name="language_on_spacebar_horizontal_margin">1dp</dimen> - -</resources> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index af5ec061b..4be5863f2 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -18,25 +18,6 @@ */ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- TODO: these settings depend on the language. They should be put either in the dictionary - header, or in the subtype maybe? --> - <!-- Symbols that are suggested between words --> - <string name="suggested_punctuations">!,?,\\,,:,;,\",(,),\',-,/,@,_</string> - <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) --> - <string name="symbols_preceded_by_space">([{&</string> - <!-- Symbols that are normally followed by a space (used to add an auto-space after these) --> - <string name="symbols_followed_by_space">.,;:!?)]}&</string> - <!-- Symbols that separate words --> - <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) --> - <string name="symbols_word_separators">"	 \n"()[]{}*&<>+=|.,;:!?/_\"</string> - <!-- Word connectors --> - <string name="symbols_word_connectors">\'-</string> - <!-- The sentence separator code point, for capitalization --> - <!-- U+002E: "." FULL STOP ; 2Eh = 46d --> - <integer name="sentence_separator">46</integer> - <!-- Whether this language uses spaces between words --> - <bool name="current_language_has_spaces">true</bool> - <!-- Always show the suggestion strip --> <string name="prefs_suggestion_visibility_show_value">0</string> <!-- Show the suggestion strip only on portrait mode --> @@ -57,43 +38,9 @@ <item>@string/prefs_suggestion_visibility_hide_name</item> </string-array> - <string name="auto_correction_threshold_mode_index_off">0</string> - <string name="auto_correction_threshold_mode_index_modest">1</string> - <string name="auto_correction_threshold_mode_index_aggressive">2</string> - <string name="auto_correction_threshold_mode_index_very_aggressive">3</string> - <string-array name="auto_correction_threshold_mode_indexes"> - <item>@string/auto_correction_threshold_mode_index_off</item> - <item>@string/auto_correction_threshold_mode_index_modest</item> - <item>@string/auto_correction_threshold_mode_index_aggressive</item> - <item>@string/auto_correction_threshold_mode_index_very_aggressive</item> - </string-array> - <string-array name="auto_correction_threshold_modes"> - <item>@string/auto_correction_threshold_mode_off</item> - <item>@string/auto_correction_threshold_mode_modest</item> - <item>@string/auto_correction_threshold_mode_aggressive</item> - <item>@string/auto_correction_threshold_mode_very_aggressive</item> - </string-array> - + <!-- For backward compatibility. + See {@link SettingsValues#needsToShowVoiceInputKey(SharedPreferences,Resources)} --> <string name="voice_mode_main">0</string> - <string name="voice_mode_symbols">1</string> - <string name="voice_mode_off">2</string> - <string-array name="voice_input_modes_values"> - <item>@string/voice_mode_main</item> - <item>@string/voice_mode_symbols</item> - <item>@string/voice_mode_off</item> - </string-array> - <!-- Array of Voice Input modes --> - <string-array name="voice_input_modes"> - <item>@string/voice_input_modes_main_keyboard</item> - <item>@string/voice_input_modes_symbols_keyboard</item> - <item>@string/voice_input_modes_off</item> - </string-array> - <!-- Array of Voice Input modes summary --> - <string-array name="voice_input_modes_summary"> - <item>@string/voice_input_modes_summary_main_keyboard</item> - <item>@string/voice_input_modes_summary_symbols_keyboard</item> - <item>@string/voice_input_modes_summary_off</item> - </string-array> <!-- Title for Latin keyboard debug settings activity / dialog --> <string name="english_ime_debug_settings">Android keyboard Debug settings</string> diff --git a/java/res/values/keyboard-icons-holo.xml b/java/res/values/keyboard-icons-holo.xml index b49e1d10b..4c888d570 100644 --- a/java/res/values/keyboard-icons-holo.xml +++ b/java/res/values/keyboard-icons-holo.xml @@ -32,7 +32,6 @@ <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> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 11b3ea3af..a269d4979 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -245,21 +245,6 @@ <!-- Preferences item for enabling speech input --> <string name="voice_input">Voice input key</string> - <!-- Voice Input modes --> - <!-- On settings screen, voice input pop-up menu option to show voice key on main keyboard [CHAR LIMIT=20] --> - <string name="voice_input_modes_main_keyboard">On main keyboard</string> - <!-- On settings screen, voice input pop-up menu option to show voice key on symbols keyboard [CHAR LIMIT=20] --> - <string name="voice_input_modes_symbols_keyboard">On symbols keyboard</string> - <!-- On settings screen, voice input pop-up menu option to never show voice key [CHAR LIMIT=20] --> - <string name="voice_input_modes_off">Off</string> - <!-- Voice Input modes summary --> - <!-- On settings screen, voice input pop-up menu summary text to show voice key on main keyboard [CHAR LIMIT=20] --> - <string name="voice_input_modes_summary_main_keyboard">Mic on main keyboard</string> - <!-- On settings screen, voice input pop-up menu summary text to show voice key on symbols keyboard [CHAR LIMIT=20] --> - <string name="voice_input_modes_summary_symbols_keyboard">Mic on symbols keyboard</string> - <!-- On settings screen, voice input pop-up menu summary text to never show voice key [CHAR LIMIT=20] --> - <string name="voice_input_modes_summary_off">Voice input is disabled</string> - <!-- Title for configuring input method settings [CHAR LIMIT=35] --> <string name="configure_input_method">Configure input methods</string> diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml index 298936d9c..7c9b51c0a 100644 --- a/java/res/values/themes-common.xml +++ b/java/res/values/themes-common.xml @@ -25,49 +25,49 @@ <item name="touchPositionCorrectionData">@array/touch_position_correction_data_default</item> <item name="rowHeight">25%p</item> <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item> - <item name="keyboardLeftPadding">@fraction/keyboard_left_padding</item> - <item name="keyboardRightPadding">@fraction/keyboard_right_padding</item> + <item name="keyboardLeftPadding">@fraction/config_keyboard_left_padding</item> + <item name="keyboardRightPadding">@fraction/config_keyboard_right_padding</item> <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item> </style> <style name="KeyboardView"> <item name="keyBackground">@drawable/btn_keyboard_key_klp</item> - <item name="keyLetterSize">@fraction/key_letter_ratio</item> - <item name="keyLargeLetterRatio">@fraction/key_large_letter_ratio</item> - <item name="keyLabelSize">@fraction/key_label_ratio</item> - <item name="keyLargeLabelRatio">@fraction/key_large_label_ratio</item> - <item name="keyHintLetterRatio">@fraction/key_hint_letter_ratio</item> - <item name="keyHintLabelRatio">@fraction/key_hint_label_ratio</item> - <item name="keyShiftedLetterHintRatio">@fraction/key_uppercase_letter_ratio</item> + <item name="keyLetterSize">@fraction/config_key_letter_ratio</item> + <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio</item> + <item name="keyLabelSize">@fraction/config_key_label_ratio</item> + <item name="keyLargeLabelRatio">@fraction/config_key_large_label_ratio</item> + <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio</item> + <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio</item> + <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio</item> <item name="keyTypeface">normal</item> - <item name="keyLabelHorizontalPadding">@dimen/key_label_horizontal_padding</item> - <item name="keyHintLetterPadding">@dimen/key_hint_letter_padding</item> - <item name="keyPopupHintLetterPadding">@dimen/key_popup_hint_letter_padding</item> - <item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item> - <item name="keyPreviewTextRatio">@fraction/key_preview_text_ratio</item> - <item name="verticalCorrection">@dimen/keyboard_vertical_correction</item> + <item name="keyLabelHorizontalPadding">@dimen/config_key_label_horizontal_padding</item> + <item name="keyHintLetterPadding">@dimen/config_key_hint_letter_padding</item> + <item name="keyPopupHintLetterPadding">@dimen/config_key_popup_hint_letter_padding</item> + <item name="keyShiftedLetterHintPadding">@dimen/config_key_shifted_letter_hint_padding</item> + <item name="keyPreviewTextRatio">@fraction/config_key_preview_text_ratio</item> + <item name="verticalCorrection">@dimen/config_keyboard_vertical_correction</item> <item name="backgroundDimAlpha">128</item> - <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item> - <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item> - <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item> - <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item> - <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item> - <item name="gestureTrailMinSamplingDistance">@dimen/gesture_trail_min_sampling_distance</item> - <item name="gestureTrailMaxInterpolationAngularThreshold">@integer/gesture_trail_max_interpolation_angular_threshold</item> - <item name="gestureTrailMaxInterpolationDistanceThreshold">@dimen/gesture_trail_max_interpolation_distance_threshold</item> - <item name="gestureTrailMaxInterpolationSegments">@integer/gesture_trail_max_interpolation_segments</item> + <item name="gestureFloatingPreviewTextSize">@dimen/config_gesture_floating_preview_text_size</item> + <item name="gestureFloatingPreviewTextOffset">@dimen/config_gesture_floating_preview_text_offset</item> + <item name="gestureFloatingPreviewHorizontalPadding">@dimen/config_gesture_floating_preview_horizontal_padding</item> + <item name="gestureFloatingPreviewVerticalPadding">@dimen/config_gesture_floating_preview_vertical_padding</item> + <item name="gestureFloatingPreviewRoundRadius">@dimen/config_gesture_floating_preview_round_radius</item> + <item name="gestureTrailMinSamplingDistance">@dimen/config_gesture_trail_min_sampling_distance</item> + <item name="gestureTrailMaxInterpolationAngularThreshold">@integer/config_gesture_trail_max_interpolation_angular_threshold</item> + <item name="gestureTrailMaxInterpolationDistanceThreshold">@dimen/config_gesture_trail_max_interpolation_distance_threshold</item> + <item name="gestureTrailMaxInterpolationSegments">@integer/config_gesture_trail_max_interpolation_segments</item> <item name="gestureTrailFadeoutStartDelay">@integer/config_gesture_trail_fadeout_start_delay</item> <item name="gestureTrailFadeoutDuration">@integer/config_gesture_trail_fadeout_duration</item> <item name="gestureTrailUpdateInterval">@integer/config_gesture_trail_update_interval</item> - <item name="gestureTrailStartWidth">@dimen/gesture_trail_start_width</item> - <item name="gestureTrailEndWidth">@dimen/gesture_trail_end_width</item> - <item name="gestureTrailBodyRatio">@integer/gesture_trail_body_ratio</item> - <item name="gestureTrailShadowRatio">@integer/gesture_trail_shadow_ratio</item> + <item name="gestureTrailStartWidth">@dimen/config_gesture_trail_start_width</item> + <item name="gestureTrailEndWidth">@dimen/config_gesture_trail_end_width</item> + <item name="gestureTrailBodyRatio">@integer/config_gesture_trail_body_ratio</item> + <item name="gestureTrailShadowRatio">@integer/config_gesture_trail_shadow_ratio</item> <!-- Common attributes of MainKeyboardView --> <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item> <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item> <item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item> <item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item> - <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item> + <item name="keySelectionByDraggingFinger">@bool/config_key_selection_by_dragging_finger</item> <item name="slidingKeyInputPreviewWidth">@dimen/config_sliding_key_input_preview_width</item> <item name="slidingKeyInputPreviewBodyRatio">@integer/config_sliding_key_input_preview_body_ratio</item> <item name="slidingKeyInputPreviewShadowRatio">@integer/config_sliding_key_input_preview_shadow_ratio</item> @@ -75,11 +75,14 @@ <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item> <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item> <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item> - <item name="keyPreviewHeight">@dimen/key_preview_height</item> + <item name="keyPreviewLayout">@layout/key_preview</item> + <item name="keyPreviewHeight">@dimen/config_key_preview_height</item> + <item name="keyPreviewZoomInDuration">@integer/config_key_preview_zoom_in_duration</item> + <item name="keyPreviewZoomOutDuration">@integer/config_key_preview_zoom_out_duration</item> <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item> <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item> <item name="showMoreKeysKeyboardAtTouchedPoint">@bool/config_show_more_keys_keyboard_at_touched_point</item> - <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item> + <item name="languageOnSpacebarTextRatio">@fraction/config_language_on_spacebar_text_ratio</item> <item name="languageOnSpacebarFinalAlpha">@integer/config_language_on_spacebar_final_alpha</item> <item name="languageOnSpacebarFadeoutAnimator">@anim/language_on_spacebar_fadeout</item> <!-- Remove animations for now because it could drain a non-negligible amount of battery while typing. @@ -104,6 +107,7 @@ <style name="MainKeyboardView" parent="KeyboardView" /> + <style name="KeyPreviewTextView" /> <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, for instance delete button, need themed {@link KeyboardView} attributes. --> <style @@ -118,14 +122,14 @@ parent="MainKeyboardView" /> <style name="MoreKeysKeyboardContainer" /> <style name="SuggestionStripView"> - <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item> - <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item> - <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item> - <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item> + <item name="suggestionsCountInStrip">@integer/config_suggestions_count_in_strip</item> + <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> + <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> + <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> </style> <style name="SuggestionWord" /> <style name="MoreKeysKeyboardAnimation"> <item name="android:windowEnterAnimation">@anim/more_keys_keyboard_fadein</item> <item name="android:windowExitAnimation">@anim/more_keys_keyboard_fadeout</item> </style> -</resources>
\ No newline at end of file +</resources> diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml index f52695f55..52ecafd62 100644 --- a/java/res/values/themes-gb.xml +++ b/java/res/values/themes-gb.xml @@ -23,10 +23,10 @@ <item name="keyboardStyle">@style/Keyboard.GB</item> <item name="keyboardViewStyle">@style/KeyboardView.GB</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.GB</item> + <item name="keyPreviewTextViewStyle">@style/KeyPreviewTextView.GB</item> <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.GB</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.GB</item> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.GB</item> - <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.GB</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.GB</item> <item name="suggestionWordStyle">@style/SuggestionWord.GB</item> </style> @@ -40,7 +40,6 @@ <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_holo_dark</item> <!-- TODO: Needs non-holo disabled shortcut icon drawable --> @@ -59,10 +58,10 @@ <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> <item name="themeId">1</item> <item name="touchPositionCorrectionData">@array/touch_position_correction_data_gb</item> - <item name="keyboardTopPadding">@fraction/keyboard_top_padding_gb</item> - <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_gb</item> - <item name="horizontalGap">@fraction/key_horizontal_gap_gb</item> - <item name="verticalGap">@fraction/key_bottom_gap_gb</item> + <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_gb</item> + <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_gb</item> + <item name="horizontalGap">@fraction/config_key_horizontal_gap_gb</item> + <item name="verticalGap">@fraction/config_key_vertical_gap_gb</item> </style> <style name="KeyboardView.GB" @@ -85,16 +84,22 @@ name="MainKeyboardView.GB" parent="KeyboardView.GB" > - <item name="keyPreviewLayout">@layout/key_preview_gb</item> - <item name="keyPreviewOffset">@dimen/key_preview_offset_gb</item> + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_gb</item> <item name="gestureFloatingPreviewTextColor">@color/highlight_color_gb</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_gb</item> <item name="gestureTrailColor">@color/highlight_color_gb</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_gb</item> <item name="autoCorrectionSpacebarLedEnabled">true</item> <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_gb</item> - <item name="spacebarTextColor">@color/spacebar_text_color_gb</item> - <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_gb</item> + <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_gb</item> + <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_gb</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_gb</item> + </style> + <style + name="KeyPreviewTextView.GB" + parent="KeyPreviewTextView" + > + <item name="android:background">@drawable/keyboard_key_feedback_gb</item> </style> <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, for instance delete button, need themed {@link KeyboardView} attributes. --> @@ -118,15 +123,10 @@ name="MoreKeysKeyboardView.GB" parent="KeyboardView.GB" > - <item name="android:background">@null</item> + <item name="android:background">@drawable/keyboard_popup_panel_background_gb</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_gb</item> <item name="keyTypeface">normal</item> - <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_gb</item> - </style> - <style - name="MoreKeysKeyboardContainer.GB" - > - <item name="android:background">@drawable/keyboard_popup_panel_background_gb</item> + <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_gb</item> </style> <style name="SuggestionStripView.GB" diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 432ad5122..125b640b6 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -23,10 +23,10 @@ <item name="keyboardStyle">@style/Keyboard.ICS</item> <item name="keyboardViewStyle">@style/KeyboardView.ICS</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item> + <item name="keyPreviewTextViewStyle">@style/KeyPreviewTextView.ICS</item> <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.ICS</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item> - <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.ICS</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.ICS</item> <item name="suggestionWordStyle">@style/SuggestionWord.ICS</item> </style> @@ -36,10 +36,10 @@ > <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> <item name="themeId">2</item> - <item name="keyboardTopPadding">@fraction/keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/key_bottom_gap_holo</item> + <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> + <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> + <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> + <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> </style> <style @@ -63,16 +63,22 @@ name="MainKeyboardView.ICS" parent="KeyboardView.ICS" > - <item name="keyPreviewLayout">@layout/key_preview_ics</item> - <item name="keyPreviewOffset">@dimen/key_preview_offset_holo</item> + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_ics</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item> <item name="autoCorrectionSpacebarLedEnabled">false</item> <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> - <item name="spacebarTextColor">@color/spacebar_text_color_holo</item> - <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> + <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item> + <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_ics</item> + </style> + <style + name="KeyPreviewTextView.ICS" + parent="KeyPreviewTextView" + > + <item name="android:background">@drawable/keyboard_key_feedback_ics</item> </style> <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, for instance delete button, need themed {@link KeyboardView} attributes. --> @@ -96,15 +102,10 @@ name="MoreKeysKeyboardView.ICS" parent="KeyboardView.ICS" > - <item name="android:background">@null</item> + <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item> <item name="keyTypeface">normal</item> - <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_holo</item> - </style> - <style - name="MoreKeysKeyboardContainer.ICS" - > - <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item> + <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> <style name="SuggestionStripView.ICS" diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml index a3730019d..f6c076767 100644 --- a/java/res/values/themes-klp.xml +++ b/java/res/values/themes-klp.xml @@ -23,10 +23,10 @@ <item name="keyboardStyle">@style/Keyboard.KLP</item> <item name="keyboardViewStyle">@style/KeyboardView.KLP</item> <item name="mainKeyboardViewStyle">@style/MainKeyboardView.KLP</item> + <item name="keyPreviewTextViewStyle">@style/KeyPreviewTextView.KLP</item> <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.KLP</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.KLP</item> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.KLP</item> - <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.KLP</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.KLP</item> <item name="suggestionWordStyle">@style/SuggestionWord.KLP</item> </style> @@ -36,10 +36,10 @@ > <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> <item name="themeId">0</item> - <item name="keyboardTopPadding">@fraction/keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/key_bottom_gap_holo</item> + <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> + <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> + <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> + <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> </style> <style @@ -63,16 +63,22 @@ name="MainKeyboardView.KLP" parent="KeyboardView.KLP" > - <item name="keyPreviewLayout">@layout/key_preview_klp</item> - <item name="keyPreviewOffset">@dimen/key_preview_offset_holo</item> + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> <item name="gestureFloatingPreviewTextColor">@color/highlight_color_klp</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_klp</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_klp</item> <item name="autoCorrectionSpacebarLedEnabled">false</item> <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item> - <item name="spacebarTextColor">@color/spacebar_text_color_holo</item> - <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> + <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item> + <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item> + <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_klp</item> + </style> + <style + name="KeyPreviewTextView.KLP" + parent="KeyPreviewTextView" + > + <item name="android:background">@drawable/keyboard_key_feedback_klp</item> </style> <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it, for instance delete button, need themed {@link KeyboardView} attributes. --> @@ -96,15 +102,10 @@ name="MoreKeysKeyboardView.KLP" parent="KeyboardView.KLP" > - <item name="android:background">@null</item> + <item name="android:background">@drawable/keyboard_popup_panel_background_klp</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item> <item name="keyTypeface">normal</item> - <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_holo</item> - </style> - <style - name="MoreKeysKeyboardContainer.KLP" - > - <item name="android:background">@drawable/keyboard_popup_panel_background_klp</item> + <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> <style name="SuggestionStripView.KLP" diff --git a/java/res/xml-sw600dp-land/kbd_more_keys_keyboard_template.xml b/java/res/xml-sw600dp-land/kbd_more_keys_keyboard_template.xml index 4d8b446a2..c7d446014 100644 --- a/java/res/xml-sw600dp-land/kbd_more_keys_keyboard_template.xml +++ b/java/res/xml-sw600dp-land/kbd_more_keys_keyboard_template.xml @@ -20,7 +20,7 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" latin:keyWidth="5%p" - latin:rowHeight="@dimen/popup_key_height" + latin:rowHeight="@dimen/config_more_keys_keyboard_key_height" style="?attr/moreKeysKeyboardStyle" > </Keyboard> diff --git a/java/res/xml-sw600dp/kbd_more_keys_keyboard_template.xml b/java/res/xml-sw600dp/kbd_more_keys_keyboard_template.xml index d90a5884e..fbe8cfcc0 100644 --- a/java/res/xml-sw600dp/kbd_more_keys_keyboard_template.xml +++ b/java/res/xml-sw600dp/kbd_more_keys_keyboard_template.xml @@ -20,7 +20,7 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" latin:keyWidth="8%p" - latin:rowHeight="@dimen/popup_key_height" + latin:rowHeight="@dimen/config_more_keys_keyboard_key_height" style="?attr/moreKeysKeyboardStyle" > </Keyboard> diff --git a/java/res/xml-sw600dp/key_f1.xml b/java/res/xml-sw600dp/key_f1.xml index ac0053236..530f7d63b 100644 --- a/java/res/xml-sw600dp/key_f1.xml +++ b/java/res/xml-sw600dp/key_f1.xml @@ -23,34 +23,11 @@ > <switch> <case - latin:keyboardLayoutSetElement="symbols" - latin:mode="url" - > - <Key - latin:keyLabel=":" /> - </case> - <case - latin:keyboardLayoutSetElement="symbols" - > - <Key - latin:keyLabel="\@" /> - </case> - <!-- keyboardLayoutSetElement != "symbols" --> - <case latin:mode="email" > <Key latin:keyLabel="\@" /> </case> - <case - latin:mode="url" - > - <Key - latin:keyLabel="/" - latin:keyHintLabel=":" - latin:moreKeys=":" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> <default> <Key latin:keyLabel="/" /> diff --git a/java/res/xml-sw600dp/key_shortcut.xml b/java/res/xml-sw600dp/key_shortcut.xml index 87fc75cd5..c869e745b 100644 --- a/java/res/xml-sw600dp/key_shortcut.xml +++ b/java/res/xml-sw600dp/key_shortcut.xml @@ -23,7 +23,7 @@ > <switch> <case - latin:shortcutKeyEnabled="true" + latin:supportsSwitchingToShortcutIme="true" latin:clobberSettingsKey="false" > <Key @@ -32,20 +32,20 @@ latin:moreKeys="!text/settings_as_more_key" /> </case> <case - latin:shortcutKeyEnabled="true" + latin:supportsSwitchingToShortcutIme="true" latin:clobberSettingsKey="true" > <Key latin:keyStyle="shortcutKeyStyle" /> </case> <case - latin:shortcutKeyEnabled="false" + latin:supportsSwitchingToShortcutIme="false" latin:clobberSettingsKey="false" > <Key latin:keyStyle="settingsKeyStyle" /> </case> - <!-- shortcutKeyEnabled="false" clobberSettingsKey="true" --> + <!-- supportsSwitchingToShortcutIme="false" clobberSettingsKey="true" --> <default> <Spacer /> </default> diff --git a/java/res/xml-sw600dp/key_space_symbols.xml b/java/res/xml-sw600dp/key_space_symbols.xml index 07aa7d179..d6f7cab09 100644 --- a/java/res/xml-sw600dp/key_space_symbols.xml +++ b/java/res/xml-sw600dp/key_space_symbols.xml @@ -22,5 +22,6 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <include + latin:backgroundType="normal" latin:keyboardLayout="@xml/key_space_5kw" /> </merge> diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml-sw600dp/keys_comma_period.xml index 7604e033d..55302aeab 100644 --- a/java/res/xml-sw600dp/keys_comma_period.xml +++ b/java/res/xml-sw600dp/keys_comma_period.xml @@ -21,83 +21,18 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <switch> - <case - latin:mode="email|url" - > - <Key - latin:keyLabel="," - latin:keyHintLabel="-" - latin:moreKeys="-" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="_" - latin:moreKeys="_" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> - <case - latin:languageCode="ar" - > - <Key - latin:keyLabel="!text/keylabel_for_apostrophe" - latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" - latin:moreKeys="!text/more_keys_for_apostrophe" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_arabic_diacritics" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> - <case - latin:languageCode="fa" - > - <Key - latin:keyLabel="!text/keylabel_for_apostrophe" - latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_apostrophe" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_arabic_diacritics" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> - <case - latin:languageCode="hy" - > - <!-- U+055D: "՝" ARMENIAN COMMA --> - <Key - latin:keyLabel="՝" - latin:backgroundType="functional" /> - <!-- U+0589: "։" ARMENIAN FULL STOP --> - <Key - latin:keyLabel="։" - latin:keyLabelFlags="hasPopupHint" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_punctuation" /> - </case> - <default> - <Key - latin:keyLabel="!text/keylabel_for_tablet_comma" - latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_tablet_comma" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_period" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_period" /> - </default> - </switch> + <Key + latin:keyLabel="!text/keylabel_for_tablet_comma" + latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma" + latin:keyLabelFlags="hasPopupHint" + latin:moreKeys="!text/more_keys_for_tablet_comma" + latin:backgroundType="functional" + latin:keyStyle="hasShiftedLetterHintStyle" /> + <Key + latin:keyLabel="!text/keylabel_for_tablet_period" + latin:keyHintLabel="!text/keyhintlabel_for_tablet_period" + latin:keyLabelFlags="hasPopupHint" + latin:moreKeys="!text/more_keys_for_tablet_period" + latin:backgroundType="functional" + latin:keyStyle="hasShiftedLetterHintStyle" /> </merge> diff --git a/java/res/xml-sw600dp/keys_exclamation_question.xml b/java/res/xml-sw600dp/keys_exclamation_question.xml index cd38282ee..fd849222d 100644 --- a/java/res/xml-sw600dp/keys_exclamation_question.xml +++ b/java/res/xml-sw600dp/keys_exclamation_question.xml @@ -22,7 +22,9 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <Key - latin:keyLabel="!" /> + latin:keyLabel="!" + latin:moreKeys="!text/more_keys_for_exclamation" /> <Key - latin:keyLabel="\?" /> + latin:keyLabel="\?" + latin:moreKeys="!text/more_keys_for_question" /> </merge> diff --git a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml index 774ff8d05..46a1c85dc 100644 --- a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml +++ b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml @@ -40,7 +40,7 @@ latin:keyHintLabel="\?" latin:additionalMoreKeys="\?" latin:keyStyle="hasShiftedLetterHintStyle" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:moreKeys="!text/more_keys_for_question" /> </case> <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" --> <default> @@ -58,7 +58,7 @@ latin:moreKeys="!fixedColumnOrder!3,›,≥,»" /> <Key latin:keyLabel="\?" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:moreKeys="!text/more_keys_for_question" /> </default> </switch> </merge> diff --git a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml index 254d3fdba..ae6bab79c 100644 --- a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml +++ b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml @@ -31,7 +31,7 @@ latin:keyHintLabel="!" latin:additionalMoreKeys="!" latin:keyStyle="hasShiftedLetterHintStyle" - latin:moreKeys="!text/more_keys_for_symbols_exclamation,!text/more_keys_for_symbols_1" /> + latin:moreKeys="!text/more_keys_for_exclamation,!text/more_keys_for_symbols_1" /> <Key latin:keyLabel="2" latin:keyHintLabel="\@" diff --git a/java/res/xml-sw600dp/rows_hebrew.xml b/java/res/xml-sw600dp/rows_hebrew.xml index 852e17683..86b34202f 100644 --- a/java/res/xml-sw600dp/rows_hebrew.xml +++ b/java/res/xml-sw600dp/rows_hebrew.xml @@ -45,8 +45,10 @@ latin:keyWidth="9.0%p" > <include - latin:keyboardLayout="@xml/rowkeys_hebrew3" - latin:keyXPos="10.0%p" /> + latin:keyboardLayout="@xml/rowkeys_hebrew3" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="9.5%p" /> </Row> <include latin:keyboardLayout="@xml/row_qwerty4" /> diff --git a/java/res/xml-sw600dp/rows_swiss.xml b/java/res/xml-sw600dp/rows_swiss.xml new file mode 100644 index 000000000..4f4ca85b4 --- /dev/null +++ b/java/res/xml-sw600dp/rows_swiss.xml @@ -0,0 +1,63 @@ +<?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.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_swiss1" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_swiss2" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </Row> + <Row + latin:keyWidth="8.182%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <Spacer + latin:keyWidth="3.181%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwertz3" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyXPos="-10.0%p" + latin:keyWidth="fillRight" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml index cf94b06ed..7a33f4923 100644 --- a/java/res/xml-sw600dp/rows_symbols.xml +++ b/java/res/xml-sw600dp/rows_symbols.xml @@ -62,6 +62,7 @@ </Row> <Row latin:keyWidth="9.0%p" + latin:backgroundType="functional" > <Key latin:keyStyle="toAlphaKeyStyle" diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml index 92299f65d..79d1aa142 100644 --- a/java/res/xml-sw600dp/rows_symbols_shift.xml +++ b/java/res/xml-sw600dp/rows_symbols_shift.xml @@ -64,6 +64,7 @@ </Row> <Row latin:keyWidth="9.0%p" + latin:backgroundType="functional" > <Key latin:keyStyle="toAlphaKeyStyle" diff --git a/java/res/xml/kbd_armenian_phonetic.xml b/java/res/xml/kbd_armenian_phonetic.xml index 1eb3c7e7d..da12870ed 100644 --- a/java/res/xml/kbd_armenian_phonetic.xml +++ b/java/res/xml/kbd_armenian_phonetic.xml @@ -21,9 +21,9 @@ <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:verticalGap="@fraction/config_key_vertical_gap_5row" + latin:keyLetterSize="@fraction/config_key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/config_key_shifted_letter_hint_ratio_5row" latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include diff --git a/java/res/xml/kbd_emoji_category1.xml b/java/res/xml/kbd_emoji_category1.xml index c11a83017..5145ea9d3 100644 --- a/java/res/xml/kbd_emoji_category1.xml +++ b/java/res/xml/kbd_emoji_category1.xml @@ -20,9 +20,9 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_faces" diff --git a/java/res/xml/kbd_emoji_category2.xml b/java/res/xml/kbd_emoji_category2.xml index d3e5890ea..ac8784f4b 100644 --- a/java/res/xml/kbd_emoji_category2.xml +++ b/java/res/xml/kbd_emoji_category2.xml @@ -20,9 +20,9 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_objects" diff --git a/java/res/xml/kbd_emoji_category3.xml b/java/res/xml/kbd_emoji_category3.xml index 0efafa814..88c4db92b 100644 --- a/java/res/xml/kbd_emoji_category3.xml +++ b/java/res/xml/kbd_emoji_category3.xml @@ -20,9 +20,9 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_nature" diff --git a/java/res/xml/kbd_emoji_category4.xml b/java/res/xml/kbd_emoji_category4.xml index e5291207f..262384d80 100644 --- a/java/res/xml/kbd_emoji_category4.xml +++ b/java/res/xml/kbd_emoji_category4.xml @@ -20,9 +20,9 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_places" diff --git a/java/res/xml/kbd_emoji_category5.xml b/java/res/xml/kbd_emoji_category5.xml index 1836879d6..bf823f978 100644 --- a/java/res/xml/kbd_emoji_category5.xml +++ b/java/res/xml/kbd_emoji_category5.xml @@ -20,9 +20,9 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_symbols" diff --git a/java/res/xml/kbd_emoji_category6.xml b/java/res/xml/kbd_emoji_category6.xml index b47ebfec0..edb82fc64 100644 --- a/java/res/xml/kbd_emoji_category6.xml +++ b/java/res/xml/kbd_emoji_category6.xml @@ -20,10 +20,10 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" latin:keyLetterSize="90%p" latin:keyLabelSize="60%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:textsArray="@array/emoji_emoticons" diff --git a/java/res/xml/kbd_emoji_recents.xml b/java/res/xml/kbd_emoji_recents.xml index 73926ecc0..edf3872c1 100644 --- a/java/res/xml/kbd_emoji_recents.xml +++ b/java/res/xml/kbd_emoji_recents.xml @@ -20,10 +20,10 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - latin:keyWidth="@fraction/emoji_keyboard_key_width" - latin:keyLetterSize="@fraction/emoji_keyboard_key_letter_size" + latin:keyWidth="@fraction/config_emoji_keyboard_key_width" + latin:keyLetterSize="@fraction/config_emoji_keyboard_key_letter_size" latin:keyLabelSize="60%p" - latin:rowHeight="@fraction/emoji_keyboard_row_height" + latin:rowHeight="@fraction/config_emoji_keyboard_row_height" > <GridRows latin:codesArray="@array/emoji_recents" diff --git a/java/res/xml/kbd_khmer.xml b/java/res/xml/kbd_khmer.xml index 7a2337a48..d703e78f7 100644 --- a/java/res/xml/kbd_khmer.xml +++ b/java/res/xml/kbd_khmer.xml @@ -21,9 +21,9 @@ <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:verticalGap="@fraction/config_key_vertical_gap_5row" + latin:keyLetterSize="@fraction/config_key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/config_key_shifted_letter_hint_ratio_5row" latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include diff --git a/java/res/xml/kbd_lao.xml b/java/res/xml/kbd_lao.xml index 2bba330de..6f7709562 100644 --- a/java/res/xml/kbd_lao.xml +++ b/java/res/xml/kbd_lao.xml @@ -21,9 +21,9 @@ <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:verticalGap="@fraction/config_key_vertical_gap_5row" + latin:keyLetterSize="@fraction/config_key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/config_key_shifted_letter_hint_ratio_5row" latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include diff --git a/java/res/xml/kbd_more_keys_keyboard_template.xml b/java/res/xml/kbd_more_keys_keyboard_template.xml index 537973d03..7104ec7e0 100644 --- a/java/res/xml/kbd_more_keys_keyboard_template.xml +++ b/java/res/xml/kbd_more_keys_keyboard_template.xml @@ -20,7 +20,7 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" latin:keyWidth="10%p" - latin:rowHeight="@dimen/popup_key_height" + latin:rowHeight="@dimen/config_more_keys_keyboard_key_height" style="?attr/moreKeysKeyboardStyle" > </Keyboard> diff --git a/java/res/xml/kbd_pcqwerty.xml b/java/res/xml/kbd_pcqwerty.xml index 5155bc510..045696451 100644 --- a/java/res/xml/kbd_pcqwerty.xml +++ b/java/res/xml/kbd_pcqwerty.xml @@ -21,9 +21,9 @@ <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:verticalGap="@fraction/config_key_vertical_gap_5row" + latin:keyLetterSize="@fraction/config_key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/config_key_shifted_letter_hint_ratio_5row" latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include diff --git a/java/res/xml/kbd_suggestions_pane_template.xml b/java/res/xml/kbd_suggestions_pane_template.xml index 21316e6bb..5b4f60673 100644 --- a/java/res/xml/kbd_suggestions_pane_template.xml +++ b/java/res/xml/kbd_suggestions_pane_template.xml @@ -20,6 +20,6 @@ <Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" latin:keyWidth="10%p" - latin:rowHeight="@dimen/more_suggestions_row_height" + latin:rowHeight="@dimen/config_more_suggestions_row_height" > </Keyboard> diff --git a/java/res/layout/key_preview_gb.xml b/java/res/xml/kbd_swiss.xml index 2f2a321a3..c64ad1103 100644 --- a/java/res/layout/key_preview_gb.xml +++ b/java/res/xml/kbd_swiss.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2010, The Android Open Source Project +** 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. @@ -18,10 +18,9 @@ */ --> -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@drawable/keyboard_key_feedback_gb" - android:minWidth="32dp" - android:gravity="center" -/> +<Keyboard + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/rows_swiss" /> +</Keyboard> diff --git a/java/res/xml/kbd_thai.xml b/java/res/xml/kbd_thai.xml index 294bffb5b..7e65217f9 100644 --- a/java/res/xml/kbd_thai.xml +++ b/java/res/xml/kbd_thai.xml @@ -21,9 +21,9 @@ <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:verticalGap="@fraction/config_key_vertical_gap_5row" + latin:keyLetterSize="@fraction/config_key_letter_ratio_5row" + latin:keyShiftedLetterHintRatio="@fraction/config_key_shifted_letter_hint_ratio_5row" latin:touchPositionCorrectionData="@array/touch_position_correction_data_default" > <include diff --git a/java/res/xml/key_f1.xml b/java/res/xml/key_f1.xml index 72e38cb1a..0e9d497b6 100644 --- a/java/res/xml/key_f1.xml +++ b/java/res/xml/key_f1.xml @@ -37,6 +37,16 @@ latin:keyStyle="f1MoreKeysStyle" /> </case> <case + latin:supportsSwitchingToShortcutIme="false" + > + <Key + latin:keyLabel="!text/keylabel_for_comma" + latin:keyLabelFlags="hasPopupHint" + latin:additionalMoreKeys="!text/more_keys_for_comma" + latin:keyStyle="f1MoreKeysStyle" /> + </case> + <!-- latin:supportsSwitchingToShortcutIme="true" --> + <case latin:hasShortcutKey="true" > <Key diff --git a/java/res/xml/key_nepali_traditional_period.xml b/java/res/xml/key_period.xml index 1c389b009..838db25c3 100644 --- a/java/res/xml/key_nepali_traditional_period.xml +++ b/java/res/xml/key_period.xml @@ -18,32 +18,31 @@ */ --> -<!-- The code point U+25CC for key label is needed because the font rendering system prior to - API version 16 can't automatically render dotted circle for incomplete combining letter - of Hindi. The files named res/xml/{key,keys}_nepali*.xml have this U+25CC hack, although the - counterpart files named res/xml-v16/{key,keys}_nepali*.xml don't have this hack. --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <switch> <case - latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" + latin:languageCode="ne" + latin:keyboardLayoutSet="nepali_traditional" > - <Key - latin:keyLabel="," - latin:backgroundType="functional" /> - </case> - <default> <!-- Because the font rendering system prior to API version 16 can't automatically render dotted circle for incomplete combining letter of some scripts, different set of Key definitions are needed based on the API version. --> <include latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" /> - <!-- U+002E: "." FULL STOP --> <Key latin:keyStyle="baseKeyDevanagariSignVirama" latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!fixedColumnOrder!9,.,!text/more_keys_for_punctuation" + latin:moreKeys="!text/more_keys_for_punctuation" + latin:backgroundType="functional" /> + </case> + <default> + <Key + latin:keyLabel="!text/keylabel_for_period" + latin:keyHintLabel="!text/keyhintlabel_for_period" + latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint" + latin:moreKeys="!text/more_keys_for_period" latin:backgroundType="functional" /> </default> </switch> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index c9d87bfd4..d538eb8c3 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -160,27 +160,10 @@ latin:keyLabelFlags="preserveCase" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> - <switch> - <!-- When this qwerty keyboard has no shortcut keys but shortcut key is enabled, then symbol - keyboard will have a shortcut key. That means we should use label_to_symbol_key label - and shortcut_for_label icon. --> - <case - latin:shortcutKeyOnSymbols="true" - > - <key-style - latin:styleName="baseForToSymbolKeyStyle" - latin:keyIcon="!icon/shortcut_for_label" - latin:keyLabel="!text/label_to_symbol_with_microphone_key" - latin:keyLabelFlags="withIconRight|preserveCase" - latin:parentStyle="baseForLayoutSwitchKeyStyle" /> - </case> - <default> - <key-style - latin:styleName="baseForToSymbolKeyStyle" - latin:keyLabel="!text/label_to_symbol_key" - latin:parentStyle="baseForLayoutSwitchKeyStyle" /> - </default> - </switch> + <key-style + latin:styleName="baseForToSymbolKeyStyle" + latin:keyLabel="!text/label_to_symbol_key" + latin:parentStyle="baseForLayoutSwitchKeyStyle" /> <key-style latin:styleName="toSymbolKeyStyle" latin:code="!code/key_switch_alpha_symbol" @@ -200,12 +183,6 @@ latin:code="!code/key_shift" latin:parentStyle="baseForToSymbolKeyStyle" /> <key-style - latin:styleName="punctuationKeyStyle" - latin:keyLabel="." - 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" diff --git a/java/res/xml/key_symbols_period.xml b/java/res/xml/key_symbols_period.xml deleted file mode 100644 index 6efc9dee3..000000000 --- a/java/res/xml/key_symbols_period.xml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2013, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <!-- U+2105: "℅" CARE OF - U+2122: "™" TRADE MARK SIGN - U+00AE: "®" REGISTERED SIGN - U+00A9: "©" COPYRIGHT SIGN - U+00A7: "§" SECTION SIGN - U+00B6: "¶" PILCROW SIGN - U+002C: "," COMMA - U+2022: "•" BULLET --> - <!-- U+00B0: "°" DEGREE SIGN - U+2032: "′" PRIME - U+2033: "″" DOUBLE PRIME - U+2191: "↑" UPWARDS ARROW - U+2193: "↓" DOWNWARDS ARROW - U+2190: "←" LEFTWARDS ARROW - U+2192: "→" RIGHTWARDS ARROW - U+2026: "…" HORIZONTAL ELLIPSIS --> - <!-- U+0394: "Δ" GREEK CAPITAL LETTER DELTA - U+03A0: "Π" GREEK CAPITAL LETTER PI - U+03C0: "π" GREEK SMALL LETTER PI --> - <Key - latin:keyLabel="." - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!fixedColumnOrder!8,℅,™,®,©,§,¶,\\,,•,°,′,″,↑,↓,←,→,…,!text/more_keys_for_bullet,Δ,Π,π" /> -</merge> diff --git a/java/res/xml/keyboard_layout_set_swiss.xml b/java/res/xml/keyboard_layout_set_swiss.xml new file mode 100644 index 000000000..e17a5ab8b --- /dev/null +++ b/java/res/xml/keyboard_layout_set_swiss.xml @@ -0,0 +1,42 @@ +<?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_swiss" + latin:enableProximityCharsCorrection="true" /> + <Element + latin:elementName="symbols" + latin:elementKeyboard="@xml/kbd_symbols" /> + <Element + latin:elementName="symbolsShifted" + latin:elementKeyboard="@xml/kbd_symbols_shift" /> + <Element + latin:elementName="phone" + latin:elementKeyboard="@xml/kbd_phone" /> + <Element + latin:elementName="phoneSymbols" + latin:elementKeyboard="@xml/kbd_phone_symbols" /> + <Element + latin:elementName="number" + latin:elementKeyboard="@xml/kbd_number" /> +</KeyboardLayoutSet> diff --git a/java/res/xml/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml deleted file mode 100644 index 1b51e45ed..000000000 --- a/java/res/xml/keys_comma_period.xml +++ /dev/null @@ -1,87 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2012, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <switch> - <case - latin:languageCode="ar" - > - <Key - latin:keyLabel="!text/keylabel_for_apostrophe" - latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" - latin:moreKeys="!text/more_keys_for_apostrophe" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_arabic_diacritics" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> - <case - latin:languageCode="fa" - > - <Key - latin:keyLabel="!text/keylabel_for_apostrophe" - latin:keyHintLabel="!text/keyhintlabel_for_apostrophe" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_apostrophe" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/more_keys_for_arabic_diacritics" - latin:backgroundType="functional" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </case> - <case - latin:languageCode="hy" - > - <!-- U+055D: "՝" ARMENIAN COMMA --> - <Key - latin:keyLabel="՝" - latin:backgroundType="functional" /> - <!-- U+0589: "։" ARMENIAN FULL STOP --> - <Key - latin:keyLabel="։" - latin:keyLabelFlags="hasPopupHint" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_punctuation" /> - </case> - <default> - <Key - latin:keyLabel="!text/keylabel_for_tablet_comma" - latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_tablet_comma" /> - <Key - latin:keyLabel="." - latin:keyHintLabel="!text/keyhintlabel_for_period" - latin:backgroundType="functional" - latin:moreKeys="!text/more_keys_for_period" /> - </default> - </switch> -</merge> diff --git a/java/res/xml/keys_comma_period_symbols.xml b/java/res/xml/keys_comma_period_symbols.xml new file mode 100644 index 000000000..6465fedd1 --- /dev/null +++ b/java/res/xml/keys_comma_period_symbols.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keyLabel="," + latin:backgroundType="functional" /> + <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> + <Key + latin:keyLabel="." + latin:moreKeys="…" + latin:backgroundType="functional" /> +</merge> diff --git a/java/res/xml/keys_pcqwerty4_right3.xml b/java/res/xml/keys_pcqwerty4_right3.xml index e6084cb45..a5d5a429c 100644 --- a/java/res/xml/keys_pcqwerty4_right3.xml +++ b/java/res/xml/keys_pcqwerty4_right3.xml @@ -34,7 +34,7 @@ <Key latin:keyLabel="/" latin:additionalMoreKeys="\?" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:moreKeys="!text/more_keys_for_question" /> </case> <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" --> <default> @@ -52,7 +52,7 @@ latin:moreKeys="!fixedColumnOrder!3,›,≥,»" /> <Key latin:keyLabel="\?" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:moreKeys="!text/more_keys_for_question" /> </default> </switch> </merge> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 0a27da93f..78e771fda 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -25,13 +25,14 @@ af: Afrikaans/qwerty ar: Arabic/arabic (az: Azerbaijani/qwerty) # disabled temporarily. waiting for string resources. - be: Belarusian/east_slavic + be_BY: Belarusian/east_slavic bg: Bulgarian/bulgarian bg: Bulgarian/bulgarian_bds ca: Catalan/spanish cs: Czech/qwertz da: Danish/nordic de: German/qwertz + de_CH: German Switzerland/swiss el: Greek/greek en_US: English United States/qwerty en_GB: English Great Britain/qwerty @@ -44,6 +45,7 @@ fi: Finnish/nordic fr: French/azerty fr_CA: French Canada/qwerty + fr_CH: French Switzerland/swiss hi: Hindi/hindi hr: Croatian/qwertz hu: Hungarian/qwertz @@ -88,19 +90,22 @@ (zz: Emoji/emoji) --> <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. --> +<!-- TODO: Remove "AsciiCapable" from the extra values when we can stop supporting JB-MR1 --> <!-- Note: SupportTouchPositionCorrection extra value is obsolete and maintained for backward compatibility. --> <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default subtype.--> <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"> + android:isDefault="@bool/im_is_default" + android:supportsSwitchingToNextInputMethod="true"> <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,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_en_GB" @@ -108,6 +113,7 @@ android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -115,6 +121,7 @@ android:imeSubtypeLocale="af" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -122,6 +129,7 @@ android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="false" /> <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -130,14 +138,16 @@ android:imeSubtypeLocale="az" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x1dc3a859" - android:imeSubtypeLocale="be" + android:imeSubtypeLocale="be_BY" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -145,6 +155,7 @@ android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_bulgarian_bds" @@ -152,6 +163,7 @@ android:imeSubtypeLocale="bg" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -159,6 +171,7 @@ android:imeSubtypeLocale="ca" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -166,6 +179,7 @@ android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -173,6 +187,7 @@ android:imeSubtypeLocale="da" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -180,6 +195,15 @@ android:imeSubtypeLocale="de" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" + /> + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0x7acfd0aa" + android:imeSubtypeLocale="de_CH" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -187,6 +211,7 @@ android:imeSubtypeLocale="el" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=greek,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -194,6 +219,7 @@ android:imeSubtypeLocale="eo" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -201,6 +227,7 @@ android:imeSubtypeLocale="es" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_es_US" @@ -208,6 +235,7 @@ android:imeSubtypeLocale="es_US" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -216,6 +244,7 @@ android:imeSubtypeLocale="es_419" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -224,6 +253,7 @@ android:imeSubtypeLocale="et_EE" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -231,6 +261,7 @@ android:imeSubtypeLocale="fa" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -238,6 +269,7 @@ android:imeSubtypeLocale="fi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -245,6 +277,7 @@ android:imeSubtypeLocale="fr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -252,6 +285,15 @@ android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" + /> + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0xeadc55f5" + android:imeSubtypeLocale="fr_CH" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=swiss,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -259,6 +301,7 @@ android:imeSubtypeLocale="hi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -266,6 +309,7 @@ android:imeSubtypeLocale="hr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -273,6 +317,7 @@ android:imeSubtypeLocale="hu" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -280,6 +325,7 @@ android:imeSubtypeLocale="hy_AM" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic,EmojiCapable" + android:isAsciiCapable="false" /> <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -288,6 +334,7 @@ android:imeSubtypeLocale="in" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -295,6 +342,7 @@ android:imeSubtypeLocale="is" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -302,6 +350,7 @@ android:imeSubtypeLocale="it" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -310,6 +359,7 @@ android:imeSubtypeLocale="iw" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -317,6 +367,7 @@ android:imeSubtypeLocale="ka_GE" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable" + android:isAsciiCapable="false" /> <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -325,6 +376,7 @@ android:imeSubtypeLocale="kk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" + android:isAsciiCapable="false" /> --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -333,6 +385,7 @@ android:imeSubtypeLocale="km_KH" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -340,6 +393,7 @@ android:imeSubtypeLocale="ky" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -347,6 +401,7 @@ android:imeSubtypeLocale="lo_LA" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=lao,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -354,6 +409,7 @@ android:imeSubtypeLocale="lt" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -361,6 +417,7 @@ android:imeSubtypeLocale="lv" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -368,6 +425,7 @@ android:imeSubtypeLocale="mk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -375,6 +433,7 @@ android:imeSubtypeLocale="mn_MN" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -382,6 +441,7 @@ android:imeSubtypeLocale="ms_MY" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -389,6 +449,7 @@ android:imeSubtypeLocale="nb" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -397,6 +458,7 @@ android:imeSubtypeLocale="ne" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_nepali_traditional" @@ -404,6 +466,7 @@ android:imeSubtypeLocale="ne" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable" + android:isAsciiCapable="false" /> --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -412,6 +475,7 @@ android:imeSubtypeLocale="nl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -419,6 +483,7 @@ android:imeSubtypeLocale="nl_BE" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -426,6 +491,7 @@ android:imeSubtypeLocale="pl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -433,6 +499,7 @@ android:imeSubtypeLocale="pt_BR" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -440,6 +507,7 @@ android:imeSubtypeLocale="pt_PT" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -447,6 +515,7 @@ android:imeSubtypeLocale="ro" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -454,6 +523,7 @@ android:imeSubtypeLocale="ru" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -461,6 +531,7 @@ android:imeSubtypeLocale="sk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -468,6 +539,7 @@ android:imeSubtypeLocale="sl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -475,6 +547,7 @@ android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="false" /> <!-- TODO: Uncomment once we can handle IETF language tag with script name specified. <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -483,6 +556,7 @@ android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_serbian_latin" @@ -490,6 +564,7 @@ android:imeSubtypeLocale="sr-Latn" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> --> <subtype android:icon="@drawable/ic_ime_switcher_dark" @@ -498,6 +573,7 @@ android:imeSubtypeLocale="sv" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -505,6 +581,7 @@ android:imeSubtypeLocale="sw" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -512,6 +589,7 @@ android:imeSubtypeLocale="th" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=thai,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -519,6 +597,7 @@ android:imeSubtypeLocale="tl" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -526,6 +605,7 @@ android:imeSubtypeLocale="tr" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -533,6 +613,7 @@ android:imeSubtypeLocale="uk" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable" + android:isAsciiCapable="false" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -540,6 +621,7 @@ android:imeSubtypeLocale="vi" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" @@ -547,6 +629,7 @@ android:imeSubtypeLocale="zu" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_no_language_qwerty" @@ -554,6 +637,7 @@ android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable,EmojiCapable" + android:isAsciiCapable="true" /> <!-- Emoji subtype has to be an addtional subtype added at boot time because ICS doesn't support Emoji. --> @@ -564,6 +648,7 @@ android:imeSubtypeLocale="zz" android:imeSubtypeMode="keyboard" android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji,EmojiCapable" + android:isAsciiCapable="false" /> --> </input-method> diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml index 8d9508e38..124c44388 100644 --- a/java/res/xml/prefs_for_debug.xml +++ b/java/res/xml/prefs_for_debug.xml @@ -58,6 +58,12 @@ android:persistent="true" android:title="@string/prefs_use_only_personalization_dictionary" /> + <CheckBoxPreference + android:defaultValue="false" + android:key="boost_personalization_dictionary_for_debug" + android:persistent="true" + android:title="@string/prefs_boost_personalization_dictionary" /> + <PreferenceScreen android:key="read_external_dictionary" android:title="@string/prefs_read_external_dictionary" /> diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml index 4ec908ba1..0a3f4d289 100644 --- a/java/res/xml/row_pcqwerty5.xml +++ b/java/res/xml/row_pcqwerty5.xml @@ -28,7 +28,7 @@ latin:keyWidth="11.538%p" /> <switch> <case - latin:shortcutKeyEnabled="true" + latin:supportsSwitchingToShortcutIme="true" > <Key latin:keyStyle="shortcutKeyStyle" diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml index 578bc1234..509092d96 100644 --- a/java/res/xml/row_qwerty4.xml +++ b/java/res/xml/row_qwerty4.xml @@ -32,36 +32,8 @@ <include latin:keyXPos="25%p" latin:keyboardLayout="@xml/key_space_5kw" /> - <switch> - <case - latin:languageCode="ar|fa" - > - <Key - latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics" - latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint" - latin:moreKeys="!text/more_keys_for_arabic_diacritics" - latin:keyStyle="punctuationKeyStyle" /> - </case> - <case - latin:languageCode="ne" - latin:keyboardLayoutSet="nepali_traditional" - > - <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" /> - </default> - </switch> + <include + latin:keyboardLayout="@xml/key_period" /> <Key latin:keyStyle="enterKeyStyle" latin:keyWidth="fillRight" /> diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml index fbfdc5f72..09d2a1971 100644 --- a/java/res/xml/row_symbols4.xml +++ b/java/res/xml/row_symbols4.xml @@ -19,24 +19,12 @@ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:backgroundType="functional" latin:keyLabel="_" /> <Key - latin:backgroundType="functional" latin:keyLabel="/" /> - - <switch> - <case latin:hasShortcutKey="true" > - <Key latin:keyStyle="shortcutKeyStyle" /> - </case> - <!-- latin:hasShortcutKey="false" --> - <default> - </default> - </switch> - - <include latin:keyboardLayout="@xml/key_space_symbols" /> - <include latin:keyboardLayout="@xml/keys_comma_period" /> - + <include + latin:keyboardLayout="@xml/key_space_symbols" /> + <include + latin:keyboardLayout="@xml/keys_comma_period_symbols" /> </merge> diff --git a/java/res/xml/row_symbols_shift4.xml b/java/res/xml/row_symbols_shift4.xml index 0909374f4..f75575bc6 100644 --- a/java/res/xml/row_symbols_shift4.xml +++ b/java/res/xml/row_symbols_shift4.xml @@ -22,5 +22,5 @@ <include latin:keyboardLayout="@xml/keys_less_greater" /> <include latin:keyboardLayout="@xml/key_space_symbols" /> - <include latin:keyboardLayout="@xml/keys_comma_period" /> + <include latin:keyboardLayout="@xml/keys_comma_period_symbols" /> </merge> diff --git a/java/res/xml/rowkeys_khmer1.xml b/java/res/xml/rowkeys_khmer1.xml index 25da66400..05d1a864a 100644 --- a/java/res/xml/rowkeys_khmer1.xml +++ b/java/res/xml/rowkeys_khmer1.xml @@ -64,6 +64,7 @@ <Key latin:keyLabel="័" latin:keyHintLabel="៚" + latin:keyHintLabelVerticalAdjustment="-30%" latin:moreKeys="៚" latin:keyLabelFlags="fontNormal" /> <!-- U+17CF: "៏" KHMER SIGN AHSDA --> diff --git a/java/res/xml/rowkeys_khmer2.xml b/java/res/xml/rowkeys_khmer2.xml index cba2d3b90..801f23ff6 100644 --- a/java/res/xml/rowkeys_khmer2.xml +++ b/java/res/xml/rowkeys_khmer2.xml @@ -31,12 +31,13 @@ latin:keyLabel="ឈ" latin:keyHintLabel="ៜ" latin:moreKeys="ៜ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+17BA: "ឺ" KHMER VOWEL SIGN YY U+17DD: "៝" KHMER SIGN ATTHACAN --> <Key latin:keyLabel="ឺ" latin:keyHintLabel="៝" + latin:keyHintLabelVerticalAdjustment="40%" latin:moreKeys="៝" latin:keyLabelFlags="fontNormal" /> <!-- U+17C2: "ែ" KHMER VOWEL SIGN AE --> @@ -69,7 +70,7 @@ <!-- U+17C5: "ៅ" KHMER VOWEL SIGN AU --> <Key latin:keyLabel="ៅ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+1797: "ភ" KHMER LETTER PHO --> <Key latin:keyLabel="ភ" @@ -77,7 +78,7 @@ <!-- U+17BF: "ឿ" KHMER VOWEL SIGN YA --> <Key latin:keyLabel="ឿ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI --> <Key latin:keyLabel="ឰ" @@ -119,7 +120,7 @@ <!-- U+17C4: "ោ" KHMER VOWEL SIGN OO --> <Key latin:keyLabel="ោ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+1795: "ផ" KHMER LETTER PHA --> <Key latin:keyLabel="ផ" @@ -127,7 +128,7 @@ <!-- U+17C0: "ៀ" KHMER VOWEL SIGN IE --> <Key latin:keyLabel="ៀ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+17AA: "ឪ" KHMER INDEPENDENT VOWEL QUUV U+17A7: "ឧ" KHMER INDEPENDENT VOWEL QU U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE diff --git a/java/res/xml/rowkeys_khmer3.xml b/java/res/xml/rowkeys_khmer3.xml index ff6c9ca51..f35ba5c56 100644 --- a/java/res/xml/rowkeys_khmer3.xml +++ b/java/res/xml/rowkeys_khmer3.xml @@ -70,7 +70,7 @@ <!-- U+17C4/U+17C7: "ោះ" KHMER VOWEL SIGN OO/KHMER SIGN REAHMUK --> <Key latin:keyLabel="ោះ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|followKeyLetterRatio|autoScale" /> <!-- U+17C9: "៉" KHMER SIGN MUUSIKATOAN --> <Key latin:keyLabel="៉" diff --git a/java/res/xml/rowkeys_khmer4.xml b/java/res/xml/rowkeys_khmer4.xml index fe6c59125..598aed8d5 100644 --- a/java/res/xml/rowkeys_khmer4.xml +++ b/java/res/xml/rowkeys_khmer4.xml @@ -40,7 +40,7 @@ <!-- U+17C1/U+17C7: "េះ" KHMER VOWEL SIGN E/KHMER SIGN REAHMUK --> <Key latin:keyLabel="េះ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|followKeyLetterRatio|autoScale" /> <!-- U+1796: "ព" KHMER LETTER PO U+179E: "ឞ" KHMER LETTER SSO --> <Key @@ -51,7 +51,7 @@ <!-- U+178E: "ណ" KHMER LETTER NNO --> <Key latin:keyLabel="ណ" - latin:keyLabelFlags="fontNormal" /> + latin:keyLabelFlags="fontNormal|autoScale" /> <!-- U+17C6: "ំ" KHMER SIGN NIKAHIT --> <Key latin:keyLabel="ំ" diff --git a/java/res/xml/rowkeys_pcqwerty1.xml b/java/res/xml/rowkeys_pcqwerty1.xml index de548d0ba..1ac264afa 100644 --- a/java/res/xml/rowkeys_pcqwerty1.xml +++ b/java/res/xml/rowkeys_pcqwerty1.xml @@ -26,7 +26,7 @@ latin:additionalMoreKeys="~" /> <Key latin:keyLabel="1" - latin:additionalMoreKeys="!,!text/more_keys_for_symbols_exclamation" + latin:additionalMoreKeys="!,!text/more_keys_for_exclamation" latin:moreKeys="!text/more_keys_for_symbols_1" /> <Key latin:keyLabel="2" diff --git a/java/res/xml/rowkeys_pcqwerty1_shift.xml b/java/res/xml/rowkeys_pcqwerty1_shift.xml index bc39f944e..718acfd24 100644 --- a/java/res/xml/rowkeys_pcqwerty1_shift.xml +++ b/java/res/xml/rowkeys_pcqwerty1_shift.xml @@ -25,7 +25,7 @@ latin:keyLabel="~" /> <Key latin:keyLabel="!" - latin:additionalMoreKeys="!text/more_keys_for_symbols_exclamation" /> + latin:additionalMoreKeys="!text/more_keys_for_exclamation" /> <Key latin:keyLabel="\@" /> <Key diff --git a/java/res/xml/rowkeys_swiss1.xml b/java/res/xml/rowkeys_swiss1.xml new file mode 100644 index 000000000..e3b842619 --- /dev/null +++ b/java/res/xml/rowkeys_swiss1.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. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/rowkeys_qwertz1" /> + <Key + latin:keyLabel="!text/keylabel_for_swiss_row1_11" + latin:moreKeys="!text/more_keys_for_swiss_row1_11" /> +</merge> diff --git a/java/res/xml/rowkeys_swiss2.xml b/java/res/xml/rowkeys_swiss2.xml new file mode 100644 index 000000000..5364a448c --- /dev/null +++ b/java/res/xml/rowkeys_swiss2.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2" /> + <Key + latin:keyLabel="!text/keylabel_for_swiss_row2_10" + latin:moreKeys="!text/more_keys_for_swiss_row2_10" /> + <Key + latin:keyLabel="!text/keylabel_for_swiss_row2_11" + latin:moreKeys="!text/more_keys_for_swiss_row2_11" /> +</merge> diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml index 074078cb6..e525dc480 100644 --- a/java/res/xml/rowkeys_symbols3.xml +++ b/java/res/xml/rowkeys_symbols3.xml @@ -55,8 +55,8 @@ latin:moreKeys="!text/more_keys_for_symbols_semicolon" /> <Key latin:keyLabel="!" - latin:moreKeys="!text/more_keys_for_symbols_exclamation" /> + latin:moreKeys="!text/more_keys_for_exclamation" /> <Key latin:keyLabel="!text/keylabel_for_symbols_question" - latin:moreKeys="!text/more_keys_for_symbols_question" /> + latin:moreKeys="!text/more_keys_for_question" /> </merge> diff --git a/java/res/xml/rows_swiss.xml b/java/res/xml/rows_swiss.xml new file mode 100644 index 000000000..03e412940 --- /dev/null +++ b/java/res/xml/rows_swiss.xml @@ -0,0 +1,57 @@ +<?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="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_swiss1" /> + </Row> + <Row + latin:keyWidth="9.091%p" + > + <include + latin:keyboardLayout="@xml/rowkeys_swiss2" /> + </Row> + <Row + latin:keyWidth="9.2%p" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="15%p" + latin:visualInsetsRight="1%p" /> + <Spacer + latin:keyWidth="2.8%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwertz3" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyXPos="-15%p" + latin:keyWidth="fillRight" + latin:visualInsetsLeft="1%p" /> + </Row> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 73896dfd3..c601cf17e 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -82,7 +82,7 @@ public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateComp private void initInternal(final InputMethodService inputMethod) { mInputMethod = inputMethod; mEdgeSlop = inputMethod.getResources().getDimensionPixelSize( - R.dimen.accessibility_edge_slop); + R.dimen.config_accessibility_edge_slop); } /** diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java index e23131a30..d56a3cf25 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java @@ -31,17 +31,17 @@ public class EmojiCategoryPageIndicatorView extends LinearLayout { private int mCurrentCategoryPageId = 0; private float mOffset = 0.0f; - public EmojiCategoryPageIndicatorView(Context context) { + public EmojiCategoryPageIndicatorView(final Context context) { this(context, null /* attrs */); } - public EmojiCategoryPageIndicatorView(Context context, AttributeSet attrs) { + public EmojiCategoryPageIndicatorView(final Context context, final AttributeSet attrs) { super(context, attrs); mPaint.setColor(context.getResources().getColor( R.color.emoji_category_page_id_view_foreground)); } - public void setCategoryPageId(int size, int id, float offset) { + public void setCategoryPageId(final int size, final int id, final float offset) { mCategoryPageSize = size; mCurrentCategoryPageId = id; mOffset = offset; @@ -49,7 +49,7 @@ public class EmojiCategoryPageIndicatorView extends LinearLayout { } @Override - protected void onDraw(Canvas canvas) { + protected void onDraw(final Canvas canvas) { if (mCategoryPageSize <= 1) { // If the category is not set yet or contains only one category, // just clear and return. diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java index f12373503..ff0d53865 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java @@ -44,8 +44,8 @@ import android.widget.TabHost.OnTabChangeListener; import android.widget.TextView; import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard; -import com.android.inputmethod.keyboard.internal.ScrollKeyboardView; -import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier; +import com.android.inputmethod.keyboard.internal.EmojiLayoutParams; +import com.android.inputmethod.keyboard.internal.EmojiPageKeyboardView; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -72,15 +72,15 @@ import java.util.concurrent.ConcurrentHashMap; */ public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener, ViewPager.OnPageChangeListener, View.OnClickListener, - ScrollKeyboardView.OnKeyClickListener { - private static final String TAG = EmojiPalettesView.class.getSimpleName(); + EmojiPageKeyboardView.OnKeyClickListener { + static final String TAG = EmojiPalettesView.class.getSimpleName(); private static final boolean DEBUG_PAGER = false; private final int mKeyBackgroundId; private final int mEmojiFunctionalKeyBackgroundId; - private final KeyboardLayoutSet mLayoutSet; private final ColorStateList mTabLabelColor; private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener; private EmojiPalettesAdapter mEmojiPalettesAdapter; + private final EmojiLayoutParams mEmojiLayoutParams; private TabHost mTabHost; private ViewPager mEmojiPager; @@ -149,7 +149,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange public EmojiCategory(final SharedPreferences prefs, final Resources res, final KeyboardLayoutSet layoutSet) { mPrefs = prefs; - mMaxPageKeyCount = res.getInteger(R.integer.emoji_keyboard_max_key_count); + mMaxPageKeyCount = res.getInteger(R.integer.config_emoji_keyboard_max_page_key_count); mLayoutSet = layoutSet; for (int i = 0; i < sCategoryName.length; ++i) { mCategoryNameToIdMap.put(sCategoryName[i], i); @@ -174,7 +174,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange .loadRecentKeys(mCategoryKeyboardMap.values()); } - private void addShownCategoryId(int categoryId) { + private void addShownCategoryId(final int categoryId) { // Load a keyboard of categoryId getKeyboard(categoryId, 0 /* cagetoryPageId */); final CategoryProperties properties = @@ -182,20 +182,20 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange mShownCategories.add(properties); } - public String getCategoryName(int categoryId, int categoryPageId) { + public String getCategoryName(final int categoryId, final int categoryPageId) { return sCategoryName[categoryId] + "-" + categoryPageId; } - public int getCategoryId(String name) { + public int getCategoryId(final String name) { final String[] strings = name.split("-"); return mCategoryNameToIdMap.get(strings[0]); } - public int getCategoryIcon(int categoryId) { + public int getCategoryIcon(final int categoryId) { return sCategoryIcon[categoryId]; } - public String getCategoryLabel(int categoryId) { + public String getCategoryLabel(final int categoryId) { return sCategoryLabel[categoryId]; } @@ -211,7 +211,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return getCategoryPageSize(mCurrentCategoryId); } - public int getCategoryPageSize(int categoryId) { + public int getCategoryPageSize(final int categoryId) { for (final CategoryProperties prop : mShownCategories) { if (prop.mCategoryId == categoryId) { return prop.mPageCount; @@ -222,12 +222,12 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return 0; } - public void setCurrentCategoryId(int categoryId) { + public void setCurrentCategoryId(final int categoryId) { mCurrentCategoryId = categoryId; Settings.writeLastShownEmojiCategoryId(mPrefs, categoryId); } - public void setCurrentCategoryPageId(int id) { + public void setCurrentCategoryPageId(final int id) { mCurrentCategoryPageId = id; } @@ -244,7 +244,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return mCurrentCategoryId == CATEGORY_ID_RECENTS; } - public int getTabIdFromCategoryId(int categoryId) { + public int getTabIdFromCategoryId(final int categoryId) { for (int i = 0; i < mShownCategories.size(); ++i) { if (mShownCategories.get(i).mCategoryId == categoryId) { return i; @@ -255,7 +255,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } // Returns the view pager's page position for the categoryId - public int getPageIdFromCategoryId(int categoryId) { + public int getPageIdFromCategoryId(final int categoryId) { final int lastSavedCategoryPageId = Settings.readLastTypedEmojiCategoryPageId(mPrefs, categoryId); int sum = 0; @@ -274,7 +274,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return getTabIdFromCategoryId(CATEGORY_ID_RECENTS); } - private int getCategoryPageCount(int categoryId) { + private int getCategoryPageCount(final int categoryId) { final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); return (keyboard.getKeys().length - 1) / mMaxPageKeyCount + 1; } @@ -283,9 +283,9 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange // position. The category page id is numbered in each category. And the view page position // is the position of the current shown page in the view pager which contains all pages of // all categories. - public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(int position) { + public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(final int position) { int sum = 0; - for (CategoryProperties properties : mShownCategories) { + for (final CategoryProperties properties : mShownCategories) { final int temp = sum; sum += properties.mPageCount; if (sum > position) { @@ -296,7 +296,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } // Returns a keyboard from the view pager's page position. - public DynamicGridKeyboard getKeyboardFromPagePosition(int position) { + public DynamicGridKeyboard getKeyboardFromPagePosition(final int position) { final Pair<Integer, Integer> categoryAndId = getCategoryIdAndPageIdFromPagePosition(position); if (categoryAndId != null) { @@ -305,39 +305,41 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return null; } - public DynamicGridKeyboard getKeyboard(int categoryId, int id) { - synchronized(mCategoryKeyboardMap) { - final long key = (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; - final DynamicGridKeyboard kbd; - if (!mCategoryKeyboardMap.containsKey(key)) { - if (categoryId != CATEGORY_ID_RECENTS) { - final Keyboard keyboard = - mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); - final Key[][] sortedKeys = sortKeys(keyboard.getKeys(), mMaxPageKeyCount); - for (int i = 0; i < sortedKeys.length; ++i) { - final DynamicGridKeyboard tempKbd = new DynamicGridKeyboard(mPrefs, - mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), - mMaxPageKeyCount, categoryId, i /* categoryPageId */); - for (Key emojiKey : sortedKeys[i]) { - if (emojiKey == null) { - break; - } - tempKbd.addKeyLast(emojiKey); - } - mCategoryKeyboardMap.put((((long) categoryId) - << Constants.MAX_INT_BIT_COUNT) | i, tempKbd); + private static final Long getCategoryKeyboardMapKey(final int categoryId, final int id) { + return (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id; + } + + public DynamicGridKeyboard getKeyboard(final int categoryId, final int id) { + synchronized (mCategoryKeyboardMap) { + final Long categotyKeyboardMapKey = getCategoryKeyboardMapKey(categoryId, id); + if (mCategoryKeyboardMap.containsKey(categotyKeyboardMapKey)) { + return mCategoryKeyboardMap.get(categotyKeyboardMapKey); + } + + if (categoryId == CATEGORY_ID_RECENTS) { + final DynamicGridKeyboard kbd = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId); + mCategoryKeyboardMap.put(categotyKeyboardMapKey, kbd); + return kbd; + } + + final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]); + final Key[][] sortedKeys = sortKeysIntoPages(keyboard.getKeys(), mMaxPageKeyCount); + for (int pageId = 0; pageId < sortedKeys.length; ++pageId) { + final DynamicGridKeyboard tempKeyboard = new DynamicGridKeyboard(mPrefs, + mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), + mMaxPageKeyCount, categoryId); + for (final Key emojiKey : sortedKeys[pageId]) { + if (emojiKey == null) { + break; } - kbd = mCategoryKeyboardMap.get(key); - } else { - kbd = new DynamicGridKeyboard(mPrefs, - mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS), - mMaxPageKeyCount, categoryId, 0 /* categoryPageId */); - mCategoryKeyboardMap.put(key, kbd); + tempKeyboard.addKeyLast(emojiKey); } - } else { - kbd = mCategoryKeyboardMap.get(key); + mCategoryKeyboardMap.put( + getCategoryKeyboardMapKey(categoryId, pageId), tempKeyboard); } - return kbd; + return mCategoryKeyboardMap.get(categotyKeyboardMapKey); } } @@ -349,29 +351,31 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange return sum; } - private Key[][] sortKeys(Key[] inKeys, int maxPageCount) { - Key[] keys = Arrays.copyOf(inKeys, inKeys.length); - Arrays.sort(keys, 0, keys.length, new Comparator<Key>() { - @Override - public int compare(Key lhs, Key rhs) { - final Rect lHitBox = lhs.getHitBox(); - final Rect rHitBox = rhs.getHitBox(); - if (lHitBox.top < rHitBox.top) { - return -1; - } else if (lHitBox.top > rHitBox.top) { - return 1; - } - if (lHitBox.left < rHitBox.left) { - return -1; - } else if (lHitBox.left > rHitBox.left) { - return 1; - } - if (lhs.getCode() == rhs.getCode()) { - return 0; - } - return lhs.getCode() < rhs.getCode() ? -1 : 1; + private static Comparator<Key> EMOJI_KEY_COMPARATOR = new Comparator<Key>() { + @Override + public int compare(final Key lhs, final Key rhs) { + final Rect lHitBox = lhs.getHitBox(); + final Rect rHitBox = rhs.getHitBox(); + if (lHitBox.top < rHitBox.top) { + return -1; + } else if (lHitBox.top > rHitBox.top) { + return 1; + } + if (lHitBox.left < rHitBox.left) { + return -1; + } else if (lHitBox.left > rHitBox.left) { + return 1; + } + if (lhs.getCode() == rhs.getCode()) { + return 0; } - }); + return lhs.getCode() < rhs.getCode() ? -1 : 1; + } + }; + + private static Key[][] sortKeysIntoPages(final Key[] inKeys, final int maxPageCount) { + final Key[] keys = Arrays.copyOf(inKeys, inKeys.length); + Arrays.sort(keys, 0, keys.length, EMOJI_KEY_COMPARATOR); final int pageCount = (keys.length - 1) / maxPageCount + 1; final Key[][] retval = new Key[pageCount][maxPageCount]; for (int i = 0; i < keys.length; ++i) { @@ -404,12 +408,12 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( context, null /* editorInfo */); final Resources res = context.getResources(); - final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); + mEmojiLayoutParams = new EmojiLayoutParams(res); builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), - emojiLp.mEmojiKeyboardHeight); - builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */); - mLayoutSet = builder.build(); + mEmojiLayoutParams.mEmojiKeyboardHeight); + builder.setOptions(false /* shortcutImeEnabled */, false /* showsVoiceInputKey */, + false /* languageSwitchKeyEnabled */); mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context), context.getResources(), builder.build()); mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context); @@ -423,7 +427,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final int width = ResourceUtils.getDefaultKeyboardWidth(res) + getPaddingLeft() + getPaddingRight(); final int height = ResourceUtils.getDefaultKeyboardHeight(res) - + res.getDimensionPixelSize(R.dimen.suggestions_strip_height) + + res.getDimensionPixelSize(R.dimen.config_suggestions_strip_height) + getPaddingTop() + getPaddingBottom(); setMeasuredDimension(width, height); } @@ -458,25 +462,23 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange mTabHost.setOnTabChangedListener(this); mTabHost.getTabWidget().setStripEnabled(true); - mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, mLayoutSet, this); + mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, this); mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager); mEmojiPager.setAdapter(mEmojiPalettesAdapter); mEmojiPager.setOnPageChangeListener(this); mEmojiPager.setOffscreenPageLimit(0); - mEmojiPager.setPersistentDrawingCache(ViewPager.PERSISTENT_NO_CACHE); - final Resources res = getResources(); - final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res); - emojiLp.setPagerProperties(mEmojiPager); + mEmojiPager.setPersistentDrawingCache(PERSISTENT_NO_CACHE); + mEmojiLayoutParams.setPagerProperties(mEmojiPager); mEmojiCategoryPageIndicatorView = (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view); - emojiLp.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); + mEmojiLayoutParams.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView); setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */); final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar); - emojiLp.setActionBarProperties(actionBar); + mEmojiLayoutParams.setActionBarProperties(actionBar); final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete); deleteKey.setTag(Constants.CODE_DELETE); @@ -489,7 +491,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange spaceKey.setBackgroundResource(mKeyBackgroundId); spaceKey.setTag(Constants.CODE_SPACE); spaceKey.setOnClickListener(this); - emojiLp.setKeyProperties(spaceKey); + mEmojiLayoutParams.setKeyProperties(spaceKey); final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2); alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId); alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL); @@ -628,16 +630,15 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } private static class EmojiPalettesAdapter extends PagerAdapter { - private final ScrollKeyboardView.OnKeyClickListener mListener; + private final EmojiPageKeyboardView.OnKeyClickListener mListener; private final DynamicGridKeyboard mRecentsKeyboard; - private final SparseArray<ScrollKeyboardView> mActiveKeyboardViews = + private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews = CollectionUtils.newSparseArray(); private final EmojiCategory mEmojiCategory; private int mActivePosition = 0; public EmojiPalettesAdapter(final EmojiCategory emojiCategory, - final KeyboardLayoutSet layoutSet, - final ScrollKeyboardView.OnKeyClickListener listener) { + final EmojiPageKeyboardView.OnKeyClickListener listener) { mEmojiCategory = emojiCategory; mListener = listener; mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0); @@ -671,11 +672,12 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } @Override - public void setPrimaryItem(final View container, final int position, final Object object) { + public void setPrimaryItem(final ViewGroup container, final int position, + final Object object) { if (mActivePosition == position) { return; } - final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition); + final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition); if (oldKeyboardView != null) { oldKeyboardView.releaseCurrentKey(); oldKeyboardView.deallocateMemory(); @@ -688,7 +690,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange if (DEBUG_PAGER) { Log.d(TAG, "instantiate item: " + position); } - final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position); + final EmojiPageKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position); if (oldKeyboardView != null) { oldKeyboardView.deallocateMemory(); // This may be redundant but wanted to be safer.. @@ -697,18 +699,13 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange final Keyboard keyboard = mEmojiCategory.getKeyboardFromPagePosition(position); final LayoutInflater inflater = LayoutInflater.from(container.getContext()); - final View view = inflater.inflate( + final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate( R.layout.emoji_keyboard_page, container, false /* attachToRoot */); - final ScrollKeyboardView keyboardView = (ScrollKeyboardView)view.findViewById( - R.id.emoji_keyboard_page); keyboardView.setKeyboard(keyboard); keyboardView.setOnKeyClickListener(mListener); - final ScrollViewWithNotifier scrollView = (ScrollViewWithNotifier)view.findViewById( - R.id.emoji_keyboard_scroller); - keyboardView.setScrollView(scrollView); - container.addView(view); + container.addView(keyboardView); mActiveKeyboardViews.put(position, keyboardView); - return view; + return keyboardView; } @Override @@ -722,7 +719,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange if (DEBUG_PAGER) { Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName()); } - final ScrollKeyboardView keyboardView = mActiveKeyboardViews.get(position); + final EmojiPageKeyboardView keyboardView = mActiveKeyboardViews.get(position); if (keyboardView != null) { keyboardView.deallocateMemory(); mActiveKeyboardViews.remove(position); @@ -795,7 +792,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange } } - public void pressDelete(int repeatCount) { + public void pressDelete(final int repeatCount) { mKeyboardActionListener.onPressKey( Constants.CODE_DELETE, repeatCount, true /* isSinglePointer */); mKeyboardActionListener.onCodeInput( @@ -804,22 +801,22 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange Constants.CODE_DELETE, false /* withSliding */); } - public void setKeyboardActionListener(KeyboardActionListener listener) { + public void setKeyboardActionListener(final KeyboardActionListener listener) { mKeyboardActionListener = listener; } @Override - public boolean onTouch(View v, MotionEvent event) { + public boolean onTouch(final View v, final MotionEvent event) { switch(event.getAction()) { - case MotionEvent.ACTION_DOWN: - v.setBackgroundColor(mDeleteKeyPressedBackgroundColor); - pressDelete(0 /* repeatCount */); - startRepeat(); - return true; - case MotionEvent.ACTION_UP: - v.setBackgroundColor(0); - abortRepeat(); - return true; + case MotionEvent.ACTION_DOWN: + v.setBackgroundColor(mDeleteKeyPressedBackgroundColor); + pressDelete(0 /* repeatCount */); + startRepeat(); + return true; + case MotionEvent.ACTION_UP: + v.setBackgroundColor(0); + abortRepeat(); + return true; } return false; } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index f7ec9509d..b975b9c70 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -28,7 +28,6 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.util.Log; import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyDrawParams; @@ -53,8 +52,6 @@ import java.util.Locale; * Class for describing the position and characteristics of a single key in the keyboard. */ public class Key implements Comparable<Key> { - private static final String TAG = Key.class.getSimpleName(); - /** * The key code (unicode or custom code) that this key generates. */ @@ -84,10 +81,16 @@ public class Key implements Comparable<Key> { private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800; private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; + // The bit to calculate the ratio of key label width against key width. If autoXScale bit is on + // and autoYScale bit is off, the key label may be shrunk only for X-direction. + // If both autoXScale and autoYScale bits are on, the key label text size may be auto scaled. private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; - private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; - private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000; - private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000; + private static final int LABEL_FLAGS_AUTO_Y_SCALE = 0x8000; + private static final int LABEL_FLAGS_AUTO_SCALE = LABEL_FLAGS_AUTO_X_SCALE + | LABEL_FLAGS_AUTO_Y_SCALE; + private static final int LABEL_FLAGS_PRESERVE_CASE = 0x10000; + private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000; + private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000; private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000; private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000; @@ -376,9 +379,6 @@ public class Key implements Comparable<Key> { mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); keyAttr.recycle(); mHashCode = computeHashCode(this); - if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) { - Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this); - } } /** @@ -702,10 +702,14 @@ public class Key implements Comparable<Key> { return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; } - public final boolean needsXScale() { + public final boolean needsAutoXScale() { return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; } + public final boolean needsAutoScale() { + return (mLabelFlags & LABEL_FLAGS_AUTO_SCALE) == LABEL_FLAGS_AUTO_SCALE; + } + public final boolean isShiftedLetterActivated() { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index befb6fa92..149f10fd7 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -81,7 +81,7 @@ public class KeyDetector { return mKeyboard; } - public boolean alwaysAllowsSlidingInput() { + public boolean alwaysAllowsKeySelectionByDraggingFinger() { return false; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 736f13ed6..02beb3f11 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -70,8 +70,7 @@ public final class KeyboardId { public final int mElementId; private final EditorInfo mEditorInfo; public final boolean mClobberSettingsKey; - public final boolean mShortcutKeyEnabled; - public final boolean mShortcutKeyOnSymbols; + public final boolean mSupportsSwitchingToShortcutIme; public final boolean mLanguageSwitchKeyEnabled; public final String mCustomActionLabel; public final boolean mHasShortcutKey; @@ -87,17 +86,11 @@ public final class KeyboardId { mElementId = elementId; mEditorInfo = params.mEditorInfo; mClobberSettingsKey = params.mNoSettingsKey; - mShortcutKeyEnabled = params.mVoiceKeyEnabled; - mShortcutKeyOnSymbols = mShortcutKeyEnabled && !params.mVoiceKeyOnMain; + mSupportsSwitchingToShortcutIme = params.mSupportsSwitchingToShortcutIme; mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled; mCustomActionLabel = (mEditorInfo.actionLabel != null) ? mEditorInfo.actionLabel.toString() : null; - final boolean alphabetMayHaveShortcutKey = isAlphabetKeyboard(elementId) - && !mShortcutKeyOnSymbols; - final boolean symbolsMayHaveShortcutKey = (elementId == KeyboardId.ELEMENT_SYMBOLS) - && mShortcutKeyOnSymbols; - mHasShortcutKey = mShortcutKeyEnabled - && (alphabetMayHaveShortcutKey || symbolsMayHaveShortcutKey); + mHasShortcutKey = mSupportsSwitchingToShortcutIme && params.mShowsVoiceInputKey; mHashCode = computeHashCode(this); } @@ -110,8 +103,8 @@ public final class KeyboardId { id.mHeight, id.passwordInput(), id.mClobberSettingsKey, - id.mShortcutKeyEnabled, - id.mShortcutKeyOnSymbols, + id.mSupportsSwitchingToShortcutIme, + id.mHasShortcutKey, id.mLanguageSwitchKeyEnabled, id.isMultiLine(), id.imeAction(), @@ -131,8 +124,8 @@ public final class KeyboardId { && other.mHeight == mHeight && other.passwordInput() == passwordInput() && other.mClobberSettingsKey == mClobberSettingsKey - && other.mShortcutKeyEnabled == mShortcutKeyEnabled - && other.mShortcutKeyOnSymbols == mShortcutKeyOnSymbols + && other.mSupportsSwitchingToShortcutIme == mSupportsSwitchingToShortcutIme + && other.mHasShortcutKey == mHasShortcutKey && other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled && other.isMultiLine() == isMultiLine() && other.imeAction() == imeAction() @@ -186,21 +179,20 @@ public final class KeyboardId { @Override public String toString() { - return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s %s%s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), mWidth, mHeight, modeName(mMode), - imeAction(), - (navigateNext() ? "navigateNext" : ""), - (navigatePrevious() ? "navigatePrevious" : ""), + actionName(imeAction()), + (navigateNext() ? " navigateNext" : ""), + (navigatePrevious() ? " navigatePrevious" : ""), (mClobberSettingsKey ? " clobberSettingsKey" : ""), (passwordInput() ? " passwordInput" : ""), - (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""), - (mShortcutKeyOnSymbols ? " shortcutKeyOnSymbols" : ""), + (mSupportsSwitchingToShortcutIme ? " supportsSwitchingToShortcutIme" : ""), (mHasShortcutKey ? " hasShortcutKey" : ""), (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""), - (isMultiLine() ? "isMultiLine" : "") + (isMultiLine() ? " isMultiLine" : "") ); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 1eccdf341..e5b814faf 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -105,10 +105,10 @@ public final class KeyboardLayoutSet { int mMode; EditorInfo mEditorInfo; boolean mDisableTouchPositionCorrectionDataForTest; - boolean mVoiceKeyEnabled; - // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show - // the voice input key on the symbol layout - boolean mVoiceKeyOnMain; + boolean mIsPasswordField; + boolean mSupportsSwitchingToShortcutIme; + boolean mShowsVoiceInputKey; + boolean mNoMicrophoneKey; boolean mNoSettingsKey; boolean mLanguageSwitchKeyEnabled; InputMethodSubtype mSubtype; @@ -221,16 +221,24 @@ public final class KeyboardLayoutSet { private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo(); - public Builder(final Context context, final EditorInfo editorInfo) { + public Builder(final Context context, final EditorInfo ei) { mContext = context; mPackageName = context.getPackageName(); mResources = context.getResources(); final Params params = mParams; + final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO; params.mMode = getKeyboardMode(editorInfo); - params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO; + params.mEditorInfo = editorInfo; + params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType); + @SuppressWarnings("deprecation") + final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( + null, NO_MICROPHONE_COMPAT, editorInfo); + params.mNoMicrophoneKey = InputAttributes.inPrivateImeOptions( + mPackageName, NO_MICROPHONE, editorInfo) + || deprecatedNoMicrophone; params.mNoSettingsKey = InputAttributes.inPrivateImeOptions( - mPackageName, NO_SETTINGS_KEY, params.mEditorInfo); + mPackageName, NO_SETTINGS_KEY, editorInfo); } public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) { @@ -261,18 +269,11 @@ public final class KeyboardLayoutSet { return this; } - // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show - // the voice input key on the symbol layout - public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain, - final boolean languageSwitchKeyEnabled) { - @SuppressWarnings("deprecation") - final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( - null, NO_MICROPHONE_COMPAT, mParams.mEditorInfo); - final boolean noMicrophone = InputAttributes.inPrivateImeOptions( - mPackageName, NO_MICROPHONE, mParams.mEditorInfo) - || deprecatedNoMicrophone; - mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone; - mParams.mVoiceKeyOnMain = voiceKeyOnMain; + public Builder setOptions(final boolean isShortcutImeEnabled, + final boolean showsVoiceInputKey, final boolean languageSwitchKeyEnabled) { + mParams.mSupportsSwitchingToShortcutIme = + isShortcutImeEnabled && !mParams.mNoMicrophoneKey && !mParams.mIsPasswordField; + mParams.mShowsVoiceInputKey = showsVoiceInputKey; mParams.mLanguageSwitchKeyEnabled = languageSwitchKeyEnabled; return this; } @@ -368,9 +369,6 @@ public final class KeyboardLayoutSet { } private static int getKeyboardMode(final EditorInfo editorInfo) { - if (editorInfo == null) - return KeyboardId.MODE_TEXT; - final int inputType = editorInfo.inputType; final int variation = inputType & InputType.TYPE_MASK_VARIATION; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 5abc9ab38..fb84f1d73 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -154,8 +154,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); builder.setOptions( - settingsValues.isVoiceKeyEnabled(editorInfo), - true /* always show a voice key on the main keyboard */, + mSubtypeSwitcher.isShortcutImeEnabled(), + settingsValues.mShowsVoiceInputKey, settingsValues.isLanguageSwitchKeyEnabled()); mKeyboardLayoutSet = builder.build(); try { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index 5578713a0..422bd12a3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -113,9 +113,6 @@ public class KeyboardView extends View { private final Canvas mOffscreenCanvas = new Canvas(); private final Paint mPaint = new Paint(); private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); - private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; - private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; - public KeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.keyboardViewStyle); } @@ -322,7 +319,7 @@ public class KeyboardView extends View { params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE; if (!key.isSpacer()) { - onDrawKeyBackground(key, canvas); + onDrawKeyBackground(key, canvas, mKeyBackground); } onDrawKeyTopVisuals(key, canvas, paint, params); @@ -330,14 +327,14 @@ public class KeyboardView extends View { } // Draw key background. - protected void onDrawKeyBackground(final Key key, final Canvas canvas) { + protected void onDrawKeyBackground(final Key key, final Canvas canvas, + final Drawable background) { final Rect padding = mKeyBackgroundPadding; final int bgWidth = key.getDrawWidth() + padding.left + padding.right; final int bgHeight = key.getHeight() + padding.top + padding.bottom; final int bgX = -padding.left; final int bgY = -padding.top; final int[] drawableState = key.getCurrentDrawableState(); - final Drawable background = mKeyBackground; background.setState(drawableState); final Rect bounds = background.getBounds(); if (bgWidth != bounds.right || bgHeight != bounds.bottom) { @@ -370,10 +367,8 @@ public class KeyboardView extends View { if (label != null) { paint.setTypeface(key.selectTypeface(params)); paint.setTextSize(key.selectTextSize(params)); - final float labelCharHeight = TypefaceUtils.getCharHeight( - KEY_LABEL_REFERENCE_CHAR, paint); - final float labelCharWidth = TypefaceUtils.getCharWidth( - KEY_LABEL_REFERENCE_CHAR, paint); + final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); + final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); // Vertical label text alignment. final float baseline = centerY + labelCharHeight / 2.0f; @@ -391,12 +386,12 @@ public class KeyboardView extends View { positionX = centerX - labelCharWidth * 7.0f / 4.0f; paint.setTextAlign(Align.LEFT); } else if (key.hasLabelWithIconLeft() && icon != null) { - labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth() + labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() + LABEL_ICON_MARGIN * keyWidth; positionX = centerX + labelWidth / 2.0f; paint.setTextAlign(Align.RIGHT); } else if (key.hasLabelWithIconRight() && icon != null) { - labelWidth = TypefaceUtils.getLabelWidth(label, paint) + icon.getIntrinsicWidth() + labelWidth = TypefaceUtils.getStringWidth(label, paint) + icon.getIntrinsicWidth() + LABEL_ICON_MARGIN * keyWidth; positionX = centerX - labelWidth / 2.0f; paint.setTextAlign(Align.LEFT); @@ -404,9 +399,15 @@ public class KeyboardView extends View { positionX = centerX; paint.setTextAlign(Align.CENTER); } - if (key.needsXScale()) { - paint.setTextScaleX(Math.min(1.0f, - (keyWidth * MAX_LABEL_RATIO) / TypefaceUtils.getLabelWidth(label, paint))); + if (key.needsAutoXScale()) { + final float ratio = Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / + TypefaceUtils.getStringWidth(label, paint)); + if (key.needsAutoScale()) { + final float autoSize = paint.getTextSize() * ratio; + paint.setTextSize(autoSize); + } else { + paint.setTextScaleX(ratio); + } } paint.setColor(key.selectTextColor(params)); @@ -451,36 +452,35 @@ public class KeyboardView extends View { // TODO: Should add a way to specify type face for hint letters paint.setTypeface(Typeface.DEFAULT_BOLD); blendAlpha(paint, params.mAnimAlpha); + final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); + final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); + final KeyVisualAttributes visualAttr = key.getVisualAttributes(); + final float adjustmentY = (visualAttr == null) ? 0.0f + : visualAttr.mHintLabelVerticalAdjustment * labelCharHeight; final float hintX, hintY; if (key.hasHintLabel()) { // The hint label is placed just right of the key label. Used mainly on // "phone number" layout. // TODO: Generalize the following calculations. - hintX = positionX - + TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) * 2.0f; - hintY = centerY - + TypefaceUtils.getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f; + hintX = positionX + labelCharWidth * 2.0f; + hintY = centerY + labelCharHeight / 2.0f; paint.setTextAlign(Align.LEFT); } else if (key.hasShiftedLetterHint()) { // The hint label is placed at top-right corner of the key. Used mainly on tablet. - hintX = keyWidth - mKeyShiftedLetterHintPadding - - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f; + hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; paint.getFontMetrics(mFontMetrics); hintY = -mFontMetrics.top; 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); + final float hintDigitWidth = TypefaceUtils.getReferenceDigitWidth(paint); + final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); hintX = keyWidth - mKeyHintLetterPadding - - Math.max(keyNumericHintLabelReferenceCharWidth, keyHintLabelStringWidth) - / 2.0f; + - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; hintY = -paint.ascent(); paint.setTextAlign(Align.CENTER); } - canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY, paint); + canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint); if (LatinImeLogger.sVISUALDEBUG) { final Paint line = new Paint(); @@ -530,7 +530,7 @@ public class KeyboardView extends View { paint.setColor(params.mHintLabelColor); paint.setTextAlign(Align.CENTER); final float hintX = keyWidth - mKeyHintLetterPadding - - TypefaceUtils.getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2.0f; + - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; final float hintY = keyHeight - mKeyPopupHintLetterPadding; canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 13db47004..1400e05c8 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -16,7 +16,10 @@ package com.android.inputmethod.keyboard; +import android.animation.Animator; import android.animation.AnimatorInflater; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.content.SharedPreferences; @@ -28,27 +31,24 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Typeface; import android.graphics.drawable.Drawable; -import android.os.Message; -import android.os.SystemClock; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; -import android.util.SparseArray; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.annotations.ExternallyReferenced; -import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; -import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.keyboard.internal.DrawingHandler; import com.android.inputmethod.keyboard.internal.GestureFloatingPreviewText; import com.android.inputmethod.keyboard.internal.GestureTrailsPreview; import com.android.inputmethod.keyboard.internal.KeyDrawParams; @@ -56,6 +56,7 @@ import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; import com.android.inputmethod.keyboard.internal.PreviewPlacerView; import com.android.inputmethod.keyboard.internal.SlidingKeyInputPreview; +import com.android.inputmethod.keyboard.internal.TimerHandler; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -64,13 +65,15 @@ import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; -import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.TypefaceUtils; import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; import com.android.inputmethod.research.ResearchLogger; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.HashSet; import java.util.WeakHashMap; /** @@ -78,9 +81,10 @@ import java.util.WeakHashMap; * * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon - * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio - * @attr ref R.styleable#MainKeyboardView_spacebarTextColor - * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor + * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio + * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor + * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowColor + * @attr ref R.styleable#MainKeyboardView_spacebarBackground * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator @@ -88,7 +92,7 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance - * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable + * @attr ref R.styleable#MainKeyboardView_keySelectionByDraggingFinger * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout @@ -114,26 +118,27 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration */ -public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler, - PointerTracker.DrawingProxy, MoreKeysPanel.Controller { +public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy, + MoreKeysPanel.Controller, DrawingHandler.Callbacks, TimerHandler.Callbacks { private static final String TAG = MainKeyboardView.class.getSimpleName(); /** Listener for {@link KeyboardActionListener}. */ private KeyboardActionListener mKeyboardActionListener; - /* Space key and its icons */ + /* Space key and its icon and background. */ private Key mSpaceKey; - private Drawable mSpaceIcon; + private Drawable mSpacebarIcon; + private final Drawable mSpacebarBackground; // Stuff to draw language name on spacebar. private final int mLanguageOnSpacebarFinalAlpha; private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator; private boolean mNeedsToDisplayLanguage; private boolean mHasMultipleEnabledIMEsOrSubtypes; private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE; - private final float mSpacebarTextRatio; - private float mSpacebarTextSize; - private final int mSpacebarTextColor; - private final int mSpacebarTextShadowColor; + private final float mLanguageOnSpacebarTextRatio; + private float mLanguageOnSpacebarTextSize; + private final int mLanguageOnSpacebarTextColor; + private final int mLanguageOnSpacebarTextShadowColor; // The minimum x-scale to fit the language name on spacebar. private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f; // Stuff to draw auto correction LED on spacebar. @@ -143,8 +148,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private static final int SPACE_LED_LENGTH_PERCENT = 80; // Stuff to draw altCodeWhileTyping keys. - private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; - private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; + private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator; + private final ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator; private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE; // Preview placer view @@ -155,13 +160,26 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final SlidingKeyInputPreview mSlidingKeyInputPreview; // Key preview + private static final boolean FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED = false; private final int mKeyPreviewLayoutId; private final int mKeyPreviewOffset; private final int mKeyPreviewHeight; - private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray(); + // Free {@link TextView} pool that can be used for key preview. + private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque(); + // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. + private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap(); private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams(); private boolean mShowKeyPreviewPopup = true; private int mKeyPreviewLingerTimeout; + private int mKeyPreviewZoomInDuration; + private int mKeyPreviewZoomOutDuration; + private static final float KEY_PREVIEW_START_ZOOM_IN_SCALE = 0.7f; + private static final float KEY_PREVIEW_END_ZOOM_IN_SCALE = 1.0f; + private static final float KEY_PREVIEW_END_ZOOM_OUT_SCALE = 0.7f; + private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = + new AccelerateInterpolator(); + private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = + new DecelerateInterpolator(); // More keys keyboard private final Paint mBackgroundDimAlphaPaint = new Paint(); @@ -178,244 +196,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // TODO: Make this parameter customizable by user via settings. private int mGestureFloatingPreviewTextLingerTimeout; - private KeyDetector mKeyDetector; + private final KeyDetector mKeyDetector; private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper; - private final KeyTimerHandler mKeyTimerHandler; + private final TimerHandler mKeyTimerHandler; private final int mLanguageOnSpacebarHorizontalMargin; - private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView> - implements TimerProxy { - private static final int MSG_TYPING_STATE_EXPIRED = 0; - private static final int MSG_REPEAT_KEY = 1; - private static final int MSG_LONGPRESS_KEY = 2; - private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3; - private static final int MSG_UPDATE_BATCH_INPUT = 4; - - private final int mIgnoreAltCodeKeyTimeout; - private final int mGestureRecognitionUpdateTime; - - public KeyTimerHandler(final MainKeyboardView outerInstance, - final TypedArray mainKeyboardViewAttr) { - super(outerInstance); - - mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); - mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( - R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); - } - - @Override - public void handleMessage(final Message msg) { - final MainKeyboardView keyboardView = getOuterInstance(); - if (keyboardView == null) { - return; - } - final PointerTracker tracker = (PointerTracker) msg.obj; - switch (msg.what) { - case MSG_TYPING_STATE_EXPIRED: - startWhileTypingFadeinAnimation(keyboardView); - break; - case MSG_REPEAT_KEY: - tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); - break; - case MSG_LONGPRESS_KEY: - keyboardView.onLongPress(tracker); - break; - case MSG_UPDATE_BATCH_INPUT: - tracker.updateBatchInputByTimer(SystemClock.uptimeMillis()); - startUpdateBatchInputTimer(tracker); - break; - } - } - - @Override - public void startKeyRepeatTimer(final PointerTracker tracker, final int repeatCount, - final int delay) { - final Key key = tracker.getKey(); - if (key == null || delay == 0) { - return; - } - sendMessageDelayed( - obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay); - } - - public void cancelKeyRepeatTimer() { - removeMessages(MSG_REPEAT_KEY); - } - - // TODO: Suppress layout changes in key repeat mode - public boolean isInKeyRepeat() { - return hasMessages(MSG_REPEAT_KEY); - } - - @Override - public void startLongPressTimer(final PointerTracker tracker, final int delay) { - cancelLongPressTimer(); - if (delay <= 0) return; - sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay); - } - - @Override - public void cancelLongPressTimer() { - removeMessages(MSG_LONGPRESS_KEY); - } - - private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, - final ObjectAnimator animatorToStart) { - if (animatorToCancel == null || animatorToStart == null) { - // TODO: Stop using null as a no-operation animator. - return; - } - float startFraction = 0.0f; - if (animatorToCancel.isStarted()) { - animatorToCancel.cancel(); - startFraction = 1.0f - animatorToCancel.getAnimatedFraction(); - } - final long startTime = (long)(animatorToStart.getDuration() * startFraction); - animatorToStart.start(); - animatorToStart.setCurrentPlayTime(startTime); - } - - private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) { - cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator, - keyboardView.mAltCodeKeyWhileTypingFadeinAnimator); - } - - private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) { - cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator, - keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator); - } - - @Override - public void startTypingStateTimer(final Key typedKey) { - if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) { - return; - } - - final boolean isTyping = isTypingState(); - removeMessages(MSG_TYPING_STATE_EXPIRED); - final MainKeyboardView keyboardView = getOuterInstance(); - - // When user hits the space or the enter key, just cancel the while-typing timer. - final int typedCode = typedKey.getCode(); - if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { - if (isTyping) { - startWhileTypingFadeinAnimation(keyboardView); - } - return; - } - - sendMessageDelayed( - obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout); - if (isTyping) { - return; - } - startWhileTypingFadeoutAnimation(keyboardView); - } - - @Override - public boolean isTypingState() { - return hasMessages(MSG_TYPING_STATE_EXPIRED); - } - - @Override - public void startDoubleTapShiftKeyTimer() { - sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY), - ViewConfiguration.getDoubleTapTimeout()); - } - - @Override - public void cancelDoubleTapShiftKeyTimer() { - removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY); - } - - @Override - public boolean isInDoubleTapShiftKeyTimeout() { - return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY); - } - - @Override - public void cancelKeyTimers() { - cancelKeyRepeatTimer(); - cancelLongPressTimer(); - } - - @Override - public void startUpdateBatchInputTimer(final PointerTracker tracker) { - if (mGestureRecognitionUpdateTime <= 0) { - return; - } - removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); - sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker), - mGestureRecognitionUpdateTime); - } - - @Override - public void cancelUpdateBatchInputTimer(final PointerTracker tracker) { - removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); - } - - @Override - public void cancelAllUpdateBatchInputTimers() { - removeMessages(MSG_UPDATE_BATCH_INPUT); - } - - public void cancelAllMessages() { - cancelKeyTimers(); - cancelAllUpdateBatchInputTimers(); - } - } - - private final DrawingHandler mDrawingHandler = new DrawingHandler(this); - - public static class DrawingHandler extends StaticInnerHandlerWrapper<MainKeyboardView> { - private static final int MSG_DISMISS_KEY_PREVIEW = 0; - private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; - - public DrawingHandler(final MainKeyboardView outerInstance) { - super(outerInstance); - } - - @Override - public void handleMessage(final Message msg) { - final MainKeyboardView mainKeyboardView = getOuterInstance(); - if (mainKeyboardView == null) return; - final PointerTracker tracker = (PointerTracker) msg.obj; - switch (msg.what) { - case MSG_DISMISS_KEY_PREVIEW: - final TextView previewText = mainKeyboardView.mKeyPreviewTexts.get( - tracker.mPointerId); - if (previewText != null) { - previewText.setVisibility(INVISIBLE); - } - break; - case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: - mainKeyboardView.showGestureFloatingPreviewText(SuggestedWords.EMPTY); - break; - } - } - - public void dismissKeyPreview(final long delay, final PointerTracker tracker) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay); - } - - public void cancelDismissKeyPreview(final PointerTracker tracker) { - removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker); - } - - private void cancelAllDismissKeyPreviews() { - removeMessages(MSG_DISMISS_KEY_PREVIEW); - } - - public void dismissGestureFloatingPreviewText(final long delay) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); - } - - public void cancelAllMessages() { - cancelAllDismissKeyPreviews(); - } - } + private final DrawingHandler mDrawingHandler = + new DrawingHandler(this); public MainKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.mainKeyboardViewStyle); @@ -424,7 +212,27 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); - PointerTracker.init(getResources()); + mPreviewPlacerView = new PreviewPlacerView(context, attrs); + + final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( + attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); + final int ignoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0); + final int gestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0); + mKeyTimerHandler = new TimerHandler( + this, ignoreAltCodeKeyTimeout, gestureRecognitionUpdateTime); + + final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( + R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f); + final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension( + R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f); + mKeyDetector = new KeyDetector( + keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); + + PointerTracker.init(mainKeyboardViewAttr, mKeyTimerHandler, this /* DrawingProxy */, + mKeyDetector); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final boolean forceNonDistinctMultitouch = prefs.getBoolean( DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH, false); @@ -432,26 +240,24 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT) && !forceNonDistinctMultitouch; mNonDistinctMultitouchHelper = hasDistinctMultitouch ? null - : new NonDistinctMultitouchHelper(); - - mPreviewPlacerView = new PreviewPlacerView(context, attrs); + : new NonDistinctMultitouchHelper(PointerTracker.getPointerTracker(0)); - final TypedArray mainKeyboardViewAttr = context.obtainStyledAttributes( - attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView); final int backgroundDimAlpha = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_backgroundDimAlpha, 0); mBackgroundDimAlphaPaint.setColor(Color.BLACK); mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha); + mSpacebarBackground = mainKeyboardViewAttr.getDrawable( + R.styleable.MainKeyboardView_spacebarBackground); mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean( R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false); mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable( R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon); - mSpacebarTextRatio = mainKeyboardViewAttr.getFraction( - R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f); - mSpacebarTextColor = mainKeyboardViewAttr.getColor( - R.styleable.MainKeyboardView_spacebarTextColor, 0); - mSpacebarTextShadowColor = mainKeyboardViewAttr.getColor( - R.styleable.MainKeyboardView_spacebarTextShadowColor, 0); + mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction( + R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f); + mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor( + R.styleable.MainKeyboardView_languageOnSpacebarTextColor, 0); + mLanguageOnSpacebarTextShadowColor = mainKeyboardViewAttr.getColor( + R.styleable.MainKeyboardView_languageOnSpacebarTextShadowColor, 0); mLanguageOnSpacebarFinalAlpha = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha, Constants.Color.ALPHA_OPAQUE); @@ -462,13 +268,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final int altCodeKeyWhileTypingFadeinAnimatorResId = mainKeyboardViewAttr.getResourceId( R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0); - final float keyHysteresisDistance = mainKeyboardViewAttr.getDimension( - R.styleable.MainKeyboardView_keyHysteresisDistance, 0.0f); - final float keyHysteresisDistanceForSlidingModifier = mainKeyboardViewAttr.getDimension( - R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0.0f); - mKeyDetector = new KeyDetector( - keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier); - mKeyTimerHandler = new KeyTimerHandler(this, mainKeyboardViewAttr); mKeyPreviewOffset = mainKeyboardViewAttr.getDimensionPixelOffset( R.styleable.MainKeyboardView_keyPreviewOffset, 0); mKeyPreviewHeight = mainKeyboardViewAttr.getDimensionPixelSize( @@ -480,6 +279,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (mKeyPreviewLayoutId == 0) { mShowKeyPreviewPopup = false; } + mKeyPreviewZoomInDuration = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyPreviewZoomInDuration, 0); + mKeyPreviewZoomOutDuration = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_keyPreviewZoomOutDuration, 0); final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId( R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0); mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean( @@ -487,7 +290,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureFloatingPreviewTextLingerTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gestureFloatingPreviewTextLingerTimeout, 0); - PointerTracker.setParameters(mainKeyboardViewAttr); mGestureFloatingPreviewText = new GestureFloatingPreviewText( mPreviewPlacerView, mainKeyboardViewAttr); @@ -513,8 +315,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER; - mLanguageOnSpacebarHorizontalMargin = - (int) getResources().getDimension(R.dimen.language_on_spacebar_horizontal_margin); + mLanguageOnSpacebarHorizontalMargin = (int)getResources().getDimension( + R.dimen.config_language_on_spacebar_horizontal_margin); } @Override @@ -536,6 +338,35 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return animator; } + private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel, + final ObjectAnimator animatorToStart) { + if (animatorToCancel == null || animatorToStart == null) { + // TODO: Stop using null as a no-operation animator. + return; + } + float startFraction = 0.0f; + if (animatorToCancel.isStarted()) { + animatorToCancel.cancel(); + startFraction = 1.0f - animatorToCancel.getAnimatedFraction(); + } + final long startTime = (long)(animatorToStart.getDuration() * startFraction); + animatorToStart.start(); + animatorToStart.setCurrentPlayTime(startTime); + } + + // Implements {@link TimerHander.Callbacks} method. + @Override + public void startWhileTypingFadeinAnimation() { + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeoutAnimator, mAltCodeKeyWhileTypingFadeinAnimator); + } + + @Override + public void startWhileTypingFadeoutAnimation() { + cancelAndStartAnimators( + mAltCodeKeyWhileTypingFadeinAnimator, mAltCodeKeyWhileTypingFadeoutAnimator); + } + @ExternallyReferenced public int getLanguageOnSpacebarAnimAlpha() { return mLanguageOnSpacebarAnimAlpha; @@ -573,28 +404,16 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack PointerTracker.setKeyboardActionListener(listener); } - /** - * Returns the {@link KeyboardActionListener} object. - * @return the listener attached to this keyboard - */ - @Override - public KeyboardActionListener getKeyboardActionListener() { - return mKeyboardActionListener; + // TODO: We should reconsider which coordinate system should be used to represent keyboard + // event. + public int getKeyX(final int x) { + return Constants.isValidCoordinate(x) ? mKeyDetector.getTouchX(x) : x; } - @Override - public KeyDetector getKeyDetector() { - return mKeyDetector; - } - - @Override - public DrawingProxy getDrawingProxy() { - return this; - } - - @Override - public TimerProxy getTimerProxy() { - return mKeyTimerHandler; + // TODO: We should reconsider which coordinate system should be used to represent keyboard + // event. + public int getKeyY(final int y) { + return Constants.isValidCoordinate(y) ? mKeyDetector.getTouchY(y) : y; } /** @@ -606,19 +425,20 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack */ @Override public void setKeyboard(final Keyboard keyboard) { - // Remove any pending messages, except dismissing preview and key repeat. - mKeyTimerHandler.cancelLongPressTimer(); + // Remove any pending messages. + mKeyTimerHandler.cancelAllKeyTimers(); super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); PointerTracker.setKeyDetector(mKeyDetector); + PointerTracker.setKeyboardActionListener(mKeyboardActionListener); mMoreKeysKeyboardCache.clear(); mSpaceKey = keyboard.getKey(Constants.CODE_SPACE); - mSpaceIcon = (mSpaceKey != null) + mSpacebarIcon = (mSpaceKey != null) ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null; final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; - mSpacebarTextSize = keyHeight * mSpacebarTextRatio; + mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio; if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { final int orientation = getContext().getResources().getConfiguration().orientation; ResearchLogger.mainKeyboardView_setKeyboard(keyboard, orientation); @@ -681,34 +501,33 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return mShowKeyPreviewPopup; } - private void addKeyPreview(final TextView keyPreview) { - locatePreviewPlacerView(); - mPreviewPlacerView.addView( - keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); - } - - private TextView getKeyPreviewText(final int pointerId) { - TextView previewText = mKeyPreviewTexts.get(pointerId); - if (previewText != null) { - return previewText; + private TextView getKeyPreviewTextView(final Key key) { + TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); + if (previewTextView != null) { + return previewTextView; + } + previewTextView = mFreeKeyPreviewTextViews.poll(); + if (previewTextView != null) { + return previewTextView; } final Context context = getContext(); if (mKeyPreviewLayoutId != 0) { - previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); + previewTextView = (TextView)LayoutInflater.from(context) + .inflate(mKeyPreviewLayoutId, null); } else { - previewText = new TextView(context); + previewTextView = new TextView(context); } - mKeyPreviewTexts.put(pointerId, previewText); - return previewText; + locatePreviewPlacerView(); + mPreviewPlacerView.addView( + previewTextView, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); + return previewTextView; } - private void dismissAllKeyPreviews() { - final int pointerCount = mKeyPreviewTexts.size(); - for (int id = 0; id < pointerCount; id++) { - final TextView previewText = mKeyPreviewTexts.get(id); - if (previewText != null) { - previewText.setVisibility(INVISIBLE); - } + // Implements {@link DrawingHandler.Callbacks} method. + @Override + public void dismissAllKeyPreviews() { + for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) { + dismissKeyPreviewWithoutDelay(key); } PointerTracker.setReleasedKeyGraphicsToAllKeys(); } @@ -734,24 +553,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private static final int STATE_NORMAL = 0; private static final int STATE_HAS_MOREKEYS = 1; + // TODO: Take this method out of this class. @Override - public void showKeyPreview(final PointerTracker tracker) { - final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; - final Keyboard keyboard = getKeyboard(); - if (!mShowKeyPreviewPopup) { - previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap; - return; - } - - final TextView previewText = getKeyPreviewText(tracker.mPointerId); - // If the key preview has no parent view yet, add it to the ViewGroup which can place - // key preview absolutely in SoftInputWindow. - if (previewText.getParent() == null) { - addKeyPreview(previewText); - } - - mDrawingHandler.cancelDismissKeyPreview(tracker); - final Key key = tracker.getKey(); + public void showKeyPreview(final Key key) { // If key is invalid or IME is already closed, we must not show key preview. // Trying to show key preview while root window is closed causes // WindowManager.BadTokenException. @@ -759,38 +563,47 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return; } + final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; + final Keyboard keyboard = getKeyboard(); + if (!mShowKeyPreviewPopup) { + previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap; + return; + } + + final TextView previewTextView = getKeyPreviewTextView(key); final KeyDrawParams drawParams = mKeyDrawParams; - previewText.setTextColor(drawParams.mPreviewTextColor); - final Drawable background = previewText.getBackground(); + previewTextView.setTextColor(drawParams.mPreviewTextColor); + final Drawable background = previewTextView.getBackground(); final String label = key.getPreviewLabel(); // What we show as preview should match what we show on a key top in onDraw(). if (label != null) { // TODO Should take care of temporaryShiftLabel here. - previewText.setCompoundDrawables(null, null, null, null); - previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, + previewTextView.setCompoundDrawables(null, null, null, null); + previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)); - previewText.setTypeface(key.selectPreviewTypeface(drawParams)); - previewText.setText(label); + previewTextView.setTypeface(key.selectPreviewTypeface(drawParams)); + previewTextView.setText(label); } else { - previewText.setCompoundDrawables(null, null, null, + previewTextView.setCompoundDrawables(null, null, null, key.getPreviewIcon(keyboard.mIconsSet)); - previewText.setText(null); + previewTextView.setText(null); } - previewText.measure( + previewTextView.measure( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); final int keyDrawWidth = key.getDrawWidth(); - final int previewWidth = previewText.getMeasuredWidth(); + final int previewWidth = previewTextView.getMeasuredWidth(); final int previewHeight = mKeyPreviewHeight; // The width and height of visible part of the key preview background. The content marker // of the background 9-patch have to cover the visible part of the background. - previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft() - - previewText.getPaddingRight(); - previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop() - - previewText.getPaddingBottom(); + previewParams.mPreviewVisibleWidth = previewWidth - previewTextView.getPaddingLeft() + - previewTextView.getPaddingRight(); + previewParams.mPreviewVisibleHeight = previewHeight - previewTextView.getPaddingTop() + - previewTextView.getPaddingBottom(); // The distance between the top edge of the parent key and the bottom of the visible part // of the key preview background. - previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom(); + previewParams.mPreviewVisibleOffset = + mKeyPreviewOffset - previewTextView.getPaddingBottom(); getLocationInWindow(mOriginCoords); // The key preview is horizontally aligned with the center of the visible part of the // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and @@ -817,13 +630,132 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); } ViewLayoutUtils.placeViewAt( - previewText, previewX, previewY, previewWidth, previewHeight); - previewText.setVisibility(VISIBLE); + previewTextView, previewX, previewY, previewWidth, previewHeight); + + if (!isHardwareAccelerated()) { + previewTextView.setVisibility(VISIBLE); + mShowingKeyPreviewTextViews.put(key, previewTextView); + return; + } + previewTextView.setPivotX(previewWidth / 2.0f); + previewTextView.setPivotY(previewHeight); + + final Animator zoomIn = createZoomInAniation(key, previewTextView); + final Animator zoomOut = createZoomOutAnimation(key, previewTextView); + final KeyPreviewAnimations animation = new KeyPreviewAnimations(zoomIn, zoomOut); + previewTextView.setTag(animation); + animation.startZoomIn(); } + // TODO: Move this internal class out to a separate external class. + private static class KeyPreviewAnimations extends AnimatorListenerAdapter { + private final Animator mZoomIn; + private final Animator mZoomOut; + + public KeyPreviewAnimations(final Animator zoomIn, final Animator zoomOut) { + mZoomIn = zoomIn; + mZoomOut = zoomOut; + } + + public void startZoomIn() { + mZoomIn.start(); + } + + public void startZoomOut() { + if (mZoomIn.isRunning()) { + mZoomIn.addListener(this); + return; + } + mZoomOut.start(); + } + + @Override + public void onAnimationEnd(final Animator animation) { + mZoomOut.start(); + } + } + + // TODO: Take this method out of this class. + private Animator createZoomInAniation(final Key key, final TextView previewTextView) { + final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( + previewTextView, SCALE_X, KEY_PREVIEW_START_ZOOM_IN_SCALE, + KEY_PREVIEW_END_ZOOM_IN_SCALE); + final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( + previewTextView, SCALE_Y, KEY_PREVIEW_START_ZOOM_IN_SCALE, + KEY_PREVIEW_END_ZOOM_IN_SCALE); + final AnimatorSet zoomInAnimation = new AnimatorSet(); + zoomInAnimation.play(scaleXAnimation).with(scaleYAnimation); + // TODO: Implement preference option to control key preview animation duration. + zoomInAnimation.setDuration(mKeyPreviewZoomInDuration); + zoomInAnimation.setInterpolator(DECELERATE_INTERPOLATOR); + zoomInAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(final Animator animation) { + previewTextView.setVisibility(VISIBLE); + mShowingKeyPreviewTextViews.put(key, previewTextView); + } + }); + return zoomInAnimation; + } + + // TODO: Take this method out of this class. + private Animator createZoomOutAnimation(final Key key, final TextView previewTextView) { + final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( + previewTextView, SCALE_X, KEY_PREVIEW_END_ZOOM_OUT_SCALE); + final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( + previewTextView, SCALE_Y, KEY_PREVIEW_END_ZOOM_OUT_SCALE); + final AnimatorSet zoomOutAnimation = new AnimatorSet(); + zoomOutAnimation.play(scaleXAnimation).with(scaleYAnimation); + // TODO: Implement preference option to control key preview animation duration. + zoomOutAnimation.setDuration(mKeyPreviewZoomOutDuration); + zoomOutAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); + zoomOutAnimation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(final Animator animation) { + dismissKeyPreviewWithoutDelay(key); + } + }); + return zoomOutAnimation; + } + + // Implements {@link TimerHandler.Callbacks} method. + // TODO: Take this method out of this class. @Override - public void dismissKeyPreview(final PointerTracker tracker) { - mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker); + public void dismissKeyPreviewWithoutDelay(final Key key) { + if (key == null) { + return; + } + final TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); + if (previewTextView != null) { + final Object tag = previewTextView.getTag(); + if (tag instanceof Animator) { + ((Animator)tag).cancel(); + } + previewTextView.setTag(null); + previewTextView.setVisibility(INVISIBLE); + mFreeKeyPreviewTextViews.add(previewTextView); + } + // To redraw key top letter. + invalidateKey(key); + } + + // TODO: Take this method out of this class. + @Override + public void dismissKeyPreview(final Key key) { + final TextView previewTextView = mShowingKeyPreviewTextViews.get(key); + if (previewTextView == null) { + return; + } + if (!isHardwareAccelerated()) { + // TODO: Implement preference option to control key preview method and duration. + mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, key); + return; + } + final Object tag = previewTextView.getTag(); + if (tag instanceof KeyPreviewAnimations) { + final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; + animation.startZoomOut(); + } } public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { @@ -847,6 +779,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mGestureTrailsPreview.setPreviewEnabled(isGestureTrailEnabled); } + // Implements {@link DrawingHandler.Callbacks} method. + @Override public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) { locatePreviewPlacerView(); mGestureFloatingPreviewText.setSuggetedWords(suggestedWords); @@ -922,11 +856,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return moreKeysKeyboardView; } + // Implements {@link TimerHandler.Callbacks} method. /** * Called when a key is long pressed. * @param tracker the pointer tracker which pressed the parent key */ - private void onLongPress(final PointerTracker tracker) { + @Override + public void onLongPress(final PointerTracker tracker) { if (isShowingMoreKeysPanel()) { return; } @@ -982,13 +918,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset; moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener); tracker.onShowMoreKeysPanel(moreKeysPanel); + // TODO: Implement zoom in animation of more keys panel. + dismissKeyPreviewWithoutDelay(key); } - public boolean isInSlidingKeyInput() { + public boolean isInDraggingFinger() { if (isShowingMoreKeysPanel()) { return true; } - return PointerTracker.isAnyInSlidingKeyInput(); + return PointerTracker.isAnyInDraggingFinger(); } @Override @@ -1049,10 +987,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (mNonDistinctMultitouchHelper != null) { if (me.getPointerCount() > 1 && mKeyTimerHandler.isInKeyRepeat()) { // Key repeating timer will be canceled if 2 or more keys are in action. - mKeyTimerHandler.cancelKeyRepeatTimer(); + mKeyTimerHandler.cancelKeyRepeatTimers(); } // Non distinct multitouch screen support - mNonDistinctMultitouchHelper.processMotionEvent(me, this); + mNonDistinctMultitouchHelper.processMotionEvent(me, mKeyDetector); return true; } return processMotionEvent(me); @@ -1069,8 +1007,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final int index = me.getActionIndex(); final int id = me.getPointerId(index); - final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); - tracker.processMotionEvent(me, this); + final PointerTracker tracker = PointerTracker.getPointerTracker(id); + // When a more keys panel is showing, we should ignore other fingers' single touch events + // other than the finger that is showing the more keys panel. + if (isShowingMoreKeysPanel() && !tracker.isShowingMoreKeysPanel() + && PointerTracker.getActivePointerTrackerCount() == 1) { + return true; + } + tracker.processMotionEvent(me, mKeyDetector); return true; } @@ -1099,7 +1043,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public boolean dispatchHoverEvent(final MotionEvent event) { if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - final PointerTracker tracker = PointerTracker.getPointerTracker(0, this); + final PointerTracker tracker = PointerTracker.getPointerTracker(0); return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker); } @@ -1169,12 +1113,30 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } + // Draw key background. + @Override + protected void onDrawKeyBackground(final Key key, final Canvas canvas, + final Drawable background) { + if (key.getCode() == Constants.CODE_SPACE) { + super.onDrawKeyBackground(key, canvas, mSpacebarBackground); + return; + } + super.onDrawKeyBackground(key, canvas, background); + } + @Override protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params) { if (key.altCodeWhileTyping() && key.isEnabled()) { params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; } + // Don't draw key top letter when key preview is showing. + if (FADE_OUT_KEY_TOP_LETTER_WHEN_KEY_IS_PRESSED + && mShowingKeyPreviewTextViews.containsKey(key)) { + // TODO: Fade out animation for the key top letter, and fade in animation for the key + // background color when the user presses the key. + return; + } final int code = key.getCode(); if (code == Constants.CODE_SPACE) { drawSpacebar(key, canvas, paint); @@ -1193,7 +1155,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) { final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2; paint.setTextScaleX(1.0f); - final float textWidth = TypefaceUtils.getLabelWidth(text, paint); + final float textWidth = TypefaceUtils.getStringWidth(text, paint); if (textWidth < width) { return true; } @@ -1204,7 +1166,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } paint.setTextScaleX(scaleX); - return TypefaceUtils.getLabelWidth(text, paint) < maxTextWidth; + return TypefaceUtils.getStringWidth(text, paint) < maxTextWidth; } // Layout language name on spacebar. @@ -1238,17 +1200,17 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (mNeedsToDisplayLanguage) { paint.setTextAlign(Align.CENTER); paint.setTypeface(Typeface.DEFAULT); - paint.setTextSize(mSpacebarTextSize); + paint.setTextSize(mLanguageOnSpacebarTextSize); final InputMethodSubtype subtype = getKeyboard().mId.mSubtype; final String language = layoutLanguageOnSpacebar(paint, subtype, width); // Draw language text with shadow final float descent = paint.descent(); final float textHeight = -paint.ascent() + descent; final float baseline = height / 2 + textHeight / 2; - paint.setColor(mSpacebarTextShadowColor); + paint.setColor(mLanguageOnSpacebarTextShadowColor); paint.setAlpha(mLanguageOnSpacebarAnimAlpha); canvas.drawText(language, width / 2, baseline - descent - 1, paint); - paint.setColor(mSpacebarTextColor); + paint.setColor(mLanguageOnSpacebarTextColor); paint.setAlpha(mLanguageOnSpacebarAnimAlpha); canvas.drawText(language, width / 2, baseline - descent, paint); } @@ -1260,12 +1222,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack int x = (width - iconWidth) / 2; int y = height - iconHeight; drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight); - } else if (mSpaceIcon != null) { - final int iconWidth = mSpaceIcon.getIntrinsicWidth(); - final int iconHeight = mSpaceIcon.getIntrinsicHeight(); + } else if (mSpacebarIcon != null) { + final int iconWidth = mSpacebarIcon.getIntrinsicWidth(); + final int iconHeight = mSpacebarIcon.getIntrinsicHeight(); int x = (width - iconWidth) / 2; int y = height - iconHeight; - drawIcon(canvas, mSpaceIcon, x, y, iconWidth, iconHeight); + drawIcon(canvas, mSpacebarIcon, x, y, iconWidth, iconHeight); } } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java index 6b76e2461..81b8f0428 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java @@ -28,7 +28,7 @@ public final class MoreKeysDetector extends KeyDetector { } @Override - public boolean alwaysAllowsSlidingInput() { + public boolean alwaysAllowsKeySelectionByDraggingFinger() { return true; } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index 8256d4623..670524380 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -223,7 +223,7 @@ public final class MoreKeysKeyboard extends Keyboard { } public int getDefaultKeyCoordX() { - return mLeftKeys * mColumnWidth; + return mLeftKeys * mColumnWidth + mLeftPadding; } public int getX(final int n, final int row) { @@ -298,7 +298,7 @@ public final class MoreKeysKeyboard extends Keyboard { height = keyPreviewDrawParams.mPreviewVisibleHeight + mParams.mVerticalGap; } else { final float padding = context.getResources().getDimension( - R.dimen.more_keys_keyboard_key_horizontal_padding) + R.dimen.config_more_keys_keyboard_key_horizontal_padding) + (parentKey.hasLabelsInMoreKeys() ? mParams.mDefaultKeyWidth * LABEL_PADDING_RATIO : 0.0f); width = getMaxKeyWidth(parentKey, mParams.mDefaultKeyWidth, padding, @@ -327,7 +327,7 @@ public final class MoreKeysKeyboard extends Keyboard { // If the label is single letter, minKeyWidth is enough to hold the label. if (label != null && StringUtils.codePointCount(label) > 1) { maxWidth = Math.max(maxWidth, - (int)(TypefaceUtils.getLabelWidth(label, paint) + padding)); + (int)(TypefaceUtils.getStringWidth(label, paint) + padding)); } } return maxWidth; diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 973128d36..a7c468538 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -52,7 +52,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel final Resources res = context.getResources(); mKeyDetector = new MoreKeysDetector( - res.getDimension(R.dimen.more_keys_keyboard_slide_allowance)); + res.getDimension(R.dimen.config_more_keys_keyboard_slide_allowance)); } @Override @@ -81,11 +81,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel mListener = listener; final View container = getContainerView(); // The coordinates of panel's left-top corner in parentView's coordinate system. - final int x = pointX - getDefaultCoordX() - container.getPaddingLeft(); - final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom(); + // We need to consider background drawable paddings. + final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft(); + final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom() + + getPaddingBottom(); parentView.getLocationInWindow(mCoordinates); - // Ensure the horizontal position of the panel does not extend past the screen edges. + // Ensure the horizontal position of the panel does not extend past the parentView edges. final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth(); final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates); final int panelY = y + CoordinateUtils.y(mCoordinates); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 52f190e77..f9e78bfd9 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -55,37 +55,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private static boolean sGestureHandlingEnabledByInputField = false; private static boolean sGestureHandlingEnabledByUser = false; - public interface KeyEventHandler { - /** - * Get KeyDetector object that is used for this PointerTracker. - * @return the KeyDetector object that is used for this PointerTracker - */ - public KeyDetector getKeyDetector(); - - /** - * Get KeyboardActionListener object that is used to register key code and so on. - * @return the KeyboardActionListner for this PointerTracke - */ - public KeyboardActionListener getKeyboardActionListener(); - - /** - * Get DrawingProxy object that is used for this PointerTracker. - * @return the DrawingProxy object that is used for this PointerTracker - */ - public DrawingProxy getDrawingProxy(); - - /** - * Get TimerProxy object that handles key repeat and long press timer event for this - * PointerTracker. - * @return the TimerProxy object that handles key repeat and long press timer event. - */ - public TimerProxy getTimerProxy(); - } - public interface DrawingProxy { public void invalidateKey(Key key); - public void showKeyPreview(PointerTracker tracker); - public void dismissKeyPreview(PointerTracker tracker); + public void showKeyPreview(Key key); + public void dismissKeyPreview(Key key); public void showSlidingKeyInputPreview(PointerTracker tracker); public void dismissSlidingKeyInputPreview(); public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText); @@ -94,13 +67,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public interface TimerProxy { public void startTypingStateTimer(Key typedKey); public boolean isTypingState(); - public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay); - public void startLongPressTimer(PointerTracker tracker, int delay); - public void cancelLongPressTimer(); + public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay); + public void startLongPressTimerOf(PointerTracker tracker, int delay); + public void cancelLongPressTimerOf(PointerTracker tracker); + public void cancelKeyTimersOf(PointerTracker tracker); public void startDoubleTapShiftKeyTimer(); public void cancelDoubleTapShiftKeyTimer(); public boolean isInDoubleTapShiftKeyTimeout(); - public void cancelKeyTimers(); public void startUpdateBatchInputTimer(PointerTracker tracker); public void cancelUpdateBatchInputTimer(PointerTracker tracker); public void cancelAllUpdateBatchInputTimers(); @@ -111,11 +84,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element { @Override public boolean isTypingState() { return false; } @Override - public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {} + public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay) {} + @Override + public void startLongPressTimerOf(PointerTracker tracker, int delay) {} @Override - public void startLongPressTimer(PointerTracker tracker, int delay) {} + public void cancelLongPressTimerOf(PointerTracker tracker) {} @Override - public void cancelLongPressTimer() {} + public void cancelKeyTimersOf(PointerTracker tracker) {} @Override public void startDoubleTapShiftKeyTimer() {} @Override @@ -123,8 +98,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element { @Override public boolean isInDoubleTapShiftKeyTimeout() { return false; } @Override - public void cancelKeyTimers() {} - @Override public void startUpdateBatchInputTimer(PointerTracker tracker) {} @Override public void cancelUpdateBatchInputTimer(PointerTracker tracker) {} @@ -134,7 +107,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } static final class PointerTrackerParams { - public final boolean mSlidingKeyInputEnabled; + public final boolean mKeySelectionByDraggingFinger; public final int mTouchNoiseThresholdTime; public final int mTouchNoiseThresholdDistance; public final int mSuppressKeyPreviewAfterBatchInputDuration; @@ -142,21 +115,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public final int mKeyRepeatInterval; public final int mLongPressShiftLockTimeout; - public static final PointerTrackerParams DEFAULT = new PointerTrackerParams(); - - private PointerTrackerParams() { - mSlidingKeyInputEnabled = false; - mTouchNoiseThresholdTime = 0; - mTouchNoiseThresholdDistance = 0; - mSuppressKeyPreviewAfterBatchInputDuration = 0; - mKeyRepeatStartTimeout = 0; - mKeyRepeatInterval = 0; - mLongPressShiftLockTimeout = 0; - } - public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) { - mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean( - R.styleable.MainKeyboardView_slidingKeyInputEnable, false); + mKeySelectionByDraggingFinger = mainKeyboardViewAttr.getBoolean( + R.styleable.MainKeyboardView_keySelectionByDraggingFinger, false); mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0); mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize( @@ -189,13 +150,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public final int mPointerId; - private DrawingProxy mDrawingProxy; - private TimerProxy mTimerProxy; + private static DrawingProxy sDrawingProxy; + private static TimerProxy sTimerProxy; + private static KeyDetector sDefaultKeyDetector; private KeyDetector mKeyDetector; - private KeyboardActionListener mListener = KeyboardActionListener.EMPTY_LISTENER; + private static KeyboardActionListener sListener = KeyboardActionListener.EMPTY_LISTENER; private Keyboard mKeyboard; - private int mPhantonSuddenMoveThreshold; + private int mPhantomSuddenMoveThreshold; private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector(); private boolean mIsDetectingGesture = false; // per PointerTracker. @@ -341,16 +303,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element { private MoreKeysPanel mMoreKeysPanel; private static final int MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT = 3; - // true if this pointer is in a sliding key input. - boolean mIsInSlidingKeyInput; - // true if this pointer is in a sliding key input from a modifier key, + // true if this pointer is in the dragging finger mode. + boolean mIsInDraggingFinger; + // true if this pointer is sliding from a modifier key and in the sliding key input mode, // so that further modifier keys should be ignored. - boolean mIsInSlidingKeyInputFromModifier; + boolean mIsInSlidingKeyInput; // if not a NOT_A_CODE, the key of this code is repeating private int mCurrentRepeatingKeyCode = Constants.NOT_A_CODE; - // true if a sliding key input is allowed. - private boolean mIsAllowedSlidingKeyInput; + // true if dragging finger is allowed. + private boolean mIsAllowedDraggingFinger; private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints; @@ -377,22 +339,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return needsTheHack; } - public static void init(final Resources res) { + // TODO: Add PointerTrackerFactory singleton and move some class static methods into it. + public static void init(final TypedArray mainKeyboardViewAttr, final TimerProxy timerProxy, + final DrawingProxy drawingProxy, final KeyDetector defaultKeyDetector) { + sParams = new PointerTrackerParams(mainKeyboardViewAttr); + sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr); + sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr); + sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams); + + final Resources res = mainKeyboardViewAttr.getResources(); sNeedsPhantomSuddenMoveEventHack = Boolean.parseBoolean( ResourceUtils.getDeviceOverrideValue( res, R.array.phantom_sudden_move_event_device_list)); sNeedsProximateBogusDownMoveUpEventHack = needsProximateBogusDownMoveUpEventHack(res); - sParams = PointerTrackerParams.DEFAULT; sGestureStrokeParams = GestureStrokeParams.DEFAULT; sGesturePreviewParams = GestureStrokePreviewParams.DEFAULT; sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams); - } - public static void setParameters(final TypedArray mainKeyboardViewAttr) { - sParams = new PointerTrackerParams(mainKeyboardViewAttr); - sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr); - sGesturePreviewParams = new GestureStrokePreviewParams(mainKeyboardViewAttr); - sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams); + sTimerProxy = timerProxy; + sDrawingProxy = drawingProxy; + sDefaultKeyDetector = defaultKeyDetector; } private static void updateGestureHandlingMode() { @@ -413,20 +379,20 @@ public final class PointerTracker implements PointerTrackerQueue.Element { updateGestureHandlingMode(); } - public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) { + public static PointerTracker getPointerTracker(final int id) { final ArrayList<PointerTracker> trackers = sTrackers; // Create pointer trackers until we can get 'id+1'-th tracker, if needed. for (int i = trackers.size(); i <= id; i++) { - final PointerTracker tracker = new PointerTracker(i, handler); + final PointerTracker tracker = new PointerTracker(i); trackers.add(tracker); } return trackers.get(id); } - public static boolean isAnyInSlidingKeyInput() { - return sPointerTrackerQueue.isAnyInSlidingKeyInput(); + public static boolean isAnyInDraggingFinger() { + return sPointerTrackerQueue.isAnyInDraggingFinger(); } public static void cancelAllPointerTrackers() { @@ -434,11 +400,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } public static void setKeyboardActionListener(final KeyboardActionListener listener) { - final int trackersSize = sTrackers.size(); - for (int i = 0; i < trackersSize; ++i) { - final PointerTracker tracker = sTrackers.get(i); - tracker.mListener = listener; - } + sListener = listener; } public static void setKeyDetector(final KeyDetector keyDetector) { @@ -446,8 +408,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element { for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); tracker.setKeyDetectorInner(keyDetector); - // Mark that keyboard layout has been changed. - tracker.mKeyboardLayoutHasBeenChanged = true; } final Keyboard keyboard = keyDetector.getKeyboard(); sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput(); @@ -458,7 +418,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - tracker.setReleasedKeyGraphics(tracker.mCurrentKey); + tracker.setReleasedKeyGraphics(tracker.getKey()); } } @@ -466,28 +426,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final int trackersSize = sTrackers.size(); for (int i = 0; i < trackersSize; ++i) { final PointerTracker tracker = sTrackers.get(i); - if (tracker.isShowingMoreKeysPanel()) { - tracker.mMoreKeysPanel.dismissMoreKeysPanel(); - tracker.mMoreKeysPanel = null; - } + tracker.dismissMoreKeysPanel(); } } - private PointerTracker(final int id, final KeyEventHandler handler) { - if (handler == null) { - throw new NullPointerException(); - } + private PointerTracker(final int id) { mPointerId = id; mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints( id, sGestureStrokeParams, sGesturePreviewParams); - setKeyEventHandler(handler); - } - - private void setKeyEventHandler(final KeyEventHandler handler) { - setKeyDetectorInner(handler.getKeyDetector()); - mListener = handler.getKeyboardActionListener(); - mDrawingProxy = handler.getDrawingProxy(); - mTimerProxy = handler.getTimerProxy(); + setKeyDetectorInner(sDefaultKeyDetector); } // Returns true if keyboard has been changed by this callback. @@ -500,7 +447,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) { return false; } - final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier(); + final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier(); if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onPress : %s%s%s%s", mPointerId, KeyDetector.printableCode(key), @@ -512,10 +459,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return false; } if (key.isEnabled()) { - mListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1); + sListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; mKeyboardLayoutHasBeenChanged = false; - mTimerProxy.startTypingStateTimer(key); + sTimerProxy.startTypingStateTimer(key); return keyboardLayoutHasBeenChanged; } return false; @@ -525,8 +472,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { // primaryCode is different from {@link Key#mCode}. private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x, final int y, final long eventTime) { - final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier(); - final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); + final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier(); + final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState(); final int code = altersCode ? key.getAltCode() : primaryCode; if (DEBUG_LISTENER) { final String output = code == Constants.CODE_OUTPUT_TEXT @@ -546,9 +493,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (key.isEnabled() || altersCode) { sTimeRecorder.onCodeInput(code, eventTime); if (code == Constants.CODE_OUTPUT_TEXT) { - mListener.onTextInput(key.getOutputText()); + sListener.onTextInput(key.getOutputText()); } else if (code != Constants.CODE_UNSPECIFIED) { - mListener.onCodeInput(code, x, y); + sListener.onCodeInput(code, x, y); } } } @@ -561,7 +508,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (sInGesture || mIsDetectingGesture || mIsTrackingForActionDisabled) { return; } - final boolean ignoreModifierKey = mIsInSlidingKeyInput && key.isModifier(); + final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier(); if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId, Constants.printableCode(primaryCode), @@ -576,7 +523,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return; } if (key.isEnabled()) { - mListener.onReleaseKey(primaryCode, withSliding); + sListener.onReleaseKey(primaryCode, withSliding); } } @@ -584,7 +531,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onFinishSlidingInput", mPointerId)); } - mListener.onFinishSlidingInput(); + sListener.onFinishSlidingInput(); } private void callListenerOnCancelInput() { @@ -594,7 +541,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.pointerTracker_callListenerOnCancelInput(); } - mListener.onCancelInput(); + sListener.onCancelInput(); } private void setKeyDetectorInner(final KeyDetector keyDetector) { @@ -604,23 +551,25 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } mKeyDetector = keyDetector; mKeyboard = keyDetector.getKeyboard(); + // Mark that keyboard layout has been changed. + mKeyboardLayoutHasBeenChanged = true; final int keyWidth = mKeyboard.mMostCommonKeyWidth; final int keyHeight = mKeyboard.mMostCommonKeyHeight; mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight); final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY); if (newKey != mCurrentKey) { - if (mDrawingProxy != null) { + if (sDrawingProxy != null) { setReleasedKeyGraphics(mCurrentKey); } // Keep {@link #mCurrentKey} that comes from previous keyboard. } - mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD); + mPhantomSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD); mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight); } @Override - public boolean isInSlidingKeyInput() { - return mIsInSlidingKeyInput; + public boolean isInDraggingFinger() { + return mIsInDraggingFinger; } public Key getKey() { @@ -637,7 +586,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private void setReleasedKeyGraphics(final Key key) { - mDrawingProxy.dismissKeyPreview(this); + sDrawingProxy.dismissKeyPreview(key); if (key == null) { return; } @@ -678,14 +627,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } // Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state. - final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState(); + final boolean altersCode = key.altCodeWhileTyping() && sTimerProxy.isTypingState(); final boolean needsToUpdateGraphics = key.isEnabled() || altersCode; if (!needsToUpdateGraphics) { return; } if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) { - mDrawingProxy.showKeyPreview(this); + sDrawingProxy.showKeyPreview(key); } updatePressKeyGraphics(key); @@ -697,7 +646,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) { + if (key.altCodeWhileTyping() && sTimerProxy.isTypingState()) { final int altCode = key.getAltCode(); final Key altKey = mKeyboard.getKey(altCode); if (altKey != null) { @@ -711,14 +660,14 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - private void updateReleaseKeyGraphics(final Key key) { + private static void updateReleaseKeyGraphics(final Key key) { key.onReleased(); - mDrawingProxy.invalidateKey(key); + sDrawingProxy.invalidateKey(key); } - private void updatePressKeyGraphics(final Key key) { + private static void updatePressKeyGraphics(final Key key) { key.onPressed(); - mDrawingProxy.invalidateKey(key); + sDrawingProxy.invalidateKey(key); } public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() { @@ -766,7 +715,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return newKey; } - private static int getActivePointerTrackerCount() { + /* package */ static int getActivePointerTrackerCount() { return sPointerTrackerQueue.size(); } @@ -789,12 +738,12 @@ public final class PointerTracker implements PointerTrackerQueue.Element { sAggregratedPointers.reset(); sLastRecognitionPointSize = 0; sLastRecognitionTime = 0; - mListener.onStartBatchInput(); + sListener.onStartBatchInput(); dismissAllMoreKeysPanels(); } - mTimerProxy.cancelLongPressTimer(); + sTimerProxy.cancelLongPressTimerOf(this); // A gesture floating preview text will be shown at the oldest pointer/finger on the screen. - mDrawingProxy.showGestureTrail( + sDrawingProxy.showGestureTrail( this, isOldestTrackerInQueue() /* showsFloatingPreviewText */); } @@ -812,7 +761,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return; } // A gesture floating preview text will be shown at the oldest pointer/finger on the screen. - mDrawingProxy.showGestureTrail( + sDrawingProxy.showGestureTrail( this, isOldestTrackerInQueue() /* showsFloatingPreviewText */); } @@ -827,8 +776,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId, size)); } - mTimerProxy.startUpdateBatchInputTimer(this); - mListener.onUpdateBatchInput(sAggregratedPointers); + sTimerProxy.startUpdateBatchInputTimer(this); + sListener.onUpdateBatchInput(sAggregratedPointers); // The listener may change the size of the pointers (when auto-committing // for example), so we need to get the size from the pointers again. sLastRecognitionPointSize = sAggregratedPointers.getPointerSize(); @@ -843,13 +792,13 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (getActivePointerTrackerCount() == 1) { sInGesture = false; sTimeRecorder.onEndBatchInput(eventTime); - mTimerProxy.cancelAllUpdateBatchInputTimers(); + sTimerProxy.cancelAllUpdateBatchInputTimers(); if (!mIsTrackingForActionDisabled) { if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d", mPointerId, sAggregratedPointers.getPointerSize())); } - mListener.onEndBatchInput(sAggregratedPointers); + sListener.onEndBatchInput(sAggregratedPointers); } } } @@ -857,7 +806,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return; } // A gesture floating preview text will be shown at the oldest pointer/finger on the screen. - mDrawingProxy.showGestureTrail( + sDrawingProxy.showGestureTrail( this, isOldestTrackerInQueue() /* showsFloatingPreviewText */); } @@ -871,19 +820,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_LISTENER) { Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId)); } - mListener.onCancelBatchInput(); + sListener.onCancelBatchInput(); } - public void processMotionEvent(final MotionEvent me, final KeyEventHandler handler) { + public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) { final int action = me.getActionMasked(); final long eventTime = me.getEventTime(); if (action == MotionEvent.ACTION_MOVE) { + // When this pointer is the only active pointer and is showing a more keys panel, + // we should ignore other pointers' motion event. + final boolean shouldIgnoreOtherPointers = + isShowingMoreKeysPanel() && getActivePointerTrackerCount() == 1; final int pointerCount = me.getPointerCount(); for (int index = 0; index < pointerCount; index++) { final int id = me.getPointerId(index); - final PointerTracker tracker = getPointerTracker(id, handler); + if (shouldIgnoreOtherPointers && id != mPointerId) { + continue; + } final int x = (int)me.getX(index); final int y = (int)me.getY(index); + final PointerTracker tracker = getPointerTracker(id); tracker.onMoveEvent(x, y, eventTime, me); } return; @@ -894,7 +850,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: - onDownEvent(x, y, eventTime, handler); + onDownEvent(x, y, eventTime, keyDetector); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: @@ -907,11 +863,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private void onDownEvent(final int x, final int y, final long eventTime, - final KeyEventHandler handler) { + final KeyDetector keyDetector) { if (DEBUG_EVENT) { printTouchEvent("onDownEvent:", x, y, eventTime); } - setKeyEventHandler(handler); + setKeyDetectorInner(keyDetector); // Naive up-to-down noise filter. final long deltaT = eventTime - mUpTime; if (deltaT < sParams.mTouchNoiseThresholdTime) { @@ -954,20 +910,29 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - private boolean isShowingMoreKeysPanel() { + /* package */ boolean isShowingMoreKeysPanel() { return (mMoreKeysPanel != null); } + private void dismissMoreKeysPanel() { + if (isShowingMoreKeysPanel()) { + mMoreKeysPanel.dismissMoreKeysPanel(); + mMoreKeysPanel = null; + } + } + private void onDownEventInternal(final int x, final int y, final long eventTime) { Key key = onDownKey(x, y, eventTime); - // Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding - // from modifier key, or 3) this pointer's KeyDetector always allows sliding input. - mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled + // Key selection by dragging finger is allowed when 1) key selection by dragging finger is + // enabled by configuration, 2) this pointer starts dragging from modifier key, or 3) this + // pointer's KeyDetector always allows key selection by dragging finger, such as + // {@link MoreKeysKeyboard}. + mIsAllowedDraggingFinger = sParams.mKeySelectionByDraggingFinger || (key != null && key.isModifier()) - || mKeyDetector.alwaysAllowsSlidingInput(); + || mKeyDetector.alwaysAllowsKeySelectionByDraggingFinger(); mKeyboardLayoutHasBeenChanged = false; mIsTrackingForActionDisabled = false; - resetSlidingKeyInput(); + resetKeySelectionByDraggingFinger(); if (key != null) { // This onPress call may have changed keyboard layout. Those cases are detected at // {@link #setKeyboard}. In those cases, we should update key according to the new @@ -982,17 +947,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - private void startSlidingKeyInput(final Key key) { - if (!mIsInSlidingKeyInput) { - mIsInSlidingKeyInputFromModifier = key.isModifier(); + private void startKeySelectionByDraggingFinger(final Key key) { + if (!mIsInDraggingFinger) { + mIsInSlidingKeyInput = key.isModifier(); } - mIsInSlidingKeyInput = true; + mIsInDraggingFinger = true; } - private void resetSlidingKeyInput() { + private void resetKeySelectionByDraggingFinger() { + mIsInDraggingFinger = false; mIsInSlidingKeyInput = false; - mIsInSlidingKeyInputFromModifier = false; - mDrawingProxy.dismissSlidingKeyInputPreview(); + sDrawingProxy.dismissSlidingKeyInputPreview(); } private void onGestureMoveEvent(final int x, final int y, final long eventTime, @@ -1003,7 +968,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard( x, y, gestureTime, isMajorEvent); if (mGestureStrokeWithPreviewPoints.getLength() > beforeLength) { - mTimerProxy.startUpdateBatchInputTimer(this); + sTimerProxy.startUpdateBatchInputTimer(this); } // If the move event goes out from valid batch input area, cancel batch input. if (!onValidArea) { @@ -1048,15 +1013,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element { final int translatedY = mMoreKeysPanel.translateY(y); mMoreKeysPanel.onMoveEvent(translatedX, translatedY, mPointerId, eventTime); onMoveKey(x, y); - if (mIsInSlidingKeyInputFromModifier) { - mDrawingProxy.showSlidingKeyInputPreview(this); + if (mIsInSlidingKeyInput) { + sDrawingProxy.showSlidingKeyInputPreview(this); } return; } onMoveEventInternal(x, y, eventTime); } - private void processSlidingKeyInput(final Key newKey, final int x, final int y, + private void processDraggingFingerInToNewKey(final Key newKey, final int x, final int y, final long eventTime) { // This onPress call may have changed keyboard layout. Those cases are detected // at {@link #setKeyboard}. In those cases, we should update key according @@ -1110,29 +1075,29 @@ public final class PointerTracker implements PointerTrackerQueue.Element { onDownEventInternal(x, y, eventTime); } - private void processSildeOutFromOldKey(final Key oldKey) { + private void processDraggingFingerOutFromOldKey(final Key oldKey) { setReleasedKeyGraphics(oldKey); callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */); - startSlidingKeyInput(oldKey); - mTimerProxy.cancelKeyTimers(); + startKeySelectionByDraggingFinger(oldKey); + sTimerProxy.cancelKeyTimersOf(this); } - private void slideFromOldKeyToNewKey(final Key key, final int x, final int y, + private void dragFingerFromOldKeyToNewKey(final Key key, final int x, final int y, final long eventTime, final Key oldKey, final int lastX, final int lastY) { // The pointer has been slid in to the new key from the previous key, we must call // onRelease() first to notify that the previous key has been released, then call // onPress() to notify that the new key is being pressed. - processSildeOutFromOldKey(oldKey); + processDraggingFingerOutFromOldKey(oldKey); startRepeatKey(key); - if (mIsAllowedSlidingKeyInput) { - processSlidingKeyInput(key, x, y, eventTime); + if (mIsAllowedDraggingFinger) { + processDraggingFingerInToNewKey(key, x, y, eventTime); } // HACK: On some devices, quick successive touches may be reported as a sudden move by // touch panel firmware. This hack detects such cases and translates the move event to // successive up and down events. // TODO: Should find a way to balance gesture detection and this hack. else if (sNeedsPhantomSuddenMoveEventHack - && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) { + && getDistance(x, y, lastX, lastY) >= mPhantomSuddenMoveThreshold) { processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY); } // HACK: On some devices, quick successive proximate touches may be reported as a bogus @@ -1163,11 +1128,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } } - private void slideOutFromOldKey(final Key oldKey, final int x, final int y) { + private void dragFingerOutFromOldKey(final Key oldKey, final int x, final int y) { // The pointer has been slid out from the previous key, we must call onRelease() to // notify that the previous key has been released. - processSildeOutFromOldKey(oldKey); - if (mIsAllowedSlidingKeyInput) { + processDraggingFingerOutFromOldKey(oldKey); + if (mIsAllowedDraggingFinger) { onMoveToNewKey(null, x, y); } else { if (!mIsDetectingGesture) { @@ -1194,19 +1159,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (newKey != null) { if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { - slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY); + dragFingerFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY); } else if (oldKey == null) { // The pointer has been slid in to the new key, but the finger was not on any keys. // In this case, we must call onPress() to notify that the new key is being pressed. - processSlidingKeyInput(newKey, x, y, eventTime); + processDraggingFingerInToNewKey(newKey, x, y, eventTime); } } else { // newKey == null if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) { - slideOutFromOldKey(oldKey, x, y); + dragFingerOutFromOldKey(oldKey, x, y); } } - if (mIsInSlidingKeyInputFromModifier) { - mDrawingProxy.showSlidingKeyInputPreview(this); + if (mIsInSlidingKeyInput) { + sDrawingProxy.showSlidingKeyInputPreview(this); } } @@ -1215,7 +1180,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { printTouchEvent("onUpEvent :", x, y, eventTime); } - mTimerProxy.cancelUpdateBatchInputTimer(this); + sTimerProxy.cancelUpdateBatchInputTimer(this); if (!sInGesture) { if (mCurrentKey != null && mCurrentKey.isModifier()) { // Before processing an up event of modifier key, all pointers already being @@ -1237,18 +1202,15 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (DEBUG_EVENT) { printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime); } - if (isShowingMoreKeysPanel()) { - return; - } onUpEventInternal(mLastX, mLastY, eventTime); cancelTrackingForAction(); } private void onUpEventInternal(final int x, final int y, final long eventTime) { - mTimerProxy.cancelKeyTimers(); + sTimerProxy.cancelKeyTimersOf(this); + final boolean isInDraggingFinger = mIsInDraggingFinger; final boolean isInSlidingKeyInput = mIsInSlidingKeyInput; - final boolean isInSlidingKeyInputFromModifier = mIsInSlidingKeyInputFromModifier; - resetSlidingKeyInput(); + resetKeySelectionByDraggingFinger(); mIsDetectingGesture = false; final Key currentKey = mCurrentKey; mCurrentKey = null; @@ -1280,11 +1242,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element { return; } if (currentKey != null && currentKey.isRepeatable() - && (currentKey.getCode() == currentRepeatingKeyCode) && !isInSlidingKeyInput) { + && (currentKey.getCode() == currentRepeatingKeyCode) && !isInDraggingFinger) { return; } detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime); - if (isInSlidingKeyInputFromModifier) { + if (isInSlidingKeyInput) { callListenerOnFinishSlidingInput(); } } @@ -1306,7 +1268,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } public void onLongPressed() { - resetSlidingKeyInput(); + resetKeySelectionByDraggingFinger(); cancelTrackingForAction(); setReleasedKeyGraphics(mCurrentKey); sPointerTrackerQueue.remove(this); @@ -1324,9 +1286,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private void onCancelEventInternal() { - mTimerProxy.cancelKeyTimers(); + sTimerProxy.cancelKeyTimersOf(this); setReleasedKeyGraphics(mCurrentKey); - resetSlidingKeyInput(); + resetKeySelectionByDraggingFinger(); if (isShowingMoreKeysPanel()) { mMoreKeysPanel.dismissMoreKeysPanel(); mMoreKeysPanel = null; @@ -1347,7 +1309,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } // Here curKey points to the different key from newKey. final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared( - mIsInSlidingKeyInputFromModifier); + mIsInSlidingKeyInput); final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y); if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) { if (DEBUG_MODE) { @@ -1358,7 +1320,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } return true; } - if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput + if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedDraggingFinger && sTimeRecorder.isInFastTyping(eventTime) && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) { if (DEBUG_MODE) { @@ -1380,26 +1342,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (key == null) return; if (!key.isLongPressEnabled()) return; // Caveat: Please note that isLongPressEnabled() can be true even if the current key - // doesn't have its more keys. (e.g. spacebar, globe key) + // doesn't have its more keys. (e.g. spacebar, globe key) If we are in the dragging finger + // mode, we will disable long press timer of such key. // We always need to start the long press timer if the key has its more keys regardless of - // whether or not we are in the sliding input mode. - if (mIsInSlidingKeyInput && key.getMoreKeys() == null) return; - final int delay; - switch (key.getCode()) { - case Constants.CODE_SHIFT: - delay = sParams.mLongPressShiftLockTimeout; - break; - default: - final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout; - if (mIsInSlidingKeyInputFromModifier) { - // We use longer timeout for sliding finger input started from the modifier key. - delay = longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT; - } else { - delay = longpressTimeout; - } - break; + // whether or not we are in the dragging finger mode. + if (mIsInDraggingFinger && key.getMoreKeys() == null) return; + + final int delay = getLongPressTimeout(key.getCode()); + sTimerProxy.startLongPressTimerOf(this, delay); + } + + private int getLongPressTimeout(final int code) { + if (code == Constants.CODE_SHIFT) { + return sParams.mLongPressShiftLockTimeout; + } + final int longpressTimeout = Settings.getInstance().getCurrent().mKeyLongpressTimeout; + if (mIsInSlidingKeyInput) { + // We use longer timeout for sliding finger input started from the modifier key. + return longpressTimeout * MULTIPLIER_FOR_LONG_PRESS_TIMEOUT_IN_SLIDING_INPUT; } - mTimerProxy.startLongPressTimer(this, delay); + return longpressTimeout; } private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) { @@ -1417,10 +1379,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element { if (sInGesture) return; if (key == null) return; if (!key.isRepeatable()) return; - // Don't start key repeat when we are in sliding input mode. - if (mIsInSlidingKeyInput) return; + // Don't start key repeat when we are in the dragging finger mode. + if (mIsInDraggingFinger) return; final int startRepeatCount = 1; - mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout); + startKeyRepeatTimer(startRepeatCount); } public void onKeyRepeat(final int code, final int repeatCount) { @@ -1432,11 +1394,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element { mCurrentRepeatingKeyCode = code; mIsDetectingGesture = false; final int nextRepeatCount = repeatCount + 1; - mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval); + startKeyRepeatTimer(nextRepeatCount); callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount); callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis()); } + private void startKeyRepeatTimer(final int repeatCount) { + final int delay = + (repeatCount == 1) ? sParams.mKeyRepeatStartTimeout : sParams.mKeyRepeatInterval; + sTimerProxy.startKeyRepeatTimerOf(this, repeatCount, delay); + } + private void printTouchEvent(final String title, final int x, final int y, final long eventTime) { final Key key = mKeyDetector.detectHitKey(x, y); diff --git a/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java b/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java new file mode 100644 index 000000000..f5cc45ca7 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/CustomViewPager.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; + +/** + * Custom view pager to prevent {@link ViewPager} from crashing while handling multi-touch + * event. + */ +public class CustomViewPager extends ViewPager { + private static final String TAG = CustomViewPager.class.getSimpleName(); + + public CustomViewPager(final Context context, final AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onInterceptTouchEvent(final MotionEvent event) { + // This only happens when you multi-touch, take the first finger off and move. + // Unfortunately this causes {@link ViewPager} to crash, so we will ignore such events. + if (event.getAction() == MotionEvent.ACTION_MOVE && event.getPointerId(0) != 0) { + Log.w(TAG, "Ignored multi-touch move event to prevent ViewPager from crashing"); + return false; + } + + return super.onInterceptTouchEvent(event); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java new file mode 100644 index 000000000..df82becae --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java @@ -0,0 +1,77 @@ +/* + * 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.internal; + +import android.os.Message; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; + +// TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so. +public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { + public interface Callbacks { + public void dismissKeyPreviewWithoutDelay(Key key); + public void dismissAllKeyPreviews(); + public void showGestureFloatingPreviewText(SuggestedWords suggestedWords); + } + + private static final int MSG_DISMISS_KEY_PREVIEW = 0; + private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; + + public DrawingHandler(final Callbacks ownerInstance) { + super(ownerInstance); + } + + @Override + public void handleMessage(final Message msg) { + final Callbacks callbacks = getOwnerInstance(); + if (callbacks == null) { + return; + } + switch (msg.what) { + case MSG_DISMISS_KEY_PREVIEW: + callbacks.dismissKeyPreviewWithoutDelay((Key)msg.obj); + break; + case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: + callbacks.showGestureFloatingPreviewText(SuggestedWords.EMPTY); + break; + } + } + + public void dismissKeyPreview(final long delay, final Key key) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); + } + + private void cancelAllDismissKeyPreviews() { + removeMessages(MSG_DISMISS_KEY_PREVIEW); + final Callbacks callbacks = getOwnerInstance(); + if (callbacks == null) { + return; + } + callbacks.dismissAllKeyPreviews(); + } + + public void dismissGestureFloatingPreviewText(final long delay) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT), delay); + } + + public void cancelAllMessages() { + cancelAllDismissKeyPreviews(); + } +} diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java index 3133e54be..e2fd39017 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java @@ -25,7 +25,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.JsonUtils; import java.util.ArrayDeque; import java.util.ArrayList; @@ -53,7 +53,7 @@ public class DynamicGridKeyboard extends Keyboard { private Key[] mCachedGridKeys; public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard, - final int maxKeyCount, final int categoryId, final int categoryPageId) { + final int maxKeyCount, final int categoryId) { super(templateKeyboard); final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0); final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1); @@ -124,7 +124,7 @@ public class DynamicGridKeyboard extends Keyboard { final int keyY0 = getKeyY0(index); final int keyX1 = getKeyX1(index); final int keyY1 = getKeyY1(index); - gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1); + gridKey.updateCoordinates(keyX0, keyY0, keyX1, keyY1); index++; } } @@ -139,36 +139,48 @@ public class DynamicGridKeyboard extends Keyboard { keys.add(key.getCode()); } } - final String jsonStr = StringUtils.listToJsonStr(keys); + final String jsonStr = JsonUtils.listToJsonStr(keys); Settings.writeEmojiRecentKeys(mPrefs, jsonStr); } - private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) { + private static Key getKeyByCode(final Collection<DynamicGridKeyboard> keyboards, + final int code) { + for (final DynamicGridKeyboard keyboard : keyboards) { + final Key key = keyboard.getKey(code); + if (key != null) { + return key; + } + } + return null; + } + + private static Key getKeyByOutputText(final Collection<DynamicGridKeyboard> keyboards, + final String outputText) { for (final DynamicGridKeyboard kbd : keyboards) { - if (o instanceof Integer) { - final int code = (Integer) o; - final Key key = kbd.getKey(code); - if (key != null) { - 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); + final Key key = kbd.getKeyFromOutputText(outputText); + if (key != null) { + return key; } } return null; } - public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) { + public void loadRecentKeys(final Collection<DynamicGridKeyboard> keyboards) { final String str = Settings.readEmojiRecentKeys(mPrefs); - final List<Object> keys = StringUtils.jsonStrToList(str); + final List<Object> keys = JsonUtils.jsonStrToList(str); for (final Object o : keys) { - addKeyLast(getKey(keyboards, o)); + final Key key; + if (o instanceof Integer) { + final int code = (Integer)o; + key = getKeyByCode(keyboards, code); + } else if (o instanceof String) { + final String outputText = (String)o; + key = getKeyByOutputText(keyboards, outputText); + } else { + Log.w(TAG, "Invalid object: " + o); + continue; + } + addKeyLast(key); } } @@ -217,7 +229,7 @@ public class DynamicGridKeyboard extends Keyboard { super(originalKey); } - public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) { + public void updateCoordinates(final int x0, final int y0, final int x1, final int y1) { mCurrentX = x0; mCurrentY = y0; getHitBox().set(x0, y0, x1, y1); diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java index 967448c28..12e063261 100644 --- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiLayoutParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.keyboard; +package com.android.inputmethod.keyboard.internal; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.ResourceUtils; @@ -37,22 +37,22 @@ public class EmojiLayoutParams { private final int mBottomPadding; private final int mTopPadding; - public EmojiLayoutParams(Resources res) { + public EmojiLayoutParams(final Resources res) { final int defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - mKeyVerticalGap = (int) res.getFraction(R.fraction.key_bottom_gap_holo, - (int) defaultKeyboardHeight, (int) defaultKeyboardHeight); - mBottomPadding = (int) res.getFraction(R.fraction.keyboard_bottom_padding_holo, - (int) defaultKeyboardHeight, (int) defaultKeyboardHeight); - mTopPadding = (int) res.getFraction(R.fraction.keyboard_top_padding_holo, - (int) defaultKeyboardHeight, (int) defaultKeyboardHeight); - mKeyHorizontalGap = (int) (res.getFraction(R.fraction.key_horizontal_gap_holo, + mKeyVerticalGap = (int) res.getFraction(R.fraction.config_key_vertical_gap_holo, + defaultKeyboardHeight, defaultKeyboardHeight); + mBottomPadding = (int) res.getFraction(R.fraction.config_keyboard_bottom_padding_holo, + defaultKeyboardHeight, defaultKeyboardHeight); + mTopPadding = (int) res.getFraction(R.fraction.config_keyboard_top_padding_holo, + defaultKeyboardHeight, defaultKeyboardHeight); + mKeyHorizontalGap = (int) (res.getFraction(R.fraction.config_key_horizontal_gap_holo, defaultKeyboardWidth, defaultKeyboardWidth)); mEmojiCategoryPageIdViewHeight = - (int) (res.getDimension(R.dimen.emoji_category_page_id_height)); + (int) (res.getDimension(R.dimen.config_emoji_category_page_id_height)); final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding + mKeyVerticalGap; - mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS + mEmojiActionBarHeight = baseheight / DEFAULT_KEYBOARD_ROWS - (mKeyVerticalGap - mBottomPadding) / 2; mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight - mEmojiCategoryPageIdViewHeight; @@ -60,26 +60,26 @@ public class EmojiLayoutParams { mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1; } - public void setPagerProperties(ViewPager vp) { + public void setPagerProperties(final ViewPager vp) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams(); lp.height = mEmojiKeyboardHeight; lp.bottomMargin = mEmojiPagerBottomMargin; vp.setLayoutParams(lp); } - public void setCategoryPageIdViewProperties(LinearLayout ll) { + public void setCategoryPageIdViewProperties(final LinearLayout ll) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.height = mEmojiCategoryPageIdViewHeight; ll.setLayoutParams(lp); } - public void setActionBarProperties(LinearLayout ll) { + public void setActionBarProperties(final LinearLayout ll) { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams(); lp.height = mEmojiActionBarHeight - mBottomPadding; ll.setLayoutParams(lp); } - public void setKeyProperties(ImageView ib) { + public void setKeyProperties(final 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/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java index 9cf68d43d..5a996ff53 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java @@ -20,25 +20,21 @@ import android.content.Context; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; -import android.widget.ScrollView; -import android.widget.Scroller; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.latin.R; /** - * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard. + * This is an extended {@link KeyboardView} class that hosts an emoji page keyboard. * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support. - * TODO: Vertical scroll capability should be removed from this class because it's no longer used. */ // TODO: Implement key popup preview. -public final class ScrollKeyboardView extends KeyboardView implements - ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener { - private static final boolean PAGINATION = false; - +public final class EmojiPageKeyboardView extends KeyboardView implements + GestureDetector.OnGestureListener { public interface OnKeyClickListener { public void onKeyClick(Key key); } @@ -52,63 +48,15 @@ public final class ScrollKeyboardView extends KeyboardView implements private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */); private final GestureDetector mGestureDetector; - private final Scroller mScroller; - private ScrollViewWithNotifier mScrollView; - - public ScrollKeyboardView(final Context context, final AttributeSet attrs) { + public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) { this(context, attrs, R.attr.keyboardViewStyle); } - public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { + public EmojiPageKeyboardView(final Context context, final AttributeSet attrs, + final int defStyle) { super(context, attrs, defStyle); mGestureDetector = new GestureDetector(context, this); mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */); - mScroller = new Scroller(context); - } - - public void setScrollView(final ScrollViewWithNotifier scrollView) { - mScrollView = scrollView; - scrollView.setScrollListener(this); - } - - private final Runnable mScrollTask = new Runnable() { - @Override - public void run() { - final Scroller scroller = mScroller; - final ScrollView scrollView = mScrollView; - scroller.computeScrollOffset(); - scrollView.scrollTo(0, scroller.getCurrY()); - if (!scroller.isFinished()) { - scrollView.post(this); - } - } - }; - - // {@link ScrollViewWithNotified#ScrollListener} methods. - @Override - public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX, - final int oldY) { - if (PAGINATION) { - mScroller.forceFinished(true /* finished */); - mScrollView.removeCallbacks(mScrollTask); - final int currentTop = mScrollView.getScrollY(); - final int pageHeight = getKeyboard().mBaseHeight; - final int lastPageNo = currentTop / pageHeight; - final int lastPageTop = lastPageNo * pageHeight; - final int nextPageNo = lastPageNo + 1; - final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight); - final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop) - ? lastPageTop : nextPageTop; - final int deltaY = scrollTo - currentTop; - mScroller.startScroll(0, currentTop, 0, deltaY, 300); - mScrollView.post(mScrollTask); - } - } - - @Override - public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX, - final boolean clampedY) { - releaseCurrentKey(); } public void setOnKeyClickListener(final OnKeyClickListener listener) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java index 19e995548..8b413e5e2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GestureTrailsPreview.java @@ -31,7 +31,7 @@ import android.view.View; import com.android.inputmethod.keyboard.PointerTracker; import com.android.inputmethod.keyboard.internal.GestureTrail.Params; import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; /** * Draw gesture trail preview graphics during gesture. @@ -52,21 +52,23 @@ public final class GestureTrailsPreview extends AbstractDrawingPreview { private final DrawingHandler mDrawingHandler; private static final class DrawingHandler - extends StaticInnerHandlerWrapper<GestureTrailsPreview> { + extends LeakGuardHandlerWrapper<GestureTrailsPreview> { private static final int MSG_UPDATE_GESTURE_TRAIL = 0; private final Params mGestureTrailParams; - public DrawingHandler(final GestureTrailsPreview outerInstance, + public DrawingHandler(final GestureTrailsPreview ownerInstance, final Params gestureTrailParams) { - super(outerInstance); + super(ownerInstance); mGestureTrailParams = gestureTrailParams; } @Override public void handleMessage(final Message msg) { - final GestureTrailsPreview preview = getOuterInstance(); - if (preview == null) return; + final GestureTrailsPreview preview = getOwnerInstance(); + if (preview == null) { + return; + } switch (msg.what) { case MSG_UPDATE_GESTURE_TRAIL: preview.getDrawingView().invalidate(); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 22f5b3dd1..accfaedcb 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -78,10 +78,10 @@ public final class KeySpecParser { * or has no key specifications. */ public static String[] splitKeySpecs(final String text) { - final int size = text.length(); - if (size == 0) { + if (TextUtils.isEmpty(text)) { return null; } + final int size = text.length(); // Optimization for one-letter key specification. if (size == 1) { return text.charAt(0) == COMMA ? null : new String[] { text }; @@ -380,6 +380,9 @@ public final class KeySpecParser { public static String resolveTextReference(final String rawText, final KeyboardTextsSet textsSet) { + if (TextUtils.isEmpty(rawText)) { + return null; + } int level = 0; String text = rawText; StringBuilder sb; @@ -392,7 +395,7 @@ public final class KeySpecParser { final int prefixLen = PREFIX_TEXT.length(); final int size = text.length(); if (size < prefixLen) { - return text; + return TextUtils.isEmpty(text) ? null : text; } sb = null; @@ -421,7 +424,7 @@ public final class KeySpecParser { text = sb.toString(); } } while (sb != null); - return text; + return TextUtils.isEmpty(text) ? null : text; } private static int searchTextNameEnd(final String text, final int start) { @@ -483,7 +486,7 @@ public final class KeySpecParser { public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, final Locale locale) { if (!Constants.isLetterCode(code) || !needsToUpperCase) return code; - final String text = new String(new int[] { code } , 0, 1); + final String text = StringUtils.newSingleCodePointString(code); final String casedText = KeySpecParser.toUpperCaseOfStringForLocale( text, needsToUpperCase, locale); return StringUtils.codePointCount(casedText) == 1 diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java index 05d855e31..f7e43a6c2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java @@ -27,6 +27,7 @@ import com.android.inputmethod.latin.utils.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import java.util.Arrays; import java.util.HashMap; public final class KeyStylesSet { @@ -90,7 +91,8 @@ public final class KeyStylesSet { } final Object value = mStyleAttributes.get(index); if (value != null) { - return (String[])value; + final String[] array = (String[])value; + return Arrays.copyOf(array, array.length); } final KeyStyle parentStyle = mStyles.get(mParentStyleName); return parentStyle.getStringArray(a, index); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java index 8bdad364c..c3e0aa685 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java @@ -47,6 +47,8 @@ public final class KeyVisualAttributes { public final int mShiftedLetterHintActivatedColor; public final int mPreviewTextColor; + public final float mHintLabelVerticalAdjustment; + private static final int[] VISUAL_ATTRIBUTE_IDS = { R.styleable.Keyboard_Key_keyTypeface, R.styleable.Keyboard_Key_keyLetterSize, @@ -65,6 +67,7 @@ public final class KeyVisualAttributes { R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, R.styleable.Keyboard_Key_keyPreviewTextColor, + R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, }; private static final SparseIntArray sVisualAttributeIds = new SparseIntArray(); private static final int ATTR_DEFINED = 1; @@ -127,5 +130,8 @@ public final class KeyVisualAttributes { mShiftedLetterHintActivatedColor = keyAttr.getColor( R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0); mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0); + + mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr, + R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, 0.0f); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index c1ae65695..b31358f3c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -459,7 +459,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final int x = (int)row.getKeyX(null); final int y = row.getKeyY(); final Key key = new Key(mParams, label, null /* hintLabel */, 0 /* iconId */, - code, outputText, x, y, (int)keyWidth, (int)row.getRowHeight(), + code, outputText, x, y, (int)keyWidth, row.getRowHeight(), row.getDefaultKeyLabelFlags(), row.getDefaultBackgroundType()); endKey(key); row.advanceXPos(keyWidth); @@ -649,10 +649,9 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); - final boolean shortcutKeyEnabledMatched = matchBoolean(caseAttr, - R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled); - final boolean shortcutKeyOnSymbolsMatched = matchBoolean(caseAttr, - R.styleable.Keyboard_Case_shortcutKeyOnSymbols, id.mShortcutKeyOnSymbols); + final boolean supportsSwitchingToShortcutImeMatched = matchBoolean(caseAttr, + R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, + id.mSupportsSwitchingToShortcutIme); final boolean hasShortcutKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr, @@ -671,13 +670,12 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched && modeMatched && navigateNextMatched && navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched - && shortcutKeyEnabledMatched && shortcutKeyOnSymbolsMatched - && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched - && isMultiLineMatched && imeActionMatched && localeCodeMatched - && languageCodeMatched && countryCodeMatched; + && supportsSwitchingToShortcutImeMatched && hasShortcutKeyMatched + && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched + && localeCodeMatched && languageCodeMatched && countryCodeMatched; if (DEBUG) { - startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, + startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, textAttr(caseAttr.getString( R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"), textAttr(caseAttr.getString( @@ -694,10 +692,9 @@ public class KeyboardBuilder<KP extends KeyboardParams> { "clobberSettingsKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), - booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyEnabled, - "shortcutKeyEnabled"), - booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyOnSymbols, - "shortcutKeyOnSymbols"), + booleanAttr( + caseAttr, R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, + "supportsSwitchingToShortcutIme"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 336db186e..0ee935f60 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -48,7 +48,6 @@ public final class KeyboardIconsSet { "search_key", R.styleable.Keyboard_iconSearchKey, "tab_key", R.styleable.Keyboard_iconTabKey, "shortcut_key", R.styleable.Keyboard_iconShortcutKey, - "shortcut_for_label", R.styleable.Keyboard_iconShortcutForLabel, "space_key_for_number_layout", R.styleable.Keyboard_iconSpaceKeyForNumberLayout, "shift_key_shifted", R.styleable.Keyboard_iconShiftKeyShifted, "shortcut_key_disabled", R.styleable.Keyboard_iconShortcutKeyDisabled, diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index c2a01b5e8..1b722c5ce 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -87,11 +87,11 @@ public final class KeyboardTextsSet { return (text == null) ? LANGUAGE_DEFAULT[id] : text; } + // These texts' name should be aligned with the @string/<name> in + // values*/strings-action-keys.xml. private static final String[] RESOURCE_NAMES = { - // These texts' name should be aligned with the @string/<name> in values/strings.xml. // Labels for action. "label_go_key", - // "label_search_key", "label_send_key", "label_next_key", "label_done_key", @@ -147,111 +147,118 @@ public final class KeyboardTextsSet { /* 42 */ "keylabel_for_south_slavic_row3_8", /* 43 */ "more_keys_for_cyrillic_ie", /* 44 */ "more_keys_for_cyrillic_i", - /* 45 */ "label_to_alpha_key", - /* 46 */ "single_quotes", - /* 47 */ "double_quotes", - /* 48 */ "single_angle_quotes", - /* 49 */ "double_angle_quotes", - /* 50 */ "more_keys_for_currency_dollar", - /* 51 */ "keylabel_for_currency", - /* 52 */ "more_keys_for_currency", - /* 53 */ "more_keys_for_punctuation", - /* 54 */ "more_keys_for_star", - /* 55 */ "more_keys_for_bullet", - /* 56 */ "more_keys_for_plus", - /* 57 */ "more_keys_for_left_parenthesis", - /* 58 */ "more_keys_for_right_parenthesis", - /* 59 */ "more_keys_for_less_than", - /* 60 */ "more_keys_for_greater_than", - /* 61 */ "more_keys_for_arabic_diacritics", - /* 62 */ "keyhintlabel_for_arabic_diacritics", - /* 63 */ "keylabel_for_symbols_1", - /* 64 */ "keylabel_for_symbols_2", - /* 65 */ "keylabel_for_symbols_3", - /* 66 */ "keylabel_for_symbols_4", - /* 67 */ "keylabel_for_symbols_5", - /* 68 */ "keylabel_for_symbols_6", - /* 69 */ "keylabel_for_symbols_7", - /* 70 */ "keylabel_for_symbols_8", - /* 71 */ "keylabel_for_symbols_9", - /* 72 */ "keylabel_for_symbols_0", - /* 73 */ "label_to_symbol_key", - /* 74 */ "label_to_symbol_with_microphone_key", - /* 75 */ "additional_more_keys_for_symbols_1", - /* 76 */ "additional_more_keys_for_symbols_2", - /* 77 */ "additional_more_keys_for_symbols_3", - /* 78 */ "additional_more_keys_for_symbols_4", - /* 79 */ "additional_more_keys_for_symbols_5", - /* 80 */ "additional_more_keys_for_symbols_6", - /* 81 */ "additional_more_keys_for_symbols_7", - /* 82 */ "additional_more_keys_for_symbols_8", - /* 83 */ "additional_more_keys_for_symbols_9", - /* 84 */ "additional_more_keys_for_symbols_0", - /* 85 */ "more_keys_for_symbols_1", - /* 86 */ "more_keys_for_symbols_2", - /* 87 */ "more_keys_for_symbols_3", - /* 88 */ "more_keys_for_symbols_4", - /* 89 */ "more_keys_for_symbols_5", - /* 90 */ "more_keys_for_symbols_6", - /* 91 */ "more_keys_for_symbols_7", - /* 92 */ "more_keys_for_symbols_8", - /* 93 */ "more_keys_for_symbols_9", - /* 94 */ "more_keys_for_symbols_0", - /* 95 */ "keylabel_for_comma", - /* 96 */ "more_keys_for_comma", - /* 97 */ "keylabel_for_symbols_question", - /* 98 */ "keylabel_for_symbols_semicolon", - /* 99 */ "keylabel_for_symbols_percent", - /* 100 */ "more_keys_for_symbols_exclamation", - /* 101 */ "more_keys_for_symbols_question", - /* 102 */ "more_keys_for_symbols_semicolon", - /* 103 */ "more_keys_for_symbols_percent", - /* 104 */ "keylabel_for_tablet_comma", - /* 105 */ "keyhintlabel_for_tablet_comma", - /* 106 */ "more_keys_for_tablet_comma", + /* 45 */ "keylabel_for_swiss_row1_11", + /* 46 */ "keylabel_for_swiss_row2_10", + /* 47 */ "keylabel_for_swiss_row2_11", + /* 48 */ "more_keys_for_swiss_row1_11", + /* 49 */ "more_keys_for_swiss_row2_10", + /* 50 */ "more_keys_for_swiss_row2_11", + /* 51 */ "label_to_alpha_key", + /* 52 */ "single_quotes", + /* 53 */ "double_quotes", + /* 54 */ "single_angle_quotes", + /* 55 */ "double_angle_quotes", + /* 56 */ "more_keys_for_currency_dollar", + /* 57 */ "keylabel_for_currency", + /* 58 */ "more_keys_for_currency", + /* 59 */ "more_keys_for_punctuation", + /* 60 */ "more_keys_for_tablet_punctuation", + /* 61 */ "more_keys_for_star", + /* 62 */ "more_keys_for_bullet", + /* 63 */ "more_keys_for_plus", + /* 64 */ "more_keys_for_left_parenthesis", + /* 65 */ "more_keys_for_right_parenthesis", + /* 66 */ "more_keys_for_less_than", + /* 67 */ "more_keys_for_greater_than", + /* 68 */ "more_keys_for_arabic_diacritics", + /* 69 */ "keylabel_for_symbols_1", + /* 70 */ "keylabel_for_symbols_2", + /* 71 */ "keylabel_for_symbols_3", + /* 72 */ "keylabel_for_symbols_4", + /* 73 */ "keylabel_for_symbols_5", + /* 74 */ "keylabel_for_symbols_6", + /* 75 */ "keylabel_for_symbols_7", + /* 76 */ "keylabel_for_symbols_8", + /* 77 */ "keylabel_for_symbols_9", + /* 78 */ "keylabel_for_symbols_0", + /* 79 */ "label_to_symbol_key", + /* 80 */ "label_to_symbol_with_microphone_key", + /* 81 */ "additional_more_keys_for_symbols_1", + /* 82 */ "additional_more_keys_for_symbols_2", + /* 83 */ "additional_more_keys_for_symbols_3", + /* 84 */ "additional_more_keys_for_symbols_4", + /* 85 */ "additional_more_keys_for_symbols_5", + /* 86 */ "additional_more_keys_for_symbols_6", + /* 87 */ "additional_more_keys_for_symbols_7", + /* 88 */ "additional_more_keys_for_symbols_8", + /* 89 */ "additional_more_keys_for_symbols_9", + /* 90 */ "additional_more_keys_for_symbols_0", + /* 91 */ "more_keys_for_symbols_1", + /* 92 */ "more_keys_for_symbols_2", + /* 93 */ "more_keys_for_symbols_3", + /* 94 */ "more_keys_for_symbols_4", + /* 95 */ "more_keys_for_symbols_5", + /* 96 */ "more_keys_for_symbols_6", + /* 97 */ "more_keys_for_symbols_7", + /* 98 */ "more_keys_for_symbols_8", + /* 99 */ "more_keys_for_symbols_9", + /* 100 */ "more_keys_for_symbols_0", + /* 101 */ "keylabel_for_comma", + /* 102 */ "more_keys_for_comma", + /* 103 */ "keylabel_for_tablet_comma", + /* 104 */ "keyhintlabel_for_tablet_comma", + /* 105 */ "more_keys_for_tablet_comma", + /* 106 */ "keylabel_for_period", /* 107 */ "keyhintlabel_for_period", /* 108 */ "more_keys_for_period", - /* 109 */ "keylabel_for_apostrophe", - /* 110 */ "keyhintlabel_for_apostrophe", - /* 111 */ "more_keys_for_apostrophe", - /* 112 */ "more_keys_for_q", - /* 113 */ "more_keys_for_x", - /* 114 */ "keylabel_for_q", - /* 115 */ "keylabel_for_w", - /* 116 */ "keylabel_for_y", - /* 117 */ "keylabel_for_x", - /* 118 */ "keylabel_for_spanish_row2_10", - /* 119 */ "more_keys_for_am_pm", - /* 120 */ "settings_as_more_key", - /* 121 */ "shortcut_as_more_key", - /* 122 */ "action_next_as_more_key", - /* 123 */ "action_previous_as_more_key", - /* 124 */ "label_to_more_symbol_key", - /* 125 */ "label_to_more_symbol_for_tablet_key", - /* 126 */ "label_tab_key", - /* 127 */ "label_to_phone_numeric_key", - /* 128 */ "label_to_phone_symbols_key", - /* 129 */ "label_time_am", - /* 130 */ "label_time_pm", - /* 131 */ "keylabel_for_popular_domain", - /* 132 */ "more_keys_for_popular_domain", - /* 133 */ "more_keys_for_smiley", - /* 134 */ "single_laqm_raqm", - /* 135 */ "single_laqm_raqm_rtl", - /* 136 */ "single_raqm_laqm", - /* 137 */ "double_laqm_raqm", - /* 138 */ "double_laqm_raqm_rtl", - /* 139 */ "double_raqm_laqm", - /* 140 */ "single_lqm_rqm", - /* 141 */ "single_9qm_lqm", - /* 142 */ "single_9qm_rqm", - /* 143 */ "double_lqm_rqm", - /* 144 */ "double_9qm_lqm", - /* 145 */ "double_9qm_rqm", - /* 146 */ "more_keys_for_single_quote", - /* 147 */ "more_keys_for_double_quote", - /* 148 */ "more_keys_for_tablet_double_quote", - /* 149 */ "emoji_key_as_more_key", + /* 109 */ "keylabel_for_tablet_period", + /* 110 */ "keyhintlabel_for_tablet_period", + /* 111 */ "more_keys_for_tablet_period", + /* 112 */ "keylabel_for_symbols_question", + /* 113 */ "keylabel_for_symbols_semicolon", + /* 114 */ "keylabel_for_symbols_percent", + /* 115 */ "more_keys_for_exclamation", + /* 116 */ "more_keys_for_question", + /* 117 */ "more_keys_for_symbols_semicolon", + /* 118 */ "more_keys_for_symbols_percent", + /* 119 */ "more_keys_for_q", + /* 120 */ "more_keys_for_x", + /* 121 */ "keylabel_for_q", + /* 122 */ "keylabel_for_w", + /* 123 */ "keylabel_for_y", + /* 124 */ "keylabel_for_x", + /* 125 */ "keylabel_for_spanish_row2_10", + /* 126 */ "more_keys_for_am_pm", + /* 127 */ "settings_as_more_key", + /* 128 */ "shortcut_as_more_key", + /* 129 */ "action_next_as_more_key", + /* 130 */ "action_previous_as_more_key", + /* 131 */ "label_to_more_symbol_key", + /* 132 */ "label_to_more_symbol_for_tablet_key", + /* 133 */ "label_tab_key", + /* 134 */ "label_to_phone_numeric_key", + /* 135 */ "label_to_phone_symbols_key", + /* 136 */ "label_time_am", + /* 137 */ "label_time_pm", + /* 138 */ "keylabel_for_popular_domain", + /* 139 */ "more_keys_for_popular_domain", + /* 140 */ "more_keys_for_smiley", + /* 141 */ "single_laqm_raqm", + /* 142 */ "single_laqm_raqm_rtl", + /* 143 */ "single_raqm_laqm", + /* 144 */ "double_laqm_raqm", + /* 145 */ "double_laqm_raqm_rtl", + /* 146 */ "double_raqm_laqm", + /* 147 */ "single_lqm_rqm", + /* 148 */ "single_9qm_lqm", + /* 149 */ "single_9qm_rqm", + /* 150 */ "double_lqm_rqm", + /* 151 */ "double_9qm_lqm", + /* 152 */ "double_9qm_rqm", + /* 153 */ "more_keys_for_single_quote", + /* 154 */ "more_keys_for_double_quote", + /* 155 */ "more_keys_for_tablet_double_quote", + /* 156 */ "emoji_key_as_more_key", }; private static final String EMPTY = ""; @@ -262,145 +269,148 @@ public final class KeyboardTextsSet { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~44 */ + EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, + /* ~50 */ // Label for "switch to alphabetic" key. - /* 45 */ "ABC", - /* 46 */ "!text/single_lqm_rqm", - /* 47 */ "!text/double_lqm_rqm", - /* 48 */ "!text/single_laqm_raqm", - /* 49 */ "!text/double_laqm_raqm", + /* 51 */ "ABC", + /* 52 */ "!text/single_lqm_rqm", + /* 53 */ "!text/double_lqm_rqm", + /* 54 */ "!text/single_laqm_raqm", + /* 55 */ "!text/double_laqm_raqm", // U+00A2: "¢" CENT SIGN // U+00A3: "£" POUND SIGN // U+20AC: "€" EURO SIGN // U+00A5: "¥" YEN SIGN // U+20B1: "₱" PESO SIGN - /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", - /* 51 */ "$", - /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", - /* 53 */ "!fixedColumnOrder!8,;,/,(,),#,!,\\,,?,&,\\%,+,\",-,:,',@", + /* 56 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + /* 57 */ "$", + /* 58 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", + /* 59 */ "!fixedColumnOrder!8,;,/,(,),#,!,\\,,?,&,\\%,+,\",-,:,',@", + /* 60 */ "!fixedColumnOrder!7,;,/,(,),#,',\\,,&,\\%,+,\",-,:,@", // U+2020: "†" DAGGER // U+2021: "‡" DOUBLE DAGGER // U+2605: "★" BLACK STAR - /* 54 */ "\u2020,\u2021,\u2605", + /* 61 */ "\u2020,\u2021,\u2605", // U+266A: "♪" EIGHTH NOTE // U+2665: "♥" BLACK HEART SUIT // U+2660: "♠" BLACK SPADE SUIT // U+2666: "♦" BLACK DIAMOND SUIT // U+2663: "♣" BLACK CLUB SUIT - /* 55 */ "\u266A,\u2665,\u2660,\u2666,\u2663", + /* 62 */ "\u266A,\u2665,\u2660,\u2666,\u2663", // U+00B1: "±" PLUS-MINUS SIGN - /* 56 */ "\u00B1", + /* 63 */ "\u00B1", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 57 */ "!fixedColumnOrder!3,<,{,[", - /* 58 */ "!fixedColumnOrder!3,>,},]", + /* 64 */ "!fixedColumnOrder!3,<,{,[", + /* 65 */ "!fixedColumnOrder!3,>,},]", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /* 59 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", - /* 60 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", - /* 61 */ EMPTY, - /* 62 */ EMPTY, - /* 63 */ "1", - /* 64 */ "2", - /* 65 */ "3", - /* 66 */ "4", - /* 67 */ "5", - /* 68 */ "6", - /* 69 */ "7", - /* 70 */ "8", - /* 71 */ "9", - /* 72 */ "0", + /* 66 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB", + /* 67 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB", + /* 68 */ EMPTY, + /* 69 */ "1", + /* 70 */ "2", + /* 71 */ "3", + /* 72 */ "4", + /* 73 */ "5", + /* 74 */ "6", + /* 75 */ "7", + /* 76 */ "8", + /* 77 */ "9", + /* 78 */ "0", // Label for "switch to symbols" key. - /* 73 */ "?123", + /* 79 */ "?123", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 74 */ "123", - /* 75~ */ + /* 80 */ "123", + /* 81~ */ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~84 */ + /* ~90 */ // U+00B9: "¹" SUPERSCRIPT ONE // U+00BD: "½" VULGAR FRACTION ONE HALF // U+2153: "⅓" VULGAR FRACTION ONE THIRD // U+00BC: "¼" VULGAR FRACTION ONE QUARTER // U+215B: "⅛" VULGAR FRACTION ONE EIGHTH - /* 85 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", + /* 91 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B", // U+00B2: "²" SUPERSCRIPT TWO // U+2154: "⅔" VULGAR FRACTION TWO THIRDS - /* 86 */ "\u00B2,\u2154", + /* 92 */ "\u00B2,\u2154", // U+00B3: "³" SUPERSCRIPT THREE // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS - /* 87 */ "\u00B3,\u00BE,\u215C", + /* 93 */ "\u00B3,\u00BE,\u215C", // U+2074: "⁴" SUPERSCRIPT FOUR - /* 88 */ "\u2074", + /* 94 */ "\u2074", // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS - /* 89 */ "\u215D", - /* 90 */ EMPTY, + /* 95 */ "\u215D", + /* 96 */ EMPTY, // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS - /* 91 */ "\u215E", - /* 92 */ EMPTY, - /* 93 */ EMPTY, + /* 97 */ "\u215E", + /* 98 */ EMPTY, + /* 99 */ EMPTY, // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N // U+2205: "∅" EMPTY SET - /* 94 */ "\u207F,\u2205", - /* 95 */ ",", - /* 96 */ EMPTY, - /* 97 */ "?", - /* 98 */ ";", - /* 99 */ "%", + /* 100 */ "\u207F,\u2205", + // Comma key + /* 101 */ ",", + /* 102 */ EMPTY, + /* 103 */ ",", + /* 104 */ EMPTY, + /* 105 */ EMPTY, + // Period key + /* 106 */ ".", + /* 107 */ EMPTY, + /* 108 */ "!text/more_keys_for_punctuation", + /* 109 */ ".", + /* 110 */ EMPTY, + /* 111 */ "!text/more_keys_for_tablet_punctuation", + /* 112 */ "?", + /* 113 */ ";", + /* 114 */ "%", // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 100 */ "\u00A1", + /* 115 */ "\u00A1", // U+00BF: "¿" INVERTED QUESTION MARK - /* 101 */ "\u00BF", - /* 102 */ EMPTY, + /* 116 */ "\u00BF", + /* 117 */ EMPTY, // U+2030: "‰" PER MILLE SIGN - /* 103 */ "\u2030", - /* 104 */ ",", - /* 105~ */ - EMPTY, EMPTY, EMPTY, - /* ~107 */ - // U+2026: "…" HORIZONTAL ELLIPSIS - /* 108 */ "\u2026", - /* 109 */ "\'", - /* 110 */ "\"", - /* 111 */ "\"", - /* 112 */ EMPTY, - /* 113 */ EMPTY, - /* 114 */ "q", - /* 115 */ "w", - /* 116 */ "y", - /* 117 */ "x", - /* 118 */ EMPTY, - /* 119 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", - /* 120 */ "!icon/settings_key|!code/key_settings", - /* 121 */ "!icon/shortcut_key|!code/key_shortcut", - /* 122 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", - /* 123 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", + /* 118 */ "\u2030", + /* 119 */ EMPTY, + /* 120 */ EMPTY, + /* 121 */ "q", + /* 122 */ "w", + /* 123 */ "y", + /* 124 */ "x", + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + /* 125 */ "\u00F1", + /* 126 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm", + /* 127 */ "!icon/settings_key|!code/key_settings", + /* 128 */ "!icon/shortcut_key|!code/key_shortcut", + /* 129 */ "!hasLabels!,!text/label_next_key|!code/key_action_next", + /* 130 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous", // Label for "switch to more symbol" modifier key. Must be short to fit on key! - /* 124 */ "= \\ <", + /* 131 */ "= \\ <", // Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key! - /* 125 */ "~ [ <", + /* 132 */ "~ [ <", // Label for "Tab" key. Must be short to fit on key! - /* 126 */ "Tab", + /* 133 */ "Tab", // Label for "switch to phone numeric" key. Must be short to fit on key! - /* 127 */ "123", + /* 134 */ "123", // Label for "switch to phone symbols" key. Must be short to fit on key! // U+FF0A: "*" FULLWIDTH ASTERISK // U+FF03: "#" FULLWIDTH NUMBER SIGN - /* 128 */ "\uFF0A\uFF03", + /* 135 */ "\uFF0A\uFF03", // Key label for "ante meridiem" - /* 129 */ "AM", + /* 136 */ "AM", // Key label for "post meridiem" - /* 130 */ "PM", - /* 131 */ ".com", + /* 137 */ "PM", + /* 138 */ ".com", // popular web domains for the locale - most popular, displayed on the keyboard - /* 132 */ "!hasLabels!,.net,.org,.gov,.edu", - /* 133 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", + /* 139 */ "!hasLabels!,.net,.org,.gov,.edu", + /* 140 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ", // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK @@ -422,25 +432,25 @@ public final class KeyboardTextsSet { // The following each quotation mark pair consist of // <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 134 */ "\u2039,\u203A", - /* 135 */ "\u2039|\u203A,\u203A|\u2039", - /* 136 */ "\u203A,\u2039", - /* 137 */ "\u00AB,\u00BB", - /* 138 */ "\u00AB|\u00BB,\u00BB|\u00AB", - /* 139 */ "\u00BB,\u00AB", + /* 141 */ "\u2039,\u203A", + /* 142 */ "\u2039|\u203A,\u203A|\u2039", + /* 143 */ "\u203A,\u2039", + /* 144 */ "\u00AB,\u00BB", + /* 145 */ "\u00AB|\u00BB,\u00BB|\u00AB", + /* 146 */ "\u00BB,\u00AB", // The following each quotation mark triplet consists of // <another quotation mark>, <opening quotation mark>, <closing quotation mark> // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>. - /* 140 */ "\u201A,\u2018,\u2019", - /* 141 */ "\u2019,\u201A,\u2018", - /* 142 */ "\u2018,\u201A,\u2019", - /* 143 */ "\u201E,\u201C,\u201D", - /* 144 */ "\u201D,\u201E,\u201C", - /* 145 */ "\u201C,\u201E,\u201D", - /* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", - /* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", - /* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", - /* 149 */ "!icon/emoji_key|!code/key_emoji", + /* 147 */ "\u201A,\u2018,\u2019", + /* 148 */ "\u2019,\u201A,\u2018", + /* 149 */ "\u2018,\u201A,\u2019", + /* 150 */ "\u201E,\u201C,\u201D", + /* 151 */ "\u201D,\u201E,\u201C", + /* 152 */ "\u201C,\u201E,\u201D", + /* 153 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", + /* 154 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", + /* 155 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", + /* 156 */ "!icon/emoji_key|!code/key_emoji", }; /* Language af: Afrikaans */ @@ -502,44 +512,43 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0623: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "پ" ARABIC LETTER PEH - /* 45 */ "\u0623\u200C\u0628\u200C\u062C", - /* 46 */ null, - /* 47 */ null, - /* 48 */ "!text/single_laqm_raqm_rtl", - /* 49 */ "!text/double_laqm_raqm_rtl", - /* 50~ */ + /* 51 */ "\u0623\u200C\u0628\u200C\u062C", + /* 52 */ null, + /* 53 */ null, + /* 54 */ "!text/single_laqm_raqm_rtl", + /* 55 */ "!text/double_laqm_raqm_rtl", + /* 56~ */ null, null, null, - /* ~52 */ - // U+061F: "؟" ARABIC QUESTION MARK - // U+060C: "،" ARABIC COMMA - // U+061B: "؛" ARABIC SEMICOLON - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", + /* ~58 */ + /* 59 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", + /* 60 */ null, // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 54 */ "\u2605,\u066D", + /* 61 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 55 */ "\u266A", - /* 56 */ null, + /* 62 */ "\u266A", + /* 63 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 64 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 65 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", + /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0654: "ٔ" ARABIC HAMZA ABOVE // U+0652: "ْ" ARABIC SUKUN @@ -556,70 +565,74 @@ public final class KeyboardTextsSet { // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. - /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640", - /* 62 */ "\u0651", + /* 68 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0654|\u0654, \u0652|\u0652, \u064D|\u064D, \u064C|\u064C, \u064B|\u064B, \u0651|\u0651, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u0650|\u0650, \u064F|\u064F, \u064E|\u064E,\u0640\u0640\u0640|\u0640", // U+0661: "١" ARABIC-INDIC DIGIT ONE - /* 63 */ "\u0661", + /* 69 */ "\u0661", // U+0662: "٢" ARABIC-INDIC DIGIT TWO - /* 64 */ "\u0662", + /* 70 */ "\u0662", // U+0663: "٣" ARABIC-INDIC DIGIT THREE - /* 65 */ "\u0663", + /* 71 */ "\u0663", // U+0664: "٤" ARABIC-INDIC DIGIT FOUR - /* 66 */ "\u0664", + /* 72 */ "\u0664", // U+0665: "٥" ARABIC-INDIC DIGIT FIVE - /* 67 */ "\u0665", + /* 73 */ "\u0665", // U+0666: "٦" ARABIC-INDIC DIGIT SIX - /* 68 */ "\u0666", + /* 74 */ "\u0666", // U+0667: "٧" ARABIC-INDIC DIGIT SEVEN - /* 69 */ "\u0667", + /* 75 */ "\u0667", // U+0668: "٨" ARABIC-INDIC DIGIT EIGHT - /* 70 */ "\u0668", + /* 76 */ "\u0668", // U+0669: "٩" ARABIC-INDIC DIGIT NINE - /* 71 */ "\u0669", + /* 77 */ "\u0669", // U+0660: "٠" ARABIC-INDIC DIGIT ZERO - /* 72 */ "\u0660", + /* 78 */ "\u0660", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 73 */ "\u0663\u0662\u0661\u061F", + /* 79 */ "\u0663\u0662\u0661\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 74 */ "\u0663\u0662\u0661", - /* 75 */ "1", - /* 76 */ "2", - /* 77 */ "3", - /* 78 */ "4", - /* 79 */ "5", - /* 80 */ "6", - /* 81 */ "7", - /* 82 */ "8", - /* 83 */ "9", + /* 80 */ "\u0663\u0662\u0661", + /* 81 */ "1", + /* 82 */ "2", + /* 83 */ "3", + /* 84 */ "4", + /* 85 */ "5", + /* 86 */ "6", + /* 87 */ "7", + /* 88 */ "8", + /* 89 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 84 */ "0,\u066B,\u066C", - /* 85~ */ + /* 90 */ "0,\u066B,\u066C", + /* 91~ */ null, null, null, null, null, null, null, null, null, null, - /* ~94 */ + /* ~100 */ // U+060C: "،" ARABIC COMMA - /* 95 */ "\u060C", - /* 96 */ "\\,", - /* 97 */ "\u061F", - /* 98 */ "\u061B", - // U+066A: "٪" ARABIC PERCENT SIGN - /* 99 */ "\u066A", - /* 100 */ null, - /* 101 */ "?", - /* 102 */ ";", - // U+2030: "‰" PER MILLE SIGN - /* 103 */ "\\%,\u2030", - /* 104~ */ - null, null, null, null, null, - /* ~108 */ + /* 101 */ "\u060C", + /* 102 */ "\\,", + // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - // U+061F: "؟" ARABIC QUESTION MARK - /* 109 */ "\u060C", - /* 110 */ "\u061F", - /* 111 */ "\u061F,\u061B,!,:,-,/,\',\"", + /* 103 */ "\u060C", + /* 104 */ "\u061F", + /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\",\'", + /* 106 */ null, + // U+0651: "ّ" ARABIC SHADDA + /* 107 */ "\u0651", + /* 108 */ "!text/more_keys_for_arabic_diacritics", + /* 109 */ null, + /* 110 */ "\u0651", + /* 111 */ "!text/more_keys_for_arabic_diacritics", + /* 112 */ "\u061F", + /* 113 */ "\u061B", + // U+066A: "٪" ARABIC PERCENT SIGN + /* 114 */ "\u066A", + /* 115 */ null, + // U+00BF: "¿" INVERTED QUESTION MARK + /* 116 */ "?,\u00BF", + /* 117 */ ";", + // U+2030: "‰" PER MILLE SIGN + /* 118 */ "\\%,\u2030", }; /* Language az: Azerbaijani */ @@ -668,8 +681,8 @@ public final class KeyboardTextsSet { /* 15 */ "\u011F", }; - /* Language be: Belarusian */ - private static final String[] LANGUAGE_be = { + /* Language be_BY: Belarusian (Belarus) */ + private static final String[] LANGUAGE_be_BY = { /* 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, @@ -694,14 +707,16 @@ public final class KeyboardTextsSet { /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* 43 */ "\u0451", - /* 44 */ null, + /* 44~ */ + null, null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language bg: Bulgarian */ @@ -710,15 +725,16 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ null, + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ null, // single_quotes of Bulgarian is default single_quotes_right_left. - /* 47 */ "!text/double_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language ca: Catalan */ @@ -782,22 +798,20 @@ public final class KeyboardTextsSet { /* 15~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~52 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~58 */ // U+00B7: "·" MIDDLE DOT - /* 53 */ "!fixedColumnOrder!9,;,/,(,),#,\u00B7,!,\\,,?,&,\\%,+,\",-,:,',@", - /* 54~ */ + /* 59 */ "!fixedColumnOrder!9,;,/,(,),#,\u00B7,!,\\,,?,&,\\%,+,\",-,:,',@", + /* 60 */ "!fixedColumnOrder!8,;,/,(,),#,\u00B7,',\\,,&,\\%,+,\",-,:,@", + /* 61~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 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 */ "?,\u00B7", - /* 109~ */ - null, null, null, null, null, null, null, null, null, - /* ~117 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, + /* ~124 */ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA - /* 118 */ "\u00E7", + /* 125 */ "\u00E7", }; /* Language cs: Czech */ @@ -871,12 +885,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language da: Danish */ @@ -940,12 +954,12 @@ public final class KeyboardTextsSet { /* 24 */ "\u00F6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language de: German */ @@ -991,12 +1005,25 @@ public final class KeyboardTextsSet { /* 7~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, + /* ~44 */ + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + /* 45 */ "\u00FC", + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + /* 46 */ "\u00F6", + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + /* 47 */ "\u00E4", + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* 48 */ "\u00E8", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + /* 49 */ "\u00E9", + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + /* 50 */ "\u00E0", + /* 51 */ null, + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language el: Greek */ @@ -1005,12 +1032,13 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA // U+0393: "Γ" GREEK CAPITAL LETTER GAMMA - /* 45 */ "\u0391\u0392\u0393", + /* 51 */ "\u0391\u0392\u0393", }; /* Language en: English */ @@ -1182,20 +1210,20 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, - /* ~111 */ - /* 112 */ "q", - /* 113 */ "x", + null, null, null, null, null, null, null, null, null, + /* ~118 */ + /* 119 */ "q", + /* 120 */ "x", // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX - /* 114 */ "\u015D", + /* 121 */ "\u015D", // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX - /* 115 */ "\u011D", + /* 122 */ "\u011D", // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE - /* 116 */ "\u016D", + /* 123 */ "\u016D", // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX - /* 117 */ "\u0109", + /* 124 */ "\u0109", // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX - /* 118 */ "\u0135", + /* 125 */ "\u0135", }; /* Language es: Spanish */ @@ -1254,33 +1282,15 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~52 */ - // U+00A1: "¡" INVERTED EXCLAMATION MARK - // U+00BF: "¿" INVERTED QUESTION MARK - /* 53 */ "!fixedColumnOrder!4,;,!,\\,,?,:,\u00A1,@,\u00BF", - /* 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, - /* ~105 */ + null, null, null, null, null, null, + /* ~58 */ // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 106 */ "!,\u00A1", - /* 107 */ null, // U+00BF: "¿" INVERTED QUESTION MARK - /* 108 */ "?,\u00BF", - /* 109 */ "\"", - /* 110 */ "\'", - /* 111 */ "\'", - /* 112~ */ - null, null, null, null, null, null, - /* ~117 */ - // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE - /* 118 */ "\u00F1", + /* 59 */ "!fixedColumnOrder!9,\u00A1,;,/,(,),#,!,\\,,?,\u00BF,&,\\%,+,\",-,:,',@", }; - /* Language et: Estonian */ - private static final String[] LANGUAGE_et = { + /* Language et_EE: Estonian (Estonia) */ + private static final String[] LANGUAGE_et_EE = { // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE @@ -1379,10 +1389,10 @@ public final class KeyboardTextsSet { /* 23 */ "\u00F5", /* 24~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language fa: Persian */ @@ -1391,45 +1401,47 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH - /* 45 */ "\u0627\u200C\u0628\u200C\u067E", - /* 46 */ null, - /* 47 */ null, - /* 48 */ "!text/single_laqm_raqm_rtl", - /* 49 */ "!text/double_laqm_raqm_rtl", - /* 50 */ null, - // U+FDFC: "﷼" RIAL SIGN - /* 51 */ "\uFDFC", + /* 51 */ "\u0627\u200C\u0628\u200C\u067E", /* 52 */ null, + /* 53 */ null, + /* 54 */ "!text/single_laqm_raqm_rtl", + /* 55 */ "!text/double_laqm_raqm_rtl", + /* 56 */ null, + // U+FDFC: "﷼" RIAL SIGN + /* 57 */ "\uFDFC", + /* 58 */ null, // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON - /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", + /* 59 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", + /* 60 */ null, // U+2605: "★" BLACK STAR // U+066D: "٭" ARABIC FIVE POINTED STAR - /* 54 */ "\u2605,\u066D", + /* 61 */ "\u2605,\u066D", // U+266A: "♪" EIGHTH NOTE - /* 55 */ "\u266A", - /* 56 */ null, + /* 62 */ "\u266A", + /* 63 */ null, // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt // U+FD3E: "﴾" ORNATE LEFT PARENTHESIS // U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS - /* 57 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", - /* 58 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", + /* 64 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]", + /* 65 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", - /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", + /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>", + /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<", // U+0655: "ٕ" ARABIC HAMZA BELOW // U+0652: "ْ" ARABIC SUKUN // U+0651: "ّ" ARABIC SHADDA @@ -1446,74 +1458,75 @@ public final class KeyboardTextsSet { // U+0640: "ـ" ARABIC TATWEEL // In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. // Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. - /* 61 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640", - /* 62 */ "\u064B", + /* 68 */ "!fixedColumnOrder!7, \u0655|\u0655, \u0652|\u0652, \u0651|\u0651, \u064C|\u064C, \u064D|\u064D, \u064B|\u064B, \u0654|\u0654, \u0656|\u0656, \u0670|\u0670, \u0653|\u0653, \u064F|\u064F, \u0650|\u0650, \u064E|\u064E,\u0640\u0640\u0640|\u0640", // U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE - /* 63 */ "\u06F1", + /* 69 */ "\u06F1", // U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO - /* 64 */ "\u06F2", + /* 70 */ "\u06F2", // U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE - /* 65 */ "\u06F3", + /* 71 */ "\u06F3", // U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR - /* 66 */ "\u06F4", + /* 72 */ "\u06F4", // U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE - /* 67 */ "\u06F5", + /* 73 */ "\u06F5", // U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX - /* 68 */ "\u06F6", + /* 74 */ "\u06F6", // U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN - /* 69 */ "\u06F7", + /* 75 */ "\u06F7", // U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT - /* 70 */ "\u06F8", + /* 76 */ "\u06F8", // U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE - /* 71 */ "\u06F9", + /* 77 */ "\u06F9", // U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO - /* 72 */ "\u06F0", + /* 78 */ "\u06F0", // Label for "switch to symbols" key. // U+061F: "؟" ARABIC QUESTION MARK - /* 73 */ "\u06F3\u06F2\u06F1\u061F", + /* 79 */ "\u06F3\u06F2\u06F1\u061F", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 74 */ "\u06F3\u06F2\u06F1", - /* 75 */ "1", - /* 76 */ "2", - /* 77 */ "3", - /* 78 */ "4", - /* 79 */ "5", - /* 80 */ "6", - /* 81 */ "7", - /* 82 */ "8", - /* 83 */ "9", + /* 80 */ "\u06F3\u06F2\u06F1", + /* 81 */ "1", + /* 82 */ "2", + /* 83 */ "3", + /* 84 */ "4", + /* 85 */ "5", + /* 86 */ "6", + /* 87 */ "7", + /* 88 */ "8", + /* 89 */ "9", // U+066B: "٫" ARABIC DECIMAL SEPARATOR // U+066C: "٬" ARABIC THOUSANDS SEPARATOR - /* 84 */ "0,\u066B,\u066C", - /* 85~ */ + /* 90 */ "0,\u066B,\u066C", + /* 91~ */ null, null, null, null, null, null, null, null, null, null, - /* ~94 */ + /* ~100 */ // U+060C: "،" ARABIC COMMA - /* 95 */ "\u060C", - /* 96 */ "\\,", - /* 97 */ "\u061F", - /* 98 */ "\u061B", - // U+066A: "٪" ARABIC PERCENT SIGN - /* 99 */ "\u066A", - /* 100 */ null, - /* 101 */ "?", - /* 102 */ ";", - // U+2030: "‰" PER MILLE SIGN - /* 103 */ "\\%,\u2030", + /* 101 */ "\u060C", + /* 102 */ "\\,", // U+060C: "،" ARABIC COMMA // U+061B: "؛" ARABIC SEMICOLON // U+061F: "؟" ARABIC QUESTION MARK // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /* 104 */ "\u060C", - /* 105 */ "!", - /* 106 */ "!,\\,", + /* 103 */ "\u060C", + /* 104 */ "\u061F", + /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 106 */ null, /* 107 */ "\u061F", - /* 108 */ "\u061F,?", - /* 109 */ "\u060C", - /* 110 */ "\u061F", - /* 111 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB", + /* 108 */ "!text/more_keys_for_arabic_diacritics", + /* 109 */ null, + /* 110 */ "\u064B", + /* 111 */ "!text/more_keys_for_arabic_diacritics", + /* 112 */ "\u061F", + /* 113 */ "\u061B", + // U+066A: "٪" ARABIC PERCENT SIGN + /* 114 */ "\u066A", + /* 115 */ null, + // U+00BF: "¿" INVERTED QUESTION MARK + /* 116 */ "?,\u00BF", + /* 117 */ ";", + // U+2030: "‰" PER MILLE SIGN + /* 118 */ "\\%,\u2030", }; /* Language fi: Finnish */ @@ -1614,6 +1627,23 @@ public final class KeyboardTextsSet { /* 7 */ "\u00E7,\u0107,\u010D", // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS /* 8 */ "%,\u00FF", + /* 9~ */ + null, null, null, null, null, null, null, null, null, null, null, null, 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 */ + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + /* 45 */ "\u00E8", + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + /* 46 */ "\u00E9", + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + /* 47 */ "\u00E0", + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + /* 48 */ "\u00FC", + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + /* 49 */ "\u00F6", + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + /* 50 */ "\u00E4", }; /* Language hi: Hindi */ @@ -1622,55 +1652,56 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA - /* 45 */ "\u0915\u0916\u0917", - /* 46~ */ + /* 51 */ "\u0915\u0916\u0917", + /* 52~ */ null, null, null, null, null, - /* ~50 */ + /* ~56 */ // U+20B9: "₹" INDIAN RUPEE SIGN - /* 51 */ "\u20B9", - /* 52~ */ + /* 57 */ "\u20B9", + /* 58~ */ null, null, null, null, null, null, null, null, null, null, null, - /* ~62 */ + /* ~68 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 63 */ "\u0967", + /* 69 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 64 */ "\u0968", + /* 70 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 65 */ "\u0969", + /* 71 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 66 */ "\u096A", + /* 72 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 67 */ "\u096B", + /* 73 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 68 */ "\u096C", + /* 74 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 69 */ "\u096D", + /* 75 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 70 */ "\u096E", + /* 76 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 71 */ "\u096F", + /* 77 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 72 */ "\u0966", + /* 78 */ "\u0966", // Label for "switch to symbols" key. - /* 73 */ "?\u0967\u0968\u0969", + /* 79 */ "?\u0967\u0968\u0969", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 74 */ "\u0967\u0968\u0969", - /* 75 */ "1", - /* 76 */ "2", - /* 77 */ "3", - /* 78 */ "4", - /* 79 */ "5", - /* 80 */ "6", - /* 81 */ "7", - /* 82 */ "8", - /* 83 */ "9", - /* 84 */ "0", + /* 80 */ "\u0967\u0968\u0969", + /* 81 */ "1", + /* 82 */ "2", + /* 83 */ "3", + /* 84 */ "4", + /* 85 */ "5", + /* 86 */ "6", + /* 87 */ "7", + /* 88 */ "8", + /* 89 */ "9", + /* 90 */ "0", }; /* Language hr: Croatian */ @@ -1701,12 +1732,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language hu: Hungarian */ @@ -1755,22 +1786,23 @@ public final class KeyboardTextsSet { /* 5~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; - /* Language hy: Armenian */ - private static final String[] LANGUAGE_hy = { + /* Language hy_AM: Armenian (Armenia) */ + private static final String[] LANGUAGE_hy_AM = { /* 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, - null, null, null, null, null, null, null, null, - /* ~52 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~58 */ // U+058A: "֊" ARMENIAN HYPHEN // U+055C: "՜" ARMENIAN EXCLAMATION MARK // U+055D: "՝" ARMENIAN COMMA @@ -1779,19 +1811,36 @@ public final class KeyboardTextsSet { // 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~ */ + /* 59 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F", + /* 60~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~99 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~102 */ + // U+058F: "֏" ARMENIAN DRAM SIGN + // TODO: Enable this when we have glyph for the following letter + // <string name="keylabel_for_currency">֏</string> + // + // U+055D: "՝" ARMENIAN COMMA + /* 103 */ "\u055D", + /* 104 */ null, + /* 105 */ null, + // U+0589: "։" ARMENIAN FULL STOP + /* 106 */ "\u0589", + /* 107 */ null, + /* 108 */ null, + /* 109 */ "\u0589", + /* 110 */ null, + /* 111 */ "!text/more_keys_for_punctuation", + /* 112~ */ + null, null, null, + /* ~114 */ // U+055C: "՜" ARMENIAN EXCLAMATION MARK // U+00A1: "¡" INVERTED EXCLAMATION MARK - /* 100 */ "\u055C,\u00A1", + /* 115 */ "\u055C,\u00A1", // U+055E: "՞" ARMENIAN QUESTION MARK // U+00BF: "¿" INVERTED QUESTION MARK - /* 101 */ "\u055E,\u00BF", + /* 116 */ "\u055E,\u00BF", }; /* Language is: Icelandic */ @@ -1857,10 +1906,10 @@ public final class KeyboardTextsSet { /* 22 */ "\u00FE", /* 23~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language it: Italian */ @@ -1914,12 +1963,13 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL - /* 45 */ "\u05D0\u05D1\u05D2", + /* 51 */ "\u05D0\u05D1\u05D2", // The following characters don't need BIDI mirroring. // U+2018: "‘" LEFT SINGLE QUOTATION MARK // U+2019: "’" RIGHT SINGLE QUOTATION MARK @@ -1927,58 +1977,51 @@ public final class KeyboardTextsSet { // U+201C: "“" LEFT DOUBLE QUOTATION MARK // U+201D: "”" RIGHT DOUBLE QUOTATION MARK // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK - /* 46 */ "\u2018,\u2019,\u201A", - /* 47 */ "\u201C,\u201D,\u201E", - /* 48 */ "!text/single_laqm_raqm_rtl", - /* 49 */ "!text/double_laqm_raqm_rtl", - /* 50 */ null, + /* 52 */ "\u2018,\u2019,\u201A", + /* 53 */ "\u201C,\u201D,\u201E", + /* 54 */ "!text/single_laqm_raqm_rtl", + /* 55 */ "!text/double_laqm_raqm_rtl", + /* 56 */ null, // U+20AA: "₪" NEW SHEQEL SIGN - /* 51 */ "\u20AA", - /* 52 */ null, - /* 53 */ "!fixedColumnOrder!8,;,/,(|),)|(,#,!,\\,,?,&,\\%,+,\",-,:,',@", + /* 57 */ "\u20AA", + /* 58 */ null, + /* 59 */ "!fixedColumnOrder!8,;,/,(|),)|(,#,!,\\,,?,&,\\%,+,\",-,:,',@", + /* 60 */ "!fixedColumnOrder!7,;,/,(|),)|(,#,',\\,,&,\\%,+,\",-,:,@", // U+2605: "★" BLACK STAR - /* 54 */ "\u2605", - /* 55 */ null, + /* 61 */ "\u2605", + /* 62 */ null, // U+00B1: "±" PLUS-MINUS SIGN // U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN - /* 56 */ "\u00B1,\uFB29", + /* 63 */ "\u00B1,\uFB29", // The all letters need to be mirrored are found at // http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt - /* 57 */ "!fixedColumnOrder!3,<|>,{|},[|]", - /* 58 */ "!fixedColumnOrder!3,>|<,}|{,]|[", + /* 64 */ "!fixedColumnOrder!3,<|>,{|},[|]", + /* 65 */ "!fixedColumnOrder!3,>|<,}|{,]|[", // U+2264: "≤" LESS-THAN OR EQUAL TO // U+2265: "≥" GREATER-THAN EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", - /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", - /* 61~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~104 */ - /* 105 */ "!", - /* 106 */ "!", - /* 107 */ "?", - /* 108 */ "?", + /* 66 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB", + /* 67 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB", }; - /* Language ka: Georgian */ - private static final String[] LANGUAGE_ka = { + /* Language ka_GE: Georgian (Georgia) */ + private static final String[] LANGUAGE_ka_GE = { /* 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 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN - /* 45 */ "\u10D0\u10D1\u10D2", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + /* 51 */ "\u10D0\u10D1\u10D2", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language kk: Kazakh */ @@ -2021,31 +2064,34 @@ public final class KeyboardTextsSet { /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* 43 */ "\u0451", - /* 44 */ null, + /* 44~ */ + null, null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", + /* 51 */ "\u0410\u0411\u0412", }; - /* Language km: Khmer */ - private static final String[] LANGUAGE_km = { + /* Language km_KH: Khmer (Cambodia) */ + private static final String[] LANGUAGE_km_KH = { /* 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 */ + null, null, null, null, null, null, + /* ~50 */ // 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~ */ + /* 51 */ "\u1780\u1781\u1782", + /* 52~ */ null, null, null, null, - /* ~49 */ + /* ~55 */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL - /* 50 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", + /* 56 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", }; /* Language ky: Kirghiz */ @@ -2081,31 +2127,34 @@ public final class KeyboardTextsSet { /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* 43 */ "\u0451", - /* 44 */ null, + /* 44~ */ + null, null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", + /* 51 */ "\u0410\u0411\u0412", }; - /* Language lo: Lao */ - private static final String[] LANGUAGE_lo = { + /* Language lo_LA: Lao (Laos) */ + private static final String[] LANGUAGE_lo_LA = { /* 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 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0E81: "ກ" LAO LETTER KO // U+0E82: "ຂ" LAO LETTER KHO SUNG // U+0E84: "ຄ" LAO LETTER KHO TAM - /* 45 */ "\u0E81\u0E82\u0E84", - /* 46~ */ + /* 51 */ "\u0E81\u0E82\u0E84", + /* 52~ */ null, null, null, null, null, - /* ~50 */ + /* ~56 */ // U+20AD: "₭" KIP SIGN - /* 51 */ "\u20AD", + /* 57 */ "\u20AD", }; /* Language lt: Lithuanian */ @@ -2199,9 +2248,10 @@ public final class KeyboardTextsSet { /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language lv: Latvian */ @@ -2294,9 +2344,10 @@ public final class KeyboardTextsSet { /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language mk: Macedonian */ @@ -2318,32 +2369,36 @@ public final class KeyboardTextsSet { /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* 44 */ "\u045D", + /* 45~ */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; - /* Language mn: Mongolian */ - private static final String[] LANGUAGE_mn = { + /* Language mn_MN: Mongolian (Mongolia) */ + private static final String[] LANGUAGE_mn_MN = { /* 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 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46~ */ + /* 51 */ "\u0410\u0411\u0412", + /* 52~ */ null, null, null, null, null, - /* ~50 */ + /* ~56 */ // U+20AE: "₮" TUGRIK SIGN - /* 51 */ "\u20AE", + /* 57 */ "\u20AE", }; /* Language nb: Norwegian Bokmål */ @@ -2393,10 +2448,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E4", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", }; /* Language ne: Nepali */ @@ -2405,55 +2460,56 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA - /* 45 */ "\u0915\u0916\u0917", - /* 46~ */ + /* 51 */ "\u0915\u0916\u0917", + /* 52~ */ null, null, null, null, null, - /* ~50 */ + /* ~56 */ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN - /* 51 */ "\u0930\u0941.", - /* 52~ */ + /* 57 */ "\u0930\u0941.", + /* 58~ */ null, null, null, null, null, null, null, null, null, null, null, - /* ~62 */ + /* ~68 */ // U+0967: "१" DEVANAGARI DIGIT ONE - /* 63 */ "\u0967", + /* 69 */ "\u0967", // U+0968: "२" DEVANAGARI DIGIT TWO - /* 64 */ "\u0968", + /* 70 */ "\u0968", // U+0969: "३" DEVANAGARI DIGIT THREE - /* 65 */ "\u0969", + /* 71 */ "\u0969", // U+096A: "४" DEVANAGARI DIGIT FOUR - /* 66 */ "\u096A", + /* 72 */ "\u096A", // U+096B: "५" DEVANAGARI DIGIT FIVE - /* 67 */ "\u096B", + /* 73 */ "\u096B", // U+096C: "६" DEVANAGARI DIGIT SIX - /* 68 */ "\u096C", + /* 74 */ "\u096C", // U+096D: "७" DEVANAGARI DIGIT SEVEN - /* 69 */ "\u096D", + /* 75 */ "\u096D", // U+096E: "८" DEVANAGARI DIGIT EIGHT - /* 70 */ "\u096E", + /* 76 */ "\u096E", // U+096F: "९" DEVANAGARI DIGIT NINE - /* 71 */ "\u096F", + /* 77 */ "\u096F", // U+0966: "०" DEVANAGARI DIGIT ZERO - /* 72 */ "\u0966", + /* 78 */ "\u0966", // Label for "switch to symbols" key. - /* 73 */ "?\u0967\u0968\u0969", + /* 79 */ "?\u0967\u0968\u0969", // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic" // part because it'll be appended by the code. - /* 74 */ "\u0967\u0968\u0969", - /* 75 */ "1", - /* 76 */ "2", - /* 77 */ "3", - /* 78 */ "4", - /* 79 */ "5", - /* 80 */ "6", - /* 81 */ "7", - /* 82 */ "8", - /* 83 */ "9", - /* 84 */ "0", + /* 80 */ "\u0967\u0968\u0969", + /* 81 */ "1", + /* 82 */ "2", + /* 83 */ "3", + /* 84 */ "4", + /* 85 */ "5", + /* 86 */ "6", + /* 87 */ "7", + /* 88 */ "8", + /* 89 */ "9", + /* 90 */ "0", }; /* Language nl: Dutch */ @@ -2508,10 +2564,10 @@ public final class KeyboardTextsSet { /* 9~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", }; /* Language pl: Polish */ @@ -2569,10 +2625,10 @@ public final class KeyboardTextsSet { /* 15~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", }; /* Language pt: Portuguese */ @@ -2675,10 +2731,10 @@ public final class KeyboardTextsSet { /* 12~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_rqm", - /* 47 */ "!text/double_9qm_rqm", + null, null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_rqm", + /* 53 */ "!text/double_9qm_rqm", }; /* Language ru: Russian */ @@ -2707,14 +2763,16 @@ public final class KeyboardTextsSet { /* ~42 */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* 43 */ "\u0451", - /* 44 */ null, + /* 44~ */ + null, null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", }; /* Language sk: Slovak */ @@ -2808,11 +2866,12 @@ public final class KeyboardTextsSet { /* 16~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language sl: Slovenian */ @@ -2836,12 +2895,12 @@ public final class KeyboardTextsSet { /* 13~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, - /* ~45 */ - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, + /* ~51 */ + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language sr: Serbian */ @@ -2881,16 +2940,19 @@ public final class KeyboardTextsSet { /* 43 */ "\u0450", // U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE /* 44 */ "\u045D", + /* 45~ */ + null, null, null, null, null, null, + /* ~50 */ // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language sv: Swedish */ @@ -2972,10 +3034,10 @@ public final class KeyboardTextsSet { /* 24 */ "\u00E6", /* 25~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, - /* ~47 */ - /* 48 */ "!text/single_raqm_laqm", - /* 49 */ "!text/double_raqm_laqm", + null, null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~53 */ + /* 54 */ "!text/single_raqm_laqm", + /* 55 */ "!text/double_raqm_laqm", }; /* Language sw: Swahili */ @@ -3035,17 +3097,18 @@ 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, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI - /* 45 */ "\u0E01\u0E02\u0E04", - /* 46~ */ + /* 51 */ "\u0E01\u0E02\u0E04", + /* 52~ */ null, null, null, null, null, - /* ~50 */ + /* ~56 */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT - /* 51 */ "\u0E3F", + /* 57 */ "\u0E3F", }; /* Language tl: Tagalog */ @@ -3175,20 +3238,20 @@ public final class KeyboardTextsSet { // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN /* 37 */ "\u044A", /* 38~ */ - null, null, null, null, null, null, null, - /* ~44 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, + /* ~50 */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE - /* 45 */ "\u0410\u0411\u0412", - /* 46 */ "!text/single_9qm_lqm", - /* 47 */ "!text/double_9qm_lqm", - /* 48~ */ + /* 51 */ "\u0410\u0411\u0412", + /* 52 */ "!text/single_9qm_lqm", + /* 53 */ "!text/double_9qm_lqm", + /* 54~ */ null, null, null, - /* ~50 */ + /* ~56 */ // U+20B4: "₴" HRYVNIA SIGN - /* 51 */ "\u20B4", + /* 57 */ "\u20B4", }; /* Language vi: Vietnamese */ @@ -3273,10 +3336,11 @@ public final class KeyboardTextsSet { /* 10~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - /* ~50 */ + null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, + /* ~56 */ // U+20AB: "₫" DONG SIGN - /* 51 */ "\u20AB", + /* 57 */ "\u20AB", }; /* Language zu: Zulu */ @@ -3449,12 +3513,14 @@ public final class KeyboardTextsSet { /* 19 */ "\u0175", }; + // TODO: Use the language + "_" + region representation for the locale string key. + // Currently we are dropping the region from the key. private static final Object[] LANGUAGES_AND_TEXTS = { "DEFAULT", LANGUAGE_DEFAULT, /* default */ "af", LANGUAGE_af, /* Afrikaans */ "ar", LANGUAGE_ar, /* Arabic */ "az", LANGUAGE_az, /* Azerbaijani */ - "be", LANGUAGE_be, /* Belarusian */ + "be" /* "be_BY" */, LANGUAGE_be_BY, /* Belarusian (Belarus) */ "bg", LANGUAGE_bg, /* Bulgarian */ "ca", LANGUAGE_ca, /* Catalan */ "cs", LANGUAGE_cs, /* Czech */ @@ -3464,26 +3530,26 @@ public final class KeyboardTextsSet { "en", LANGUAGE_en, /* English */ "eo", LANGUAGE_eo, /* Esperanto */ "es", LANGUAGE_es, /* Spanish */ - "et", LANGUAGE_et, /* Estonian */ + "et" /* "et_EE" */, LANGUAGE_et_EE, /* Estonian (Estonia) */ "fa", LANGUAGE_fa, /* Persian */ "fi", LANGUAGE_fi, /* Finnish */ "fr", LANGUAGE_fr, /* French */ "hi", LANGUAGE_hi, /* Hindi */ "hr", LANGUAGE_hr, /* Croatian */ "hu", LANGUAGE_hu, /* Hungarian */ - "hy", LANGUAGE_hy, /* Armenian */ + "hy" /* "hy_AM" */, LANGUAGE_hy_AM, /* Armenian (Armenia) */ "is", LANGUAGE_is, /* Icelandic */ "it", LANGUAGE_it, /* Italian */ "iw", LANGUAGE_iw, /* Hebrew */ - "ka", LANGUAGE_ka, /* Georgian */ + "ka" /* "ka_GE" */, LANGUAGE_ka_GE, /* Georgian (Georgia) */ "kk", LANGUAGE_kk, /* Kazakh */ - "km", LANGUAGE_km, /* Khmer */ + "km" /* "km_KH" */, LANGUAGE_km_KH, /* Khmer (Cambodia) */ "ky", LANGUAGE_ky, /* Kirghiz */ - "lo", LANGUAGE_lo, /* Lao */ + "lo" /* "lo_LA" */, LANGUAGE_lo_LA, /* Lao (Laos) */ "lt", LANGUAGE_lt, /* Lithuanian */ "lv", LANGUAGE_lv, /* Latvian */ "mk", LANGUAGE_mk, /* Macedonian */ - "mn", LANGUAGE_mn, /* Mongolian */ + "mn" /* "mn_MN" */, LANGUAGE_mn_MN, /* Mongolian (Mongolia) */ "nb", LANGUAGE_nb, /* Norwegian Bokmål */ "ne", LANGUAGE_ne, /* Nepali */ "nl", LANGUAGE_nl, /* Dutch */ diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java index a0935b985..111eb6db6 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java +++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java @@ -20,18 +20,24 @@ import android.util.Log; import android.view.MotionEvent; import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; import com.android.inputmethod.latin.utils.CoordinateUtils; public final class NonDistinctMultitouchHelper { private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName(); + // Use only main (id=0) pointer tracker. + private final PointerTracker mMainTracker; private int mOldPointerCount = 1; private Key mOldKey; private int[] mLastCoords = CoordinateUtils.newInstance(); - public void processMotionEvent(final MotionEvent me, final KeyEventHandler keyEventHandler) { + public NonDistinctMultitouchHelper(final PointerTracker mainTracker) { + mMainTracker = mainTracker; + } + + public void processMotionEvent(final MotionEvent me, final KeyDetector keyDetector) { final int pointerCount = me.getPointerCount(); final int oldPointerCount = mOldPointerCount; mOldPointerCount = pointerCount; @@ -41,8 +47,7 @@ public final class NonDistinctMultitouchHelper { return; } - // Use only main (id=0) pointer tracker. - final PointerTracker mainTracker = PointerTracker.getPointerTracker(0, keyEventHandler); + final PointerTracker mainTracker = mMainTracker; final int action = me.getActionMasked(); final int index = me.getActionIndex(); final long eventTime = me.getEventTime(); @@ -51,12 +56,12 @@ public final class NonDistinctMultitouchHelper { // In single-touch. if (oldPointerCount == 1 && pointerCount == 1) { if (me.getPointerId(index) == mainTracker.mPointerId) { - mainTracker.processMotionEvent(me, keyEventHandler); + mainTracker.processMotionEvent(me, keyDetector); return; } // Inject a copied event. injectMotionEvent(action, me.getX(index), me.getY(index), downTime, eventTime, - mainTracker, keyEventHandler); + mainTracker, keyDetector); return; } @@ -70,7 +75,7 @@ public final class NonDistinctMultitouchHelper { mOldKey = mainTracker.getKeyOn(x, y); // Inject an artifact up event for the old key. injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, - mainTracker, keyEventHandler); + mainTracker, keyDetector); return; } @@ -85,11 +90,11 @@ public final class NonDistinctMultitouchHelper { // Inject an artifact down event for the new key. // An artifact up event for the new key will usually be injected as a single-touch. injectMotionEvent(MotionEvent.ACTION_DOWN, x, y, downTime, eventTime, - mainTracker, keyEventHandler); + mainTracker, keyDetector); if (action == MotionEvent.ACTION_UP) { // Inject an artifact up event for the new key also. injectMotionEvent(MotionEvent.ACTION_UP, x, y, downTime, eventTime, - mainTracker, keyEventHandler); + mainTracker, keyDetector); } } return; @@ -101,11 +106,11 @@ public final class NonDistinctMultitouchHelper { private static void injectMotionEvent(final int action, final float x, final float y, final long downTime, final long eventTime, final PointerTracker tracker, - final KeyEventHandler handler) { + final KeyDetector keyDetector) { final MotionEvent me = MotionEvent.obtain( downTime, eventTime, action, x, y, 0 /* metaState */); try { - tracker.processMotionEvent(me, handler); + tracker.processMotionEvent(me, keyDetector); } finally { me.recycle(); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java index 7ee45e8f6..5ac34188c 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java @@ -28,7 +28,7 @@ public final class PointerTrackerQueue { public interface Element { public boolean isModifier(); - public boolean isInSlidingKeyInput(); + public boolean isInDraggingFinger(); public void onPhantomUpEvent(long eventTime); public void cancelTrackingForAction(); } @@ -193,13 +193,13 @@ public final class PointerTrackerQueue { } } - public boolean isAnyInSlidingKeyInput() { + public boolean isAnyInDraggingFinger() { synchronized (mExpandableArrayOfActivePointers) { final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers; final int arraySize = mArraySize; for (int index = 0; index < arraySize; index++) { final Element element = expandableArray.get(index); - if (element.isInSlidingKeyInput()) { + if (element.isInDraggingFinger()) { return true; } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java deleted file mode 100644 index d1ccdc7b5..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.keyboard.internal; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.ScrollView; - -/** - * This is an extended {@link ScrollView} that can notify - * {@link ScrollView#onScrollChanged(int,int,int,int} and - * {@link ScrollView#onOverScrolled(int,int,int,int)} to a content view. - */ -public class ScrollViewWithNotifier extends ScrollView { - private ScrollListener mScrollListener = EMPTY_LISTER; - - public interface ScrollListener { - public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY); - public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY); - } - - private static final ScrollListener EMPTY_LISTER = new ScrollListener() { - @Override - public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY) {} - @Override - public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX, - boolean clampedY) {} - }; - - public ScrollViewWithNotifier(final Context context, final AttributeSet attrs) { - super(context, attrs); - } - - @Override - protected void onScrollChanged(final int scrollX, final int scrollY, final int oldX, - final int oldY) { - super.onScrollChanged(scrollX, scrollY, oldX, oldY); - mScrollListener.notifyScrollChanged(scrollX, scrollY, oldX, oldY); - } - - @Override - protected void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX, - final boolean clampedY) { - super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); - mScrollListener.notifyOverScrolled(scrollX, scrollY, clampedX, clampedY); - } - - public void setScrollListener(final ScrollListener listener) { - mScrollListener = listener; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java new file mode 100644 index 000000000..88d0878d1 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.os.Message; +import android.os.SystemClock; +import android.view.ViewConfiguration; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; + +// TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. +public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { + public interface Callbacks { + public void startWhileTypingFadeinAnimation(); + public void startWhileTypingFadeoutAnimation(); + public void onLongPress(PointerTracker tracker); + } + + private static final int MSG_TYPING_STATE_EXPIRED = 0; + private static final int MSG_REPEAT_KEY = 1; + private static final int MSG_LONGPRESS_KEY = 2; + private static final int MSG_DOUBLE_TAP_SHIFT_KEY = 3; + private static final int MSG_UPDATE_BATCH_INPUT = 4; + + private final int mIgnoreAltCodeKeyTimeout; + private final int mGestureRecognitionUpdateTime; + + public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, + final int gestureRecognitionUpdateTime) { + super(ownerInstance); + mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; + mGestureRecognitionUpdateTime = gestureRecognitionUpdateTime; + } + + @Override + public void handleMessage(final Message msg) { + final Callbacks callbacks = getOwnerInstance(); + if (callbacks == null) { + return; + } + final PointerTracker tracker = (PointerTracker) msg.obj; + switch (msg.what) { + case MSG_TYPING_STATE_EXPIRED: + callbacks.startWhileTypingFadeinAnimation(); + break; + case MSG_REPEAT_KEY: + tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */); + break; + case MSG_LONGPRESS_KEY: + cancelLongPressTimers(); + callbacks.onLongPress(tracker); + break; + case MSG_UPDATE_BATCH_INPUT: + tracker.updateBatchInputByTimer(SystemClock.uptimeMillis()); + startUpdateBatchInputTimer(tracker); + break; + } + } + + @Override + public void startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount, + final int delay) { + final Key key = tracker.getKey(); + if (key == null || delay == 0) { + return; + } + sendMessageDelayed( + obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay); + } + + private void cancelKeyRepeatTimerOf(final PointerTracker tracker) { + removeMessages(MSG_REPEAT_KEY, tracker); + } + + public void cancelKeyRepeatTimers() { + removeMessages(MSG_REPEAT_KEY); + } + + // TODO: Suppress layout changes in key repeat mode + public boolean isInKeyRepeat() { + return hasMessages(MSG_REPEAT_KEY); + } + + @Override + public void startLongPressTimerOf(final PointerTracker tracker, final int delay) { + if (delay <= 0) { + return; + } + sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, tracker), delay); + } + + @Override + public void cancelLongPressTimerOf(final PointerTracker tracker) { + removeMessages(MSG_LONGPRESS_KEY, tracker); + } + + private void cancelLongPressTimers() { + removeMessages(MSG_LONGPRESS_KEY); + } + + @Override + public void startTypingStateTimer(final Key typedKey) { + if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) { + return; + } + + final boolean isTyping = isTypingState(); + removeMessages(MSG_TYPING_STATE_EXPIRED); + final Callbacks callbacks = getOwnerInstance(); + if (callbacks == null) { + return; + } + + // When user hits the space or the enter key, just cancel the while-typing timer. + final int typedCode = typedKey.getCode(); + if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) { + if (isTyping) { + callbacks.startWhileTypingFadeinAnimation(); + } + return; + } + + sendMessageDelayed( + obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout); + if (isTyping) { + return; + } + callbacks.startWhileTypingFadeoutAnimation(); + } + + @Override + public boolean isTypingState() { + return hasMessages(MSG_TYPING_STATE_EXPIRED); + } + + @Override + public void startDoubleTapShiftKeyTimer() { + sendMessageDelayed(obtainMessage(MSG_DOUBLE_TAP_SHIFT_KEY), + ViewConfiguration.getDoubleTapTimeout()); + } + + @Override + public void cancelDoubleTapShiftKeyTimer() { + removeMessages(MSG_DOUBLE_TAP_SHIFT_KEY); + } + + @Override + public boolean isInDoubleTapShiftKeyTimeout() { + return hasMessages(MSG_DOUBLE_TAP_SHIFT_KEY); + } + + @Override + public void cancelKeyTimersOf(final PointerTracker tracker) { + cancelKeyRepeatTimerOf(tracker); + cancelLongPressTimerOf(tracker); + } + + public void cancelAllKeyTimers() { + cancelKeyRepeatTimers(); + cancelLongPressTimers(); + } + + @Override + public void startUpdateBatchInputTimer(final PointerTracker tracker) { + if (mGestureRecognitionUpdateTime <= 0) { + return; + } + removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); + sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker), + mGestureRecognitionUpdateTime); + } + + @Override + public void cancelUpdateBatchInputTimer(final PointerTracker tracker) { + removeMessages(MSG_UPDATE_BATCH_INPUT, tracker); + } + + @Override + public void cancelAllUpdateBatchInputTimers() { + removeMessages(MSG_UPDATE_BATCH_INPUT); + } + + public void cancelAllMessages() { + cancelAllKeyTimers(); + cancelAllUpdateBatchInputTimers(); + } +} diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java index 463d09344..e6fb9807e 100644 --- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java @@ -27,15 +27,13 @@ import java.io.File; import java.io.IOException; import java.util.Map; -// TODO: Quit extending Dictionary after implementing dynamic binary dictionary. -abstract public class AbstractDictionaryWriter extends Dictionary { +abstract public class AbstractDictionaryWriter { /** Used for Log actions from this class */ private static final String TAG = AbstractDictionaryWriter.class.getSimpleName(); private final Context mContext; - public AbstractDictionaryWriter(final Context context, final String dictType) { - super(dictType); + public AbstractDictionaryWriter(final Context context) { mContext = context; } @@ -55,18 +53,16 @@ abstract public class AbstractDictionaryWriter extends Dictionary { // TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve. abstract public void addBigramWords(final String word0, final String word1, - final int frequency, final boolean isValid, - final long lastModifiedTime); + final int frequency, final boolean isValid, final long lastModifiedTime); abstract public void removeBigramWords(final String word0, final String word1); abstract protected void writeDictionary(final DictEncoder dictEncoder, final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException; - public void write(final String fileName, final Map<String, String> attributeMap) { - final String tempFileName = fileName + ".temp"; - final File file = new File(mContext.getFilesDir(), fileName); - final File tempFile = new File(mContext.getFilesDir(), tempFileName); + public void write(final File file, final Map<String, String> attributeMap) { + final String tempFilePath = file.getAbsolutePath() + ".temp"; + final File tempFile = new File(tempFilePath); try { final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile); writeDictionary(dictEncoder, attributeMap); diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java index 875192554..fd6c24dfe 100644 --- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java +++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.utils.FileUtils; + import java.io.File; /** @@ -52,4 +54,12 @@ public final class AssetFileAddress { if (!f.isFile()) return null; return new AssetFileAddress(filename, offset, length); } + + public boolean pointsToPhysicalFile() { + return 0 == mOffset; + } + + public void deleteUnderlyingFile() { + FileUtils.deleteRecursively(new File(mFilename)); + } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index fd296988e..db4234c63 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -26,6 +26,7 @@ import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.UnigramProperty; import java.io.File; import java.util.ArrayList; @@ -57,6 +58,21 @@ public final class BinaryDictionary extends Dictionary { @UsedForTesting public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; + public static final int NOT_A_VALID_TIMESTAMP = -1; + + // Format to get unigram flags from native side via getUnigramPropertyNative(). + private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT = 4; + private static final int FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX = 0; + private static final int FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX = 1; + private static final int FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX = 2; + private static final int FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX = 3; + + // Format to get unigram historical info from native side via getUnigramPropertyNative(). + private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT = 3; + private static final int FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX = 0; + private static final int FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX = 1; + private static final int FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX = 2; + private long mNativeDict; private final Locale mLocale; private final long mDictSize; @@ -123,8 +139,13 @@ public final class BinaryDictionary extends Dictionary { private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC); private static native void flushWithGCNative(long dict, String filePath); private static native void closeNative(long dict); + private static native int getFormatVersionNative(long dict); private static native int getProbabilityNative(long dict, int[] word); private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1); + private static native void getUnigramPropertyNative(long dict, int[] word, + int[] outCodePoints, boolean[] outFlags, int[] outProbability, + int[] outHistoricalInfo, ArrayList<int[]> outShortcutTargets, + ArrayList<Integer> outShortcutProbabilities); 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, @@ -133,10 +154,14 @@ public final class BinaryDictionary extends Dictionary { int[] outputAutoCommitFirstWordConfidence); private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); private static native int editDistanceNative(int[] before, int[] after); - private static native void addUnigramWordNative(long dict, int[] word, int probability); + private static native void addUnigramWordNative(long dict, int[] word, int probability, + int[] shortcutTarget, int shortcutProbability, boolean isNotAWord, + boolean isBlacklisted, int timestamp); private static native void addBigramWordsNative(long dict, int[] word0, int[] word1, - int probability); + int probability, int timestamp); private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1); + private static native int addMultipleDictionaryEntriesNative(long dict, + LanguageModelParam[] languageModelParams, int startIndex); private static native int calculateProbabilityNative(long dict, int unigramProbability, int bigramProbability); private static native String getPropertyNative(long dict, String query); @@ -235,6 +260,10 @@ public final class BinaryDictionary extends Dictionary { return mNativeDict != 0; } + public int getFormatVersion() { + return getFormatVersionNative(mNativeDict); + } + public static float calcNormalizedScore(final String before, final String after, final int score) { return calcNormalizedScoreNative(StringUtils.toCodePointArray(before), @@ -274,23 +303,55 @@ public final class BinaryDictionary extends Dictionary { return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1); } - // Add a unigram entry to binary dictionary in native code. - public void addUnigramWord(final String word, final int probability) { + @UsedForTesting + public UnigramProperty getUnigramProperty(final String word) { + if (TextUtils.isEmpty(word)) { + return null; + } + final int[] codePoints = StringUtils.toCodePointArray(word); + final int[] outCodePoints = new int[MAX_WORD_LENGTH]; + final boolean[] outFlags = new boolean[FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT]; + final int[] outProbability = new int[1]; + final int[] outHistoricalInfo = + new int[FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT]; + final ArrayList<int[]> outShortcutTargets = CollectionUtils.newArrayList(); + final ArrayList<Integer> outShortcutProbabilities = CollectionUtils.newArrayList(); + getUnigramPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbability, + outHistoricalInfo, outShortcutTargets, outShortcutProbabilities); + return new UnigramProperty(codePoints, + outFlags[FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX], + outFlags[FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX], + outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX], + outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX], outProbability[0], + outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX], + outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX], + outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX], + outShortcutTargets, outShortcutProbabilities); + } + + // Add a unigram entry to binary dictionary with unigram attributes in native code. + public void addUnigramWord(final String word, final int probability, + final String shortcutTarget, final int shortcutProbability, final boolean isNotAWord, + final boolean isBlacklisted, final int timestamp) { if (TextUtils.isEmpty(word)) { return; } final int[] codePoints = StringUtils.toCodePointArray(word); - addUnigramWordNative(mNativeDict, codePoints, probability); + final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? + StringUtils.toCodePointArray(shortcutTarget) : null; + addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, + shortcutProbability, isNotAWord, isBlacklisted, timestamp); } - // Add a bigram entry to binary dictionary in native code. - public void addBigramWords(final String word0, final String word1, final int probability) { + // Add a bigram entry to binary dictionary with timestamp in native code. + public void addBigramWords(final String word0, final String word1, final int probability, + final int timestamp) { if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { return; } final int[] codePoints0 = StringUtils.toCodePointArray(word0); final int[] codePoints1 = StringUtils.toCodePointArray(word1); - addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability); + addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp); } // Remove a bigram entry form binary dictionary in native code. @@ -303,10 +364,70 @@ public final class BinaryDictionary extends Dictionary { removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); } + public static class LanguageModelParam { + public final int[] mWord0; + public final int[] mWord1; + public final int[] mShortcutTarget; + public final int mUnigramProbability; + public final int mBigramProbability; + public final int mShortcutProbability; + public final boolean mIsNotAWord; + public final boolean mIsBlacklisted; + public final int mTimestamp; + + // Constructor for unigram. + public LanguageModelParam(final String word, final int unigramProbability, + final int timestamp) { + mWord0 = null; + mWord1 = StringUtils.toCodePointArray(word); + mShortcutTarget = null; + mUnigramProbability = unigramProbability; + mBigramProbability = NOT_A_PROBABILITY; + mShortcutProbability = NOT_A_PROBABILITY; + mIsNotAWord = false; + mIsBlacklisted = false; + mTimestamp = timestamp; + } + + // Constructor for unigram and bigram. + public LanguageModelParam(final String word0, final String word1, + final int unigramProbability, final int bigramProbability, + final int timestamp) { + mWord0 = StringUtils.toCodePointArray(word0); + mWord1 = StringUtils.toCodePointArray(word1); + mShortcutTarget = null; + mUnigramProbability = unigramProbability; + mBigramProbability = bigramProbability; + mShortcutProbability = NOT_A_PROBABILITY; + mIsNotAWord = false; + mIsBlacklisted = false; + mTimestamp = timestamp; + } + } + + public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { + if (!isValidDictionary()) return; + int processedParamCount = 0; + while (processedParamCount < languageModelParams.length) { + if (needsToRunGC(true /* mindsBlockByGC */)) { + flushWithGC(); + } + processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict, + languageModelParams, processedParamCount); + if (processedParamCount <= 0) { + return; + } + } + + } + private void reopen() { close(); final File dictFile = new File(mDictFilePath); - mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */, + // WARNING: Because we pass 0 as the offset and file.length() as the length, this can + // only be called for actual files. Right now it's only called by the flush() family of + // functions, which require an updatable dictionary, so it's okay. But beware. + loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */, dictFile.length(), true /* isUpdatable */); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 722a82961..b4382bc2c 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -98,7 +98,7 @@ public final class BinaryDictionaryFileDumper { * This creates a URI builder able to build a URI pointing to the dictionary * pack content provider for a specific dictionary id. */ - private static Uri.Builder getProviderUriBuilder(final String path) { + public static Uri.Builder getProviderUriBuilder(final String path) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(DictionaryPackConstants.AUTHORITY).appendPath(path); } @@ -339,15 +339,25 @@ public final class BinaryDictionaryFileDumper { Log.e(TAG, "Could not copy a word list. Will not be able to use it."); // If we can't copy it we should warn the dictionary provider so that it can mark it // as invalid. - wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT, - QUERY_PARAMETER_FAILURE); + reportBrokenFileToDictionaryProvider(providerClient, clientId, wordlistId); + } + + public static boolean reportBrokenFileToDictionaryProvider( + final ContentProviderClient providerClient, final String clientId, + final String wordlistId) { try { + final Uri.Builder wordListUriBuilder = getContentUriBuilderForType(clientId, + providerClient, QUERY_PATH_DATAFILE, wordlistId /* extraPath */); + wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT, + QUERY_PARAMETER_FAILURE); if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) { - Log.e(TAG, "In addition, we were unable to delete it."); + Log.e(TAG, "Unable to delete a word list."); } } catch (RemoteException e) { - Log.e(TAG, "In addition, communication with the dictionary provider was cut", e); + Log.e(TAG, "Communication with the dictionary provider was cut", e); + return false; } + return true; } // Ideally the two following methods should be merged, but AssetFileDescriptor does not @@ -432,8 +442,9 @@ public final class BinaryDictionaryFileDumper { // Actually copy the file final byte[] buffer = new byte[FILE_READ_BUFFER_SIZE]; - for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) + for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) { output.write(buffer, 0, readBytes); + } input.close(); } @@ -478,8 +489,7 @@ public final class BinaryDictionaryFileDumper { * @param context the context for resources and providers. * @param clientId the client ID to use. */ - public static void initializeClientRecordHelper(final Context context, - final String clientId) { + public static void initializeClientRecordHelper(final Context context, final String clientId) { try { final ContentProviderClient client = context.getContentResolver(). acquireContentProviderClient(getProviderUriBuilder("").build()); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 9a9653094..c260434d5 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -70,38 +70,47 @@ public final class Constants { public static final class ExtraValue { /** - * The subtype extra value used to indicate that the subtype keyboard layout is capable - * for typing ASCII characters. + * The subtype extra value used to indicate that this subtype is capable of + * entering ASCII characters. */ 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. + * The subtype extra value used to indicate that this subtype is enabled + * when the default subtype is not marked as ascii capable. + */ + public static final String ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = + "EnabledWhenDefaultIsNotAsciiCapable"; + + /** + * The subtype extra value used to indicate that this subtype is capable of + * entering emoji characters. */ public static final String EMOJI_CAPABLE = "EmojiCapable"; + /** - * The subtype extra value used to indicate that the subtype require network connection - * to work. + * The subtype extra value used to indicate that this subtype requires a network + * connection to work. */ public static final String REQ_NETWORK_CONNECTIVITY = "requireNetworkConnectivity"; /** - * The subtype extra value used to indicate that the subtype display name contains "%s" - * for replacement mark and it should be replaced by this extra value. + * The subtype extra value used to indicate that the display name of this subtype + * contains a "%s" for printf-like replacement and it should be replaced by + * this extra value. * This extra value is supported on JellyBean and later. */ public static final String UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME = "UntranslatableReplacementStringInSubtypeName"; /** - * The subtype extra value used to indicate that the subtype keyboard layout set name. + * The subtype extra value used to indicate this subtype keyboard layout set name. * This extra value is private to LatinIME. */ public static final String KEYBOARD_LAYOUT_SET = "KeyboardLayoutSet"; /** - * The subtype extra value used to indicate that the subtype is additional subtype + * The subtype extra value used to indicate that this subtype is an additional subtype * that the user defined. This extra value is private to LatinIME. */ public static final String IS_ADDITIONAL_SUBTYPE = "isAdditionalSubtype"; diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 47891c6b7..b6cfcd064 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -44,7 +44,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private static final String TAG = ContactsBinaryDictionary.class.getSimpleName(); private static final String NAME = "contacts"; - private static boolean DEBUG = false; + private static final boolean DEBUG = false; + private static final boolean DEBUG_DUMP = false; /** * Frequency for contacts information into the dictionary @@ -71,8 +72,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { private final boolean mUseFirstLastBigrams; public ContactsBinaryDictionary(final Context context, final Locale locale) { - super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS, - false /* isUpdatable */); + super(context, getDictNameWithLocale(NAME, locale), locale, + Dictionary.TYPE_CONTACTS, false /* isUpdatable */); mLocale = locale; mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); registerObserver(context); @@ -168,6 +169,10 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { if (isValidName(name)) { addName(name); ++count; + } else { + if (DEBUG_DUMP) { + Log.d(TAG, "Invalid name: " + name); + } } cursor.moveToNext(); } @@ -204,6 +209,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { if (Character.isLetter(name.codePointAt(i))) { int end = getWordEndPosition(name, len, i); String word = name.substring(i, end); + if (DEBUG_DUMP) { + Log.d(TAG, "addName word = " + word); + } i = end - 1; // Don't add single letter words, possibly confuses // capitalization of i. diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index fa79f5af7..e04524843 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -52,13 +52,10 @@ public abstract class Dictionary { public static final String TYPE_CONTACTS = "contacts"; // User dictionary, the system-managed one. public static final String TYPE_USER = "user"; - // User history dictionary internal to LatinIME. This assumes bigram prediction for now. + // User history dictionary internal to LatinIME. public static final String TYPE_USER_HISTORY = "history"; - // Personalization binary dictionary internal to LatinIME. + // Personalization dictionary. public static final String TYPE_PERSONALIZATION = "personalization"; - // Personalization prediction dictionary internal to LatinIME's Java code. - public static final String TYPE_PERSONALIZATION_PREDICTION_IN_JAVA = - "personalization_prediction_in_java"; public final String mDictType; public Dictionary(final String dictType) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 828e54f14..e09c309ea 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.content.ContentProviderClient; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; @@ -64,6 +65,10 @@ public final class DictionaryFactory { useFullEditDistance, locale, Dictionary.TYPE_MAIN); if (readOnlyBinaryDictionary.isValidDictionary()) { dictList.add(readOnlyBinaryDictionary); + } else { + readOnlyBinaryDictionary.close(); + // Prevent this dictionary to do any further harm. + killDictionary(context, f); } } } @@ -75,6 +80,51 @@ public final class DictionaryFactory { } /** + * Kills a dictionary so that it is never used again, if possible. + * @param context The context to contact the dictionary provider, if possible. + * @param f A file address to the dictionary to kill. + */ + private static void killDictionary(final Context context, final AssetFileAddress f) { + if (f.pointsToPhysicalFile()) { + f.deleteUnderlyingFile(); + // Warn the dictionary provider if the dictionary came from there. + final ContentProviderClient providerClient; + try { + providerClient = context.getContentResolver().acquireContentProviderClient( + BinaryDictionaryFileDumper.getProviderUriBuilder("").build()); + } catch (final SecurityException e) { + Log.e(TAG, "No permission to communicate with the dictionary provider", e); + return; + } + if (null == providerClient) { + Log.e(TAG, "Can't establish communication with the dictionary provider"); + return; + } + final String wordlistId = + DictionaryInfoUtils.getWordListIdFromFileName(new File(f.mFilename).getName()); + if (null != wordlistId) { + // TODO: this is a reasonable last resort, but it is suboptimal. + // The following will remove the entry for this dictionary with the dictionary + // provider. When the metadata is downloaded again, we will try downloading it + // again. + // However, in the practice that will mean the user will find themselves without + // the new dictionary. That's fine for languages where it's included in the APK, + // but for other languages it will leave the user without a dictionary at all until + // the next update, which may be a few days away. + // Ideally, we would trigger a new download right away, and use increasing retry + // delays for this particular id/version combination. + // Then again, this is expected to only ever happen in case of human mistake. If + // the wrong file is on the server, the following is still doing the right thing. + // If it's a file left over from the last version however, it's not great. + BinaryDictionaryFileDumper.reportBrokenFileToDictionaryProvider( + providerClient, + context.getString(R.string.dictionary_pack_client_id), + wordlistId); + } + } + } + + /** * Initializes a main dictionary collection from a dictionary pack, with default flags. * * This searches for a content provider providing a dictionary pack for the specified diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java index 3df2a2b63..f960c5343 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -37,14 +37,14 @@ import java.util.Map; * An in memory dictionary for memorizing entries and writing a binary dictionary. */ public class DictionaryWriter extends AbstractDictionaryWriter { - private static final int BINARY_DICT_VERSION = 3; + private static final int BINARY_DICT_VERSION = 2; private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */); + new FormatSpec.FormatOptions(BINARY_DICT_VERSION, false /* supportsDynamicUpdate */); private FusionDictionary mFusionDictionary; - public DictionaryWriter(final Context context, final String dictType) { - super(context, dictType); + public DictionaryWriter(final Context context) { + super(context); clear(); } @@ -92,18 +92,4 @@ public class DictionaryWriter extends AbstractDictionaryWriter { } dictEncoder.writeDictionary(mFusionDictionary, FORMAT_OPTIONS); } - - @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - // This class doesn't support suggestion. - return null; - } - - @Override - public boolean isValidWord(String word) { - // This class doesn't support dictionary retrieval. - return false; - } } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index eb8650e6f..9f5cd162f 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -22,18 +22,22 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; import com.android.inputmethod.latin.makedict.FormatSpec; -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.FileUtils; import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -52,10 +56,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** Whether to print debug output to log */ private static boolean DEBUG = false; - - // TODO: Remove. - /** Whether to call binary dictionary dynamically updating methods. */ - public static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = true; + private static final boolean DBG_STRESS_TEST = false; private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; @@ -64,22 +65,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; - private static final int DICTIONARY_FORMAT_VERSION = 3; - - private static final String SUPPORTS_DYNAMIC_UPDATE = - FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE; + private static final int DICTIONARY_FORMAT_VERSION = FormatSpec.VERSION4; /** * A static map of update controllers, each of which records the time of accesses to a single * binary dictionary file and tracks whether the file is regenerating. The key for this map is - * the filename and the value is the shared dictionary time recorder associated with that - * filename. + * the dictionary name and the value is the shared dictionary time recorder associated with + * that dictionary name. */ private static final ConcurrentHashMap<String, DictionaryUpdateController> - sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap(); + sDictNameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap(); private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> - sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap(); + sDictNameExecutorMap = CollectionUtils.newConcurrentHashMap(); /** The application context. */ protected final Context mContext; @@ -95,18 +93,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected AbstractDictionaryWriter mDictionaryWriter; /** - * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple - * dictionary instances with the same filename is supported, with access controlled by - * DictionaryTimeRecorder. + * The name of this dictionary, used as a part of the filename for storing the binary + * dictionary. Multiple dictionary instances with the same name is supported, with access + * controlled by DictionaryUpdateController. */ - private final String mFilename; + private final String mDictName; + + /** Dictionary locale */ + private final Locale mLocale; /** Whether to support dynamically updating the dictionary */ private final boolean mIsUpdatable; + /** Dictionary file */ + private final File mDictFile; + // TODO: remove, once dynamic operations is serialized /** Controls updating the shared binary dictionary file across multiple instances. */ - private final DictionaryUpdateController mFilenameDictionaryUpdateController; + private final DictionaryUpdateController mDictNameDictionaryUpdateController; // TODO: remove, once dynamic operations is serialized /** Controls updating the local binary dictionary for this instance. */ @@ -132,45 +136,57 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected abstract boolean hasContentChanged(); + protected boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { + // This class is using format 2 because it's used by the User and Contacts dictionary + // only, which right now use format 2 (dicts using format 4 use Decaying*, which overrides + // this method). + // TODO: Migrate these dicts to ver4 format, and remove this function. + return formatVersion == 2; + } + + public boolean isValidDictionary() { + return mBinaryDictionary.isValidDictionary(); + } + + private File getDictFile() { + return mDictFile; + } + /** - * Gets the dictionary update controller for the given filename. + * Gets the dictionary update controller for the given dictionary name. */ private static DictionaryUpdateController getDictionaryUpdateController( - String filename) { - DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename); + final String dictName) { + DictionaryUpdateController recorder = sDictNameDictionaryUpdateControllerMap.get(dictName); if (recorder == null) { - synchronized(sFilenameDictionaryUpdateControllerMap) { + synchronized(sDictNameDictionaryUpdateControllerMap) { recorder = new DictionaryUpdateController(); - sFilenameDictionaryUpdateControllerMap.put(filename, recorder); + sDictNameDictionaryUpdateControllerMap.put(dictName, recorder); } } return recorder; } /** - * Gets the executor for the given filename. + * Gets the executor for the given dictionary name. */ - private static PrioritizedSerialExecutor getExecutor(final String filename) { - PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename); + private static PrioritizedSerialExecutor getExecutor(final String dictName) { + PrioritizedSerialExecutor executor = sDictNameExecutorMap.get(dictName); if (executor == null) { - synchronized(sFilenameExecutorMap) { + synchronized(sDictNameExecutorMap) { executor = new PrioritizedSerialExecutor(); - sFilenameExecutorMap.put(filename, executor); + sDictNameExecutorMap.put(dictName, executor); } } return executor; } private static AbstractDictionaryWriter getDictionaryWriter(final Context context, - final String dictType, final boolean isDynamicPersonalizationDictionary) { + final boolean isDynamicPersonalizationDictionary) { if (isDynamicPersonalizationDictionary) { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - return null; - } else { - return new DynamicPersonalizationDictionaryWriter(context, dictType); - } + return null; } else { - return new DictionaryWriter(context, dictType); + return new DictionaryWriter(context); } } @@ -178,26 +194,37 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Creates a new expandable binary dictionary. * * @param context The application context of the parent. - * @param filename The filename for this binary dictionary. Multiple dictionaries with the same - * filename is supported. + * @param dictName The name of the dictionary. Multiple instances with the same + * name is supported. + * @param locale the dictionary locale. * @param dictType the dictionary type, as a human-readable string * @param isUpdatable whether to support dynamically updating the dictionary. Please note that * dynamic dictionary has negative effects on memory space and computation time. */ - public ExpandableBinaryDictionary(final Context context, final String filename, - final String dictType, final boolean isUpdatable) { + public ExpandableBinaryDictionary(final Context context, final String dictName, + final Locale locale, final String dictType, final boolean isUpdatable) { + this(context, dictName, locale, dictType, isUpdatable, + new File(context.getFilesDir(), dictName + DICT_FILE_EXTENSION)); + } + + // Creates an instance that uses a given dictionary file. + public ExpandableBinaryDictionary(final Context context, final String dictName, + final Locale locale, final String dictType, final boolean isUpdatable, + final File dictFile) { super(dictType); - mFilename = filename; + mDictName = dictName; mContext = context; + mLocale = locale; mIsUpdatable = isUpdatable; + mDictFile = dictFile; mBinaryDictionary = null; - mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename); + mDictNameDictionaryUpdateController = getDictionaryUpdateController(dictName); // Currently, only dynamic personalization dictionary is updatable. - mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable); + mDictionaryWriter = getDictionaryWriter(context, isUpdatable); } - protected static String getFilenameWithLocale(final String name, final String localeStr) { - return name + "." + localeStr + DICT_FILE_EXTENSION; + protected static String getDictNameWithLocale(final String name, final Locale locale) { + return name + "." + locale.toString(); } /** @@ -205,23 +232,20 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ @Override public void close() { - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { if (mBinaryDictionary!= null) { mBinaryDictionary.close(); mBinaryDictionary = null; } - if (mDictionaryWriter != null) { - mDictionaryWriter.close(); - } } }); } protected void closeBinaryDictionary() { // Ensure that no other threads are accessing the local binary dictionary. - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { if (mBinaryDictionary != null) { @@ -234,19 +258,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected Map<String, String> getHeaderAttributeMap() { HashMap<String, String> attributeMap = new HashMap<String, String>(); - attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE, - SUPPORTS_DYNAMIC_UPDATE); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFilename); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString()); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE, + String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); return attributeMap; } protected void clear() { - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) { + if (mDictionaryWriter == null) { mBinaryDictionary.close(); - final File file = new File(mContext.getFilesDir(), mFilename); + final File file = getDictFile(); + if (file.exists() && !FileUtils.deleteRecursively(file)) { + Log.e(TAG, "Can't remove a file: " + file.getName()); + } BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); mBinaryDictionary = new BinaryDictionary( @@ -286,8 +314,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Check whether GC is needed and run GC if required. */ protected void runGCIfRequired(final boolean mindsBlockByGC) { - if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { runGCIfRequiredInternalLocked(mindsBlockByGC); @@ -296,18 +323,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) { - if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; // Calls to needsToRunGC() need to be serialized. if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) { - if (setIsRegeneratingIfNotRegenerating()) { + if (setProcessingLargeTaskIfNot()) { // Run GC after currently existing time sensitive operations. - getExecutor(mFilename).executePrioritized(new Runnable() { + getExecutor(mDictName).executePrioritized(new Runnable() { @Override public void run() { try { mBinaryDictionary.flushWithGC(); } finally { - mFilenameDictionaryUpdateController.mIsRegenerating.set(false); + mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false); } } }); @@ -318,23 +344,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry. */ - protected void addWordDynamically(final String word, final String shortcutTarget, - final int frequency, final int shortcutFreq, final boolean isNotAWord) { + protected void addWordDynamically(final String word, final int frequency, + final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, + final boolean isBlacklisted, final int timestamp) { if (!mIsUpdatable) { - Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename); + Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mDictName); return; } - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); - mBinaryDictionary.addUnigramWord(word, frequency); - } else { - // TODO: Remove. - mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, - isNotAWord); - } + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); + mBinaryDictionary.addUnigramWord(word, frequency, shortcutTarget, shortcutFreq, + isNotAWord, isBlacklisted, timestamp); } }); } @@ -343,23 +365,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry. */ protected void addBigramDynamically(final String word0, final String word1, - final int frequency, final boolean isValid) { + final int frequency, final int timestamp) { if (!mIsUpdatable) { Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: " - + mFilename); + + mDictName); return; } - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); - mBinaryDictionary.addBigramWords(word0, word1, frequency); - } else { - // TODO: Remove. - mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid, - 0 /* lastTouchedTime */); - } + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); + mBinaryDictionary.addBigramWords(word0, word1, frequency, timestamp); } }); } @@ -370,18 +386,48 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected void removeBigramDynamically(final String word0, final String word1) { if (!mIsUpdatable) { Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: " - + mFilename); + + mDictName); return; } - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); - mBinaryDictionary.removeBigramWords(word0, word1); - } else { - // TODO: Remove. - mDictionaryWriter.removeBigramWords(word0, word1); + runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); + mBinaryDictionary.removeBigramWords(word0, word1); + } + }); + } + + public interface AddMultipleDictionaryEntriesCallback { + public void onFinished(); + } + + /** + * Dynamically add multiple entries to the dictionary. + */ + protected void addMultipleDictionaryEntriesDynamically( + final ArrayList<LanguageModelParam> languageModelParams, + final AddMultipleDictionaryEntriesCallback callback) { + if (!mIsUpdatable) { + Log.w(TAG, "addMultipleDictionaryEntriesDynamically is called for non-updatable " + + "dictionary: " + mDictName); + return; + } + getExecutor(mDictName).execute(new Runnable() { + @Override + public void run() { + final boolean locked = setProcessingLargeTaskIfNot(); + try { + mBinaryDictionary.addMultipleDictionaryEntries( + languageModelParams.toArray( + new LanguageModelParam[languageModelParams.size()])); + } finally { + if (callback != null) { + callback.onFinished(); + } + if (locked) { + mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false); + } } } }); @@ -393,49 +439,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId) { reloadDictionaryIfRequired(); - if (isRegenerating()) { + if (processingLargeTask()) { return null; } - final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = new AsyncResultHolder<ArrayList<SuggestedWordInfo>>(); - getExecutor(mFilename).executePrioritized(new Runnable() { + getExecutor(mDictName).executePrioritized(new Runnable() { @Override public void run() { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - if (mBinaryDictionary == null) { - holder.set(null); - return; - } - final ArrayList<SuggestedWordInfo> binarySuggestion = - 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 { - holder.set(inMemDictSuggestion); - } + if (mBinaryDictionary == null) { + holder.set(null); + return; } + final ArrayList<SuggestedWordInfo> binarySuggestion = + mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, + proximityInfo, blockOffensiveWords, additionalFeaturesOptions, + sessionId); + holder.set(binarySuggestion); } }); return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); @@ -456,11 +476,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } protected boolean isValidWordInner(final String word) { - if (isRegenerating()) { + if (processingLargeTask()) { return false; } final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); - getExecutor(mFilename).executePrioritized(new Runnable() { + getExecutor(mDictName).executePrioritized(new Runnable() { @Override public void run() { holder.set(isValidWordLocked(word)); @@ -494,12 +514,22 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private void loadBinaryDictionary() { if (DEBUG) { - Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" - + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update=" - + mFilenameDictionaryUpdateController.mLastUpdateTime); + Log.d(TAG, "Loading binary dictionary: " + mDictName + " request=" + + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update=" + + mDictNameDictionaryUpdateController.mLastUpdateTime); + } + if (DBG_STRESS_TEST) { + // Test if this class does not cause problems when it takes long time to load binary + // dictionary. + try { + Log.w(TAG, "Start stress in loading: " + mDictName); + Thread.sleep(15000); + Log.w(TAG, "End stress in loading"); + } catch (InterruptedException e) { + } } - final File file = new File(mContext.getFilesDir(), mFilename); + final File file = getDictFile(); final String filename = file.getAbsolutePath(); final long length = file.length(); @@ -511,7 +541,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // swapping in the new one. // TODO: Ensure multi-thread assignment of mBinaryDictionary. final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; - getExecutor(mFilename).executePrioritized(new Runnable() { + getExecutor(mDictName).executePrioritized(new Runnable() { @Override public void run() { mBinaryDictionary = newBinaryDictionary; @@ -533,29 +563,31 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ private void writeBinaryDictionary() { if (DEBUG) { - Log.d(TAG, "Generating binary dictionary: " + mFilename + " request=" - + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update=" - + mFilenameDictionaryUpdateController.mLastUpdateTime); + Log.d(TAG, "Generating binary dictionary: " + mDictName + " request=" + + mDictNameDictionaryUpdateController.mLastUpdateRequestTime + " update=" + + mDictNameDictionaryUpdateController.mLastUpdateTime); } if (needsToReloadBeforeWriting()) { mDictionaryWriter.clear(); loadDictionaryAsync(); - mDictionaryWriter.write(mFilename, getHeaderAttributeMap()); + mDictionaryWriter.write(getDictFile(), getHeaderAttributeMap()); } else { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) { - final File file = new File(mContext.getFilesDir(), mFilename); - BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), - DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); - } else { - if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { - mBinaryDictionary.flushWithGC(); - } else { - mBinaryDictionary.flush(); - } + if (mBinaryDictionary == null || !isValidDictionary() + // TODO: remove the check below + || !matchesExpectedBinaryDictFormatVersionForThisType( + mBinaryDictionary.getFormatVersion())) { + final File file = getDictFile(); + if (file.exists() && !FileUtils.deleteRecursively(file)) { + Log.e(TAG, "Can't remove a file: " + file.getName()); } + BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), + DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); } else { - mDictionaryWriter.write(mFilename, getHeaderAttributeMap()); + if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { + mBinaryDictionary.flushWithGC(); + } else { + mBinaryDictionary.flush(); + } } } } @@ -570,10 +602,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected void setRequiresReload(final boolean requiresRebuild) { final long time = SystemClock.uptimeMillis(); mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time; - mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time; + mDictNameDictionaryUpdateController.mLastUpdateRequestTime = time; if (DEBUG) { - Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update=" - + mFilenameDictionaryUpdateController.mLastUpdateTime); + Log.d(TAG, "Reload request: " + mDictName + ": request=" + time + " update=" + + mDictNameDictionaryUpdateController.mLastUpdateTime); } } @@ -582,7 +614,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ public final void reloadDictionaryIfRequired() { if (!isReloadRequired()) return; - if (setIsRegeneratingIfNotRegenerating()) { + if (setProcessingLargeTaskIfNot()) { reloadDictionary(); } } @@ -594,13 +626,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate(); } - private boolean isRegenerating() { - return mFilenameDictionaryUpdateController.mIsRegenerating.get(); + private boolean processingLargeTask() { + return mDictNameDictionaryUpdateController.mProcessingLargeTask.get(); } - // Returns whether the dictionary can be regenerated. - private boolean setIsRegeneratingIfNotRegenerating() { - return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet( + // Returns whether the dictionary is being used for a large task. If true, we should not use + // this dictionary for latency sensitive operations. + private boolean setProcessingLargeTaskIfNot() { + return mDictNameDictionaryUpdateController.mProcessingLargeTask.compareAndSet( false /* expect */ , true /* update */); } @@ -611,13 +644,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private final void reloadDictionary() { // Ensure that only one thread attempts to read or write to the shared binary dictionary // file at the same time. - getExecutor(mFilename).execute(new Runnable() { + getExecutor(mDictName).execute(new Runnable() { @Override public void run() { try { final long time = SystemClock.uptimeMillis(); final boolean dictionaryFileExists = dictionaryFileExists(); - if (mFilenameDictionaryUpdateController.isOutOfDate() + if (mDictNameDictionaryUpdateController.isOutOfDate() || !dictionaryFileExists) { // If the shared dictionary file does not exist or is out of date, the // first instance that acquires the lock will generate a new one. @@ -626,31 +659,44 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // rebuild the binary dictionary. Empty dictionaries are supported (in // the case where loadDictionaryAsync() adds nothing) in order to // provide a uniform framework. - mFilenameDictionaryUpdateController.mLastUpdateTime = time; + mDictNameDictionaryUpdateController.mLastUpdateTime = time; writeBinaryDictionary(); loadBinaryDictionary(); } else { // If not, the reload request was unnecessary so revert // LastUpdateRequestTime to LastUpdateTime. - mFilenameDictionaryUpdateController.mLastUpdateRequestTime = - mFilenameDictionaryUpdateController.mLastUpdateTime; + mDictNameDictionaryUpdateController.mLastUpdateRequestTime = + mDictNameDictionaryUpdateController.mLastUpdateTime; } } else if (mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.mLastUpdateTime - < mFilenameDictionaryUpdateController.mLastUpdateTime) { + < mDictNameDictionaryUpdateController.mLastUpdateTime) { // Otherwise, if the local dictionary is older than the shared dictionary, // load the shared dictionary. loadBinaryDictionary(); } - if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) { - // Binary dictionary is not valid. Regenerate the dictionary file. - mFilenameDictionaryUpdateController.mLastUpdateTime = time; - writeBinaryDictionary(); - loadBinaryDictionary(); - } - mPerInstanceDictionaryUpdateController.mLastUpdateTime = time; + // If we just loaded the binary dictionary, then mBinaryDictionary is not + // up-to-date yet so it's useless to test it right away. Schedule the check + // for right after it's loaded instead. + getExecutor(mDictName).executePrioritized(new Runnable() { + @Override + public void run() { + if (mBinaryDictionary != null && !(isValidDictionary() + // TODO: remove the check below + && matchesExpectedBinaryDictFormatVersionForThisType( + mBinaryDictionary.getFormatVersion()))) { + // Binary dictionary or its format version is not valid. Regenerate + // the dictionary file. writeBinaryDictionary will remove the + // existing files if appropriate. + mDictNameDictionaryUpdateController.mLastUpdateTime = time; + writeBinaryDictionary(); + loadBinaryDictionary(); + } + mPerInstanceDictionaryUpdateController.mLastUpdateTime = time; + } + }); } finally { - mFilenameDictionaryUpdateController.mIsRegenerating.set(false); + mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false); } } }); @@ -658,28 +704,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { // TODO: cache the file's existence so that we avoid doing a disk access each time. private boolean dictionaryFileExists() { - final File file = new File(mContext.getFilesDir(), mFilename); - return file.exists(); - } - - /** - * Load the dictionary to memory. - */ - protected void asyncLoadDictionaryToMemory() { - getExecutor(mFilename).executePrioritized(new Runnable() { - @Override - public void run() { - if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - loadDictionaryAsync(); - } - } - }); + return getDictFile().exists(); } /** * Generate binary dictionary using DictionaryWriter. */ - protected void asyncFlashAllBinaryDictionary() { + protected void asyncFlushBinaryDictionary() { final Runnable newTask = new Runnable() { @Override public void run() { @@ -687,37 +718,32 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } }; final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask); - getExecutor(mFilename).replaceAndExecute(oldTask, newTask); + getExecutor(mDictName).replaceAndExecute(oldTask, newTask); } /** - * For tracking whether the dictionary is out of date and the dictionary is regenerating. - * Can be shared across multiple dictionary instances that access the same filename. + * For tracking whether the dictionary is out of date and the dictionary is used in a large + * task. Can be shared across multiple dictionary instances that access the same filename. */ private static class DictionaryUpdateController { public volatile long mLastUpdateTime = 0; public volatile long mLastUpdateRequestTime = 0; - public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean(); + public volatile AtomicBoolean mProcessingLargeTask = new AtomicBoolean(); public boolean isOutOfDate() { return (mLastUpdateRequestTime > mLastUpdateTime); } } - // TODO: Implement native binary methods once the dynamic dictionary implementation is done. + // TODO: Implement BinaryDictionary.isInDictionary(). @UsedForTesting public boolean isInDictionaryForTests(final String word) { final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); - getExecutor(mFilename).executePrioritized(new Runnable() { + getExecutor(mDictName).executePrioritized(new Runnable() { @Override public void run() { if (mDictType == Dictionary.TYPE_USER_HISTORY) { - if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - holder.set(mBinaryDictionary.isValidWord(word)); - } else { - holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter) - .isInBigramListForTests(word)); - } + holder.set(mBinaryDictionary.isValidWord(word)); } } }); @@ -725,12 +751,33 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @UsedForTesting - public void shutdownExecutorForTests() { - getExecutor(mFilename).shutdown(); + public void waitAllTasksForTests() { + final CountDownLatch countDownLatch = new CountDownLatch(1); + getExecutor(mDictName).execute(new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for finishing dictionary operations.", e); + } } @UsedForTesting - public boolean isTerminatedForTests() { - return getExecutor(mFilename).isTerminated(); + protected void runAfterGcForDebug(final Runnable r) { + getExecutor(mDictName).executePrioritized(new Runnable() { + @Override + public void run() { + try { + mBinaryDictionary.flushWithGC(); + r.run(); + } finally { + mDictNameDictionaryUpdateController.mProcessingLargeTask.set(false); + } + } + }); } } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java deleted file mode 100644 index 95c9bcab9..000000000 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ /dev/null @@ -1,894 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.text.TextUtils; -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.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams; - -import java.util.ArrayList; -import java.util.LinkedList; - -/** - * Class for an in-memory dictionary that can grow dynamically and can - * be searched for suggestions and valid words. - */ -// TODO: Remove after binary dictionary supports dynamic update. -public class ExpandableDictionary extends Dictionary { - private static final String TAG = ExpandableDictionary.class.getSimpleName(); - /** - * The weight to give to a word if it's length is the same as the number of typed characters. - */ - private static final int FULL_WORD_SCORE_MULTIPLIER = 2; - - private char[] mWordBuilder = new char[Constants.DICTIONARY_MAX_WORD_LENGTH]; - private int mMaxDepth; - private int mInputLength; - - private static final class Node { - char mCode; - int mFrequency; - boolean mTerminal; - Node mParent; - NodeArray mChildren; - ArrayList<char[]> mShortcutTargets; - boolean mShortcutOnly; - LinkedList<NextWord> mNGrams; // Supports ngram - } - - private static final class NodeArray { - Node[] mData; - int mLength = 0; - private static final int INCREMENT = 2; - - NodeArray() { - mData = new Node[INCREMENT]; - } - - void add(final Node n) { - if (mLength + 1 > mData.length) { - Node[] tempData = new Node[mLength + INCREMENT]; - if (mLength > 0) { - System.arraycopy(mData, 0, tempData, 0, mLength); - } - mData = tempData; - } - mData[mLength++] = n; - } - } - - public interface NextWord { - public Node getWordNode(); - public int getFrequency(); - public ForgettingCurveParams getFcParams(); - public int notifyTypedAgainAndGetFrequency(); - } - - private static final class NextStaticWord implements NextWord { - public final Node mWord; - private final int mFrequency; - public NextStaticWord(Node word, int frequency) { - mWord = word; - mFrequency = frequency; - } - - @Override - public Node getWordNode() { - return mWord; - } - - @Override - public int getFrequency() { - return mFrequency; - } - - @Override - public ForgettingCurveParams getFcParams() { - return null; - } - - @Override - public int notifyTypedAgainAndGetFrequency() { - return mFrequency; - } - } - - private static final class NextHistoryWord implements NextWord { - public final Node mWord; - public final ForgettingCurveParams mFcp; - - public NextHistoryWord(Node word, ForgettingCurveParams fcp) { - mWord = word; - mFcp = fcp; - } - - @Override - public Node getWordNode() { - return mWord; - } - - @Override - public int getFrequency() { - return mFcp.getFrequency(); - } - - @Override - public ForgettingCurveParams getFcParams() { - return mFcp; - } - - @Override - public int notifyTypedAgainAndGetFrequency() { - return mFcp.notifyTypedAgainAndGetFrequency(); - } - } - - private NodeArray mRoots; - - private int[][] mCodes; - - public ExpandableDictionary(final String dictType) { - super(dictType); - clearDictionary(); - mCodes = new int[Constants.DICTIONARY_MAX_WORD_LENGTH][]; - } - - public int getMaxWordLength() { - return Constants.DICTIONARY_MAX_WORD_LENGTH; - } - - /** - * Add a word with an optional shortcut to the dictionary. - * @param word The word to add. - * @param shortcutTarget A shortcut target for this word, or null if none. - * @param frequency The frequency for this unigram. - * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored - * if shortcutTarget is null. - */ - public void addWord(final String word, final String shortcutTarget, final int frequency, - final int shortcutFreq) { - if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH) { - return; - } - addWordRec(mRoots, word, 0, shortcutTarget, frequency, shortcutFreq, null); - } - - /** - * Add a word, recursively searching for its correct place in the trie tree. - * @param children The node to recursively search for addition. Initially, the root of the tree. - * @param word The word to add. - * @param depth The current depth in the tree. - * @param shortcutTarget A shortcut target for this word, or null if none. - * @param frequency The frequency for this unigram. - * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored - * if shortcutTarget is null. - * @param parentNode The parent node, for up linking. Initially null, as the root has no parent. - */ - private void addWordRec(final NodeArray children, final String word, final int depth, - final String shortcutTarget, final int frequency, final int shortcutFreq, - final Node parentNode) { - final int wordLength = word.length(); - if (wordLength <= depth) return; - final char c = word.charAt(depth); - // Does children have the current character? - final int childrenLength = children.mLength; - Node childNode = null; - for (int i = 0; i < childrenLength; i++) { - final Node node = children.mData[i]; - if (node.mCode == c) { - childNode = node; - break; - } - } - final boolean isShortcutOnly = (null != shortcutTarget); - if (childNode == null) { - childNode = new Node(); - childNode.mCode = c; - childNode.mParent = parentNode; - childNode.mShortcutOnly = isShortcutOnly; - children.add(childNode); - } - if (wordLength == depth + 1) { - // Terminate this word - childNode.mTerminal = true; - if (isShortcutOnly) { - if (null == childNode.mShortcutTargets) { - childNode.mShortcutTargets = CollectionUtils.newArrayList(); - } - childNode.mShortcutTargets.add(shortcutTarget.toCharArray()); - } else { - childNode.mShortcutOnly = false; - } - childNode.mFrequency = Math.max(frequency, childNode.mFrequency); - if (childNode.mFrequency > 255) childNode.mFrequency = 255; - return; - } - if (childNode.mChildren == null) { - childNode.mChildren = new NodeArray(); - } - addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, shortcutFreq, - childNode); - } - - @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - if (composer.size() > 1) { - if (composer.size() >= Constants.DICTIONARY_MAX_WORD_LENGTH) { - return null; - } - final ArrayList<SuggestedWordInfo> suggestions = - getWordsInner(composer, prevWord, proximityInfo); - return suggestions; - } else { - if (TextUtils.isEmpty(prevWord)) return null; - final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); - runBigramReverseLookUp(prevWord, suggestions); - return suggestions; - } - } - - private ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes, - final String prevWordForBigrams, final ProximityInfo proximityInfo) { - final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); - mInputLength = codes.size(); - if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; - final InputPointers ips = codes.getInputPointers(); - final int[] xCoordinates = ips.getXCoordinates(); - final int[] yCoordinates = ips.getYCoordinates(); - // Cache the codes so that we don't have to lookup an array list - for (int i = 0; i < mInputLength; i++) { - // TODO: Calculate proximity info here. - if (mCodes[i] == null || mCodes[i].length < 1) { - mCodes[i] = new int[ProximityInfo.MAX_PROXIMITY_CHARS_SIZE]; - } - final int x = xCoordinates != null && i < xCoordinates.length ? - xCoordinates[i] : Constants.NOT_A_COORDINATE; - final int y = xCoordinates != null && i < yCoordinates.length ? - yCoordinates[i] : Constants.NOT_A_COORDINATE; - proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]); - } - mMaxDepth = mInputLength * 3; - getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, suggestions); - for (int i = 0; i < mInputLength; i++) { - getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, suggestions); - } - return suggestions; - } - - @Override - public synchronized boolean isValidWord(final String word) { - final Node node = searchNode(mRoots, word, 0, word.length()); - // If node is null, we didn't find the word, so it's not valid. - // If node.mShortcutOnly is true, then it exists as a shortcut but not as a word, - // so that means it's not a valid word. - // If node.mShortcutOnly is false, then it exists as a word (it may also exist as - // a shortcut, but this does not matter), so it's a valid word. - return (node == null) ? false : !node.mShortcutOnly; - } - - public boolean removeBigram(final String word0, final String word1) { - // Refer to addOrSetBigram() about word1.toLowerCase() - final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null); - final Node secondWord = searchWord(mRoots, word1, 0, null); - LinkedList<NextWord> bigrams = firstWord.mNGrams; - NextWord bigramNode = null; - if (bigrams == null || bigrams.size() == 0) { - return false; - } else { - for (NextWord nw : bigrams) { - if (nw.getWordNode() == secondWord) { - bigramNode = nw; - break; - } - } - } - if (bigramNode == null) { - return false; - } - return bigrams.remove(bigramNode); - } - - /** - * Returns the word's frequency or -1 if not found - */ - @UsedForTesting - public int getWordFrequency(final String word) { - // Case-sensitive search - final Node node = searchNode(mRoots, word, 0, word.length()); - return (node == null) ? -1 : node.mFrequency; - } - - public NextWord getBigramWord(final String word0, final String word1) { - // Refer to addOrSetBigram() about word0.toLowerCase() - final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null); - final Node secondWord = searchWord(mRoots, word1, 0, null); - LinkedList<NextWord> bigrams = firstWord.mNGrams; - if (bigrams == null || bigrams.size() == 0) { - return null; - } else { - for (NextWord nw : bigrams) { - if (nw.getWordNode() == secondWord) { - return nw; - } - } - } - return null; - } - - private static int computeSkippedWordFinalFreq(final int freq, final int snr, - final int inputLength) { - // The computation itself makes sense for >= 2, but the == 2 case returns 0 - // anyway so we may as well test against 3 instead and return the constant - if (inputLength >= 3) { - return (freq * snr * (inputLength - 2)) / (inputLength - 1); - } else { - return 0; - } - } - - /** - * Helper method to add a word and its shortcuts. - * - * @param node the terminal node - * @param word the word to insert, as an array of code points - * @param depth the depth of the node in the tree - * @param finalFreq the frequency for this word - * @param suggestions the suggestion collection to add the suggestions to - * @return whether there is still space for more words. - */ - private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth, - final int finalFreq, final ArrayList<SuggestedWordInfo> suggestions) { - if (finalFreq > 0 && !node.mShortcutOnly) { - // Use KIND_CORRECTION always. This dictionary does not really have a notion of - // COMPLETION against CORRECTION; we could artificially add one by looking at - // the respective size of the typed word and the suggestion if it matters sometime - // in the future. - suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq, - SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); - if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false; - } - if (null != node.mShortcutTargets) { - final int length = node.mShortcutTargets.size(); - for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) { - final char[] shortcut = node.mShortcutTargets.get(shortcutIndex); - suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length), - finalFreq, SuggestedWordInfo.KIND_SHORTCUT, this /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); - if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false; - } - } - return true; - } - - /** - * Recursively traverse the tree for words that match the input. Input consists of - * a list of arrays. Each item in the list is one input character position. An input - * character is actually an array of multiple possible candidates. This function is not - * optimized for speed, assuming that the user dictionary will only be a few hundred words in - * size. - * @param roots node whose children have to be search for matches - * @param codes the input character codes - * @param word the word being composed as a possible match - * @param depth the depth of traversal - the length of the word being composed thus far - * @param completion whether the traversal is now in completion mode - meaning that we've - * exhausted the input and we're looking for all possible suffixes. - * @param snr current weight of the word being formed - * @param inputIndex position in the input characters. This can be off from the depth in - * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type - * "wouldve", it could be matching "would've", so the depth will be one more than the - * inputIndex - * @param suggestions the list in which to add suggestions - */ - // TODO: Share this routine with the native code for BinaryDictionary - private void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word, - final int depth, final boolean completion, final int snr, final int inputIndex, - final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) { - final int count = roots.mLength; - final int codeSize = mInputLength; - // Optimization: Prune out words that are too long compared to how much was typed. - if (depth > mMaxDepth) { - return; - } - final int[] currentChars; - if (codeSize <= inputIndex) { - currentChars = null; - } else { - currentChars = mCodes[inputIndex]; - } - - for (int i = 0; i < count; i++) { - final Node node = roots.mData[i]; - final char c = node.mCode; - final char lowerC = toLowerCase(c); - final boolean terminal = node.mTerminal; - final NodeArray children = node.mChildren; - final int freq = node.mFrequency; - if (completion || currentChars == null) { - word[depth] = c; - if (terminal) { - final int finalFreq; - if (skipPos < 0) { - finalFreq = freq * snr; - } else { - finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength); - } - if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, suggestions)) { - // No space left in the queue, bail out - return; - } - } - if (children != null) { - getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex, - skipPos, suggestions); - } - } else if ((c == Constants.CODE_SINGLE_QUOTE - && currentChars[0] != Constants.CODE_SINGLE_QUOTE) || depth == skipPos) { - // Skip the ' and continue deeper - word[depth] = c; - if (children != null) { - getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, - skipPos, suggestions); - } - } else { - // Don't use alternatives if we're looking for missing characters - final int alternativesSize = skipPos >= 0 ? 1 : currentChars.length; - for (int j = 0; j < alternativesSize; j++) { - final int addedAttenuation = (j > 0 ? 1 : 2); - final int currentChar = currentChars[j]; - if (currentChar == Constants.NOT_A_CODE) { - break; - } - if (currentChar == lowerC || currentChar == c) { - word[depth] = c; - - if (codeSize == inputIndex + 1) { - if (terminal) { - final int finalFreq; - if (skipPos < 0) { - finalFreq = freq * snr * addedAttenuation - * FULL_WORD_SCORE_MULTIPLIER; - } else { - finalFreq = computeSkippedWordFinalFreq(freq, - snr * addedAttenuation, mInputLength); - } - if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, - suggestions)) { - // No space left in the queue, bail out - return; - } - } - if (children != null) { - getWordsRec(children, codes, word, depth + 1, - true, snr * addedAttenuation, inputIndex + 1, - skipPos, suggestions); - } - } else if (children != null) { - getWordsRec(children, codes, word, depth + 1, - false, snr * addedAttenuation, inputIndex + 1, - skipPos, suggestions); - } - } - } - } - } - } - - public int setBigramAndGetFrequency(final String word0, final String word1, - final int frequency) { - return setBigramAndGetFrequency(word0, word1, frequency, null /* unused */); - } - - public int setBigramAndGetFrequency(final String word0, final String word1, - final ForgettingCurveParams fcp) { - return setBigramAndGetFrequency(word0, word1, 0 /* unused */, fcp); - } - - /** - * Adds bigrams to the in-memory trie structure that is being used to retrieve any word - * @param word0 the first word of this bigram - * @param word1 the second word of this bigram - * @param frequency frequency for this bigram - * @param fcp an instance of ForgettingCurveParams to use for decay policy - * @return returns the final bigram frequency - */ - private int setBigramAndGetFrequency(final String word0, final String word1, - final int frequency, final ForgettingCurveParams fcp) { - if (TextUtils.isEmpty(word0)) { - Log.e(TAG, "Invalid bigram previous word: " + word0); - return frequency; - } - // We don't want results to be different according to case of the looked up left hand side - // word. We do want however to return the correct case for the right hand side. - // So we want to squash the case of the left hand side, and preserve that of the right - // hand side word. - final String word0Lower = word0.toLowerCase(); - if (TextUtils.isEmpty(word0Lower) || TextUtils.isEmpty(word1)) { - Log.e(TAG, "Invalid bigram pair: " + word0 + ", " + word0Lower + ", " + word1); - return frequency; - } - final Node firstWord = searchWord(mRoots, word0Lower, 0, null); - final Node secondWord = searchWord(mRoots, word1, 0, null); - LinkedList<NextWord> bigrams = firstWord.mNGrams; - if (bigrams == null || bigrams.size() == 0) { - firstWord.mNGrams = CollectionUtils.newLinkedList(); - bigrams = firstWord.mNGrams; - } else { - for (NextWord nw : bigrams) { - if (nw.getWordNode() == secondWord) { - return nw.notifyTypedAgainAndGetFrequency(); - } - } - } - if (fcp != null) { - // history - firstWord.mNGrams.add(new NextHistoryWord(secondWord, fcp)); - } else { - firstWord.mNGrams.add(new NextStaticWord(secondWord, frequency)); - } - return frequency; - } - - /** - * Searches for the word and add the word if it does not exist. - * @return Returns the terminal node of the word we are searching for. - */ - private Node searchWord(final NodeArray children, final String word, final int depth, - final Node parentNode) { - final int wordLength = word.length(); - final char c = word.charAt(depth); - // Does children have the current character? - final int childrenLength = children.mLength; - Node childNode = null; - for (int i = 0; i < childrenLength; i++) { - final Node node = children.mData[i]; - if (node.mCode == c) { - childNode = node; - break; - } - } - if (childNode == null) { - childNode = new Node(); - childNode.mCode = c; - childNode.mParent = parentNode; - children.add(childNode); - } - if (wordLength == depth + 1) { - // Terminate this word - childNode.mTerminal = true; - return childNode; - } - if (childNode.mChildren == null) { - childNode.mChildren = new NodeArray(); - } - return searchWord(childNode.mChildren, word, depth + 1, childNode); - } - - private void runBigramReverseLookUp(final String previousWord, - final ArrayList<SuggestedWordInfo> suggestions) { - // Search for the lowercase version of the word only, because that's where bigrams - // store their sons. - final Node prevWord = searchNode(mRoots, previousWord.toLowerCase(), 0, - previousWord.length()); - if (prevWord != null && prevWord.mNGrams != null) { - reverseLookUp(prevWord.mNGrams, suggestions); - } - } - - // Local to reverseLookUp, but do not allocate each time. - private final char[] mLookedUpString = new char[Constants.DICTIONARY_MAX_WORD_LENGTH]; - - /** - * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words - * to the suggestions list passed as an argument. - * @param terminalNodes list of terminal nodes we want to add - * @param suggestions the suggestion collection to add the word to - */ - private void reverseLookUp(final LinkedList<NextWord> terminalNodes, - final ArrayList<SuggestedWordInfo> suggestions) { - Node node; - int freq; - for (NextWord nextWord : terminalNodes) { - node = nextWord.getWordNode(); - freq = nextWord.getFrequency(); - int index = Constants.DICTIONARY_MAX_WORD_LENGTH; - do { - --index; - mLookedUpString[index] = node.mCode; - node = node.mParent; - } while (node != null && index > 0); - - // If node is null, we have a word longer than MAX_WORD_LENGTH in the dictionary. - // It's a little unclear how this can happen, but just in case it does it's safer - // to ignore the word in this case. - if (freq >= 0 && node == null) { - suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index, - Constants.DICTIONARY_MAX_WORD_LENGTH - index), - freq, SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); - } - } - } - - /** - * Recursively search for the terminal node of the word. - * - * One iteration takes the full word to search for and the current index of the recursion. - * - * @param children the node of the trie to search under. - * @param word the word to search for. Only read [offset..length] so there may be trailing chars - * @param offset the index in {@code word} this recursion should operate on. - * @param length the length of the input word. - * @return Returns the terminal node of the word if the word exists - */ - private Node searchNode(final NodeArray children, final CharSequence word, final int offset, - final int length) { - final int count = children.mLength; - final char currentChar = word.charAt(offset); - for (int j = 0; j < count; j++) { - final Node node = children.mData[j]; - if (node.mCode == currentChar) { - if (offset == length - 1) { - if (node.mTerminal) { - return node; - } - } else { - if (node.mChildren != null) { - Node returnNode = searchNode(node.mChildren, word, offset + 1, length); - if (returnNode != null) return returnNode; - } - } - } - } - return null; - } - - public void clearDictionary() { - mRoots = new NodeArray(); - } - - private static char toLowerCase(final char c) { - char baseChar = c; - if (c < BASE_CHARS.length) { - baseChar = BASE_CHARS[c]; - } - if (baseChar >= 'A' && baseChar <= 'Z') { - return (char)(baseChar | 32); - } else if (baseChar > 127) { - return Character.toLowerCase(baseChar); - } - return baseChar; - } - - /** - * Table mapping most combined Latin, Greek, and Cyrillic characters - * to their base characters. If c is in range, BASE_CHARS[c] == c - * if c is not a combined character, or the base character if it - * is combined. - * - * cf. native/jni/src/utils/char_utils.cpp - */ - private static final char BASE_CHARS[] = { - /* U+0000 */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, - /* U+0008 */ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - /* U+0010 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, - /* U+0018 */ 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F, - /* U+0020 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, - /* U+0028 */ 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, - /* U+0030 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - /* U+0038 */ 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, - /* U+0040 */ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - /* U+0048 */ 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, - /* U+0050 */ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, - /* U+0058 */ 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, - /* U+0060 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - /* U+0068 */ 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, - /* U+0070 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, - /* U+0078 */ 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F, - /* U+0080 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, - /* U+0088 */ 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F, - /* U+0090 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, - /* U+0098 */ 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F, - /* U+00A0 */ 0x0020, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, - /* U+00A8 */ 0x0020, 0x00A9, 0x0061, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0020, - /* U+00B0 */ 0x00B0, 0x00B1, 0x0032, 0x0033, 0x0020, 0x03BC, 0x00B6, 0x00B7, - /* U+00B8 */ 0x0020, 0x0031, 0x006F, 0x00BB, 0x0031, 0x0031, 0x0033, 0x00BF, - /* U+00C0 */ 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00C6, 0x0043, - /* U+00C8 */ 0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, - /* U+00D0 */ 0x00D0, 0x004E, 0x004F, 0x004F, 0x004F, 0x004F, 0x004F, 0x00D7, - /* U+00D8 */ 0x004F, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00DE, 0x0073, - // U+00D8: Manually changed from 00D8 to 004F - // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O - // U+00DF: Manually changed from 00DF to 0073 - /* U+00E0 */ 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00E6, 0x0063, - /* U+00E8 */ 0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, - /* U+00F0 */ 0x00F0, 0x006E, 0x006F, 0x006F, 0x006F, 0x006F, 0x006F, 0x00F7, - /* U+00F8 */ 0x006F, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00FE, 0x0079, - // U+00F8: Manually changed from 00F8 to 006F - // TODO: Check if it's really acceptable to consider ø a diacritical variant of o - /* U+0100 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, - /* U+0108 */ 0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, - /* U+0110 */ 0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, - /* U+0118 */ 0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, - /* U+0120 */ 0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, - /* U+0128 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, - /* U+0130 */ 0x0049, 0x0131, 0x0049, 0x0069, 0x004A, 0x006A, 0x004B, 0x006B, - /* U+0138 */ 0x0138, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, 0x006C, 0x004C, - /* U+0140 */ 0x006C, 0x004C, 0x006C, 0x004E, 0x006E, 0x004E, 0x006E, 0x004E, - // U+0141: Manually changed from 0141 to 004C - // U+0142: Manually changed from 0142 to 006C - /* U+0148 */ 0x006E, 0x02BC, 0x014A, 0x014B, 0x004F, 0x006F, 0x004F, 0x006F, - /* U+0150 */ 0x004F, 0x006F, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, - /* U+0158 */ 0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, - /* U+0160 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, - /* U+0168 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, - /* U+0170 */ 0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, - /* U+0178 */ 0x0059, 0x005A, 0x007A, 0x005A, 0x007A, 0x005A, 0x007A, 0x0073, - /* U+0180 */ 0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, - /* U+0188 */ 0x0188, 0x0189, 0x018A, 0x018B, 0x018C, 0x018D, 0x018E, 0x018F, - /* U+0190 */ 0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, - /* U+0198 */ 0x0198, 0x0199, 0x019A, 0x019B, 0x019C, 0x019D, 0x019E, 0x019F, - /* U+01A0 */ 0x004F, 0x006F, 0x01A2, 0x01A3, 0x01A4, 0x01A5, 0x01A6, 0x01A7, - /* U+01A8 */ 0x01A8, 0x01A9, 0x01AA, 0x01AB, 0x01AC, 0x01AD, 0x01AE, 0x0055, - /* U+01B0 */ 0x0075, 0x01B1, 0x01B2, 0x01B3, 0x01B4, 0x01B5, 0x01B6, 0x01B7, - /* U+01B8 */ 0x01B8, 0x01B9, 0x01BA, 0x01BB, 0x01BC, 0x01BD, 0x01BE, 0x01BF, - /* U+01C0 */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x0044, 0x0044, 0x0064, 0x004C, - /* U+01C8 */ 0x004C, 0x006C, 0x004E, 0x004E, 0x006E, 0x0041, 0x0061, 0x0049, - /* U+01D0 */ 0x0069, 0x004F, 0x006F, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, - // U+01D5: Manually changed from 00DC to 0055 - // U+01D6: Manually changed from 00FC to 0075 - // U+01D7: Manually changed from 00DC to 0055 - /* U+01D8 */ 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x01DD, 0x0041, 0x0061, - // U+01D8: Manually changed from 00FC to 0075 - // U+01D9: Manually changed from 00DC to 0055 - // U+01DA: Manually changed from 00FC to 0075 - // U+01DB: Manually changed from 00DC to 0055 - // U+01DC: Manually changed from 00FC to 0075 - // U+01DE: Manually changed from 00C4 to 0041 - // U+01DF: Manually changed from 00E4 to 0061 - /* U+01E0 */ 0x0041, 0x0061, 0x00C6, 0x00E6, 0x01E4, 0x01E5, 0x0047, 0x0067, - // U+01E0: Manually changed from 0226 to 0041 - // U+01E1: Manually changed from 0227 to 0061 - /* U+01E8 */ 0x004B, 0x006B, 0x004F, 0x006F, 0x004F, 0x006F, 0x01B7, 0x0292, - // U+01EC: Manually changed from 01EA to 004F - // U+01ED: Manually changed from 01EB to 006F - /* U+01F0 */ 0x006A, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01F6, 0x01F7, - /* U+01F8 */ 0x004E, 0x006E, 0x0041, 0x0061, 0x00C6, 0x00E6, 0x004F, 0x006F, - // U+01FA: Manually changed from 00C5 to 0041 - // U+01FB: Manually changed from 00E5 to 0061 - // U+01FE: Manually changed from 00D8 to 004F - // TODO: Check if it's really acceptable to consider Ø a diacritical variant of O - // U+01FF: Manually changed from 00F8 to 006F - // TODO: Check if it's really acceptable to consider ø a diacritical variant of o - /* U+0200 */ 0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, - /* U+0208 */ 0x0049, 0x0069, 0x0049, 0x0069, 0x004F, 0x006F, 0x004F, 0x006F, - /* U+0210 */ 0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, - /* U+0218 */ 0x0053, 0x0073, 0x0054, 0x0074, 0x021C, 0x021D, 0x0048, 0x0068, - /* U+0220 */ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, - /* U+0228 */ 0x0045, 0x0065, 0x004F, 0x006F, 0x004F, 0x006F, 0x004F, 0x006F, - // U+022A: Manually changed from 00D6 to 004F - // U+022B: Manually changed from 00F6 to 006F - // U+022C: Manually changed from 00D5 to 004F - // U+022D: Manually changed from 00F5 to 006F - /* U+0230 */ 0x004F, 0x006F, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, - // U+0230: Manually changed from 022E to 004F - // U+0231: Manually changed from 022F to 006F - /* U+0238 */ 0x0238, 0x0239, 0x023A, 0x023B, 0x023C, 0x023D, 0x023E, 0x023F, - /* U+0240 */ 0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, - /* U+0248 */ 0x0248, 0x0249, 0x024A, 0x024B, 0x024C, 0x024D, 0x024E, 0x024F, - /* U+0250 */ 0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, - /* U+0258 */ 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F, - /* U+0260 */ 0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, - /* U+0268 */ 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F, - /* U+0270 */ 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, - /* U+0278 */ 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F, - /* U+0280 */ 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, - /* U+0288 */ 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F, - /* U+0290 */ 0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, - /* U+0298 */ 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F, - /* U+02A0 */ 0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, - /* U+02A8 */ 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF, - /* U+02B0 */ 0x0068, 0x0266, 0x006A, 0x0072, 0x0279, 0x027B, 0x0281, 0x0077, - /* U+02B8 */ 0x0079, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF, - /* U+02C0 */ 0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, - /* U+02C8 */ 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF, - /* U+02D0 */ 0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, - /* U+02D8 */ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02DE, 0x02DF, - /* U+02E0 */ 0x0263, 0x006C, 0x0073, 0x0078, 0x0295, 0x02E5, 0x02E6, 0x02E7, - /* U+02E8 */ 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF, - /* U+02F0 */ 0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, - /* U+02F8 */ 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF, - /* U+0300 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, - /* U+0308 */ 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F, - /* U+0310 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, - /* U+0318 */ 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F, - /* U+0320 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, - /* U+0328 */ 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F, - /* U+0330 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, - /* U+0338 */ 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F, - /* U+0340 */ 0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, - /* U+0348 */ 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F, - /* U+0350 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, - /* U+0358 */ 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F, - /* U+0360 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, - /* U+0368 */ 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F, - /* U+0370 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x02B9, 0x0375, 0x0376, 0x0377, - /* U+0378 */ 0x0378, 0x0379, 0x0020, 0x037B, 0x037C, 0x037D, 0x003B, 0x037F, - /* U+0380 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00A8, 0x0391, 0x00B7, - /* U+0388 */ 0x0395, 0x0397, 0x0399, 0x038B, 0x039F, 0x038D, 0x03A5, 0x03A9, - /* U+0390 */ 0x03CA, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, - /* U+0398 */ 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, - /* U+03A0 */ 0x03A0, 0x03A1, 0x03A2, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, - /* U+03A8 */ 0x03A8, 0x03A9, 0x0399, 0x03A5, 0x03B1, 0x03B5, 0x03B7, 0x03B9, - /* U+03B0 */ 0x03CB, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, - /* U+03B8 */ 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, - /* U+03C0 */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, - /* U+03C8 */ 0x03C8, 0x03C9, 0x03B9, 0x03C5, 0x03BF, 0x03C5, 0x03C9, 0x03CF, - /* U+03D0 */ 0x03B2, 0x03B8, 0x03A5, 0x03D2, 0x03D2, 0x03C6, 0x03C0, 0x03D7, - /* U+03D8 */ 0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF, - /* U+03E0 */ 0x03E0, 0x03E1, 0x03E2, 0x03E3, 0x03E4, 0x03E5, 0x03E6, 0x03E7, - /* U+03E8 */ 0x03E8, 0x03E9, 0x03EA, 0x03EB, 0x03EC, 0x03ED, 0x03EE, 0x03EF, - /* U+03F0 */ 0x03BA, 0x03C1, 0x03C2, 0x03F3, 0x0398, 0x03B5, 0x03F6, 0x03F7, - /* U+03F8 */ 0x03F8, 0x03A3, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF, - /* U+0400 */ 0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, - /* U+0408 */ 0x0408, 0x0409, 0x040A, 0x040B, 0x041A, 0x0418, 0x0423, 0x040F, - /* U+0410 */ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, - /* U+0418 */ 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, - // U+0419: Manually changed from 0418 to 0419 - /* U+0420 */ 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, - /* U+0428 */ 0x0428, 0x0429, 0x042C, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, - // U+042A: Manually changed from 042A to 042C - /* U+0430 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, - /* U+0438 */ 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, - // U+0439: Manually changed from 0438 to 0439 - /* U+0440 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, - /* U+0448 */ 0x0448, 0x0449, 0x044C, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, - // U+044A: Manually changed from 044A to 044C - /* U+0450 */ 0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, - /* U+0458 */ 0x0458, 0x0459, 0x045A, 0x045B, 0x043A, 0x0438, 0x0443, 0x045F, - /* U+0460 */ 0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, - /* U+0468 */ 0x0468, 0x0469, 0x046A, 0x046B, 0x046C, 0x046D, 0x046E, 0x046F, - /* U+0470 */ 0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, - /* U+0478 */ 0x0478, 0x0479, 0x047A, 0x047B, 0x047C, 0x047D, 0x047E, 0x047F, - /* U+0480 */ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, - /* U+0488 */ 0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F, - /* U+0490 */ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, - /* U+0498 */ 0x0498, 0x0499, 0x049A, 0x049B, 0x049C, 0x049D, 0x049E, 0x049F, - /* U+04A0 */ 0x04A0, 0x04A1, 0x04A2, 0x04A3, 0x04A4, 0x04A5, 0x04A6, 0x04A7, - /* U+04A8 */ 0x04A8, 0x04A9, 0x04AA, 0x04AB, 0x04AC, 0x04AD, 0x04AE, 0x04AF, - /* U+04B0 */ 0x04B0, 0x04B1, 0x04B2, 0x04B3, 0x04B4, 0x04B5, 0x04B6, 0x04B7, - /* U+04B8 */ 0x04B8, 0x04B9, 0x04BA, 0x04BB, 0x04BC, 0x04BD, 0x04BE, 0x04BF, - /* U+04C0 */ 0x04C0, 0x0416, 0x0436, 0x04C3, 0x04C4, 0x04C5, 0x04C6, 0x04C7, - /* U+04C8 */ 0x04C8, 0x04C9, 0x04CA, 0x04CB, 0x04CC, 0x04CD, 0x04CE, 0x04CF, - /* U+04D0 */ 0x0410, 0x0430, 0x0410, 0x0430, 0x04D4, 0x04D5, 0x0415, 0x0435, - /* U+04D8 */ 0x04D8, 0x04D9, 0x04D8, 0x04D9, 0x0416, 0x0436, 0x0417, 0x0437, - /* U+04E0 */ 0x04E0, 0x04E1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041E, 0x043E, - /* U+04E8 */ 0x04E8, 0x04E9, 0x04E8, 0x04E9, 0x042D, 0x044D, 0x0423, 0x0443, - /* U+04F0 */ 0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04F6, 0x04F7, - /* U+04F8 */ 0x042B, 0x044B, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF, - }; -} diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index 8caf6f17f..fcf043031 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -52,8 +52,7 @@ public final class InputAttributes { } else if (inputClass == 0) { // TODO: is this check still necessary? Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" - + " imeOptions=0x%08x", - inputType, editorInfo.imeOptions)); + + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); } mIsSettingsSuggestionStripOn = false; mInputTypeNoAutoCorrect = false; @@ -204,8 +203,7 @@ public final class InputAttributes { public static boolean inPrivateImeOptions(String packageName, String key, EditorInfo editorInfo) { if (editorInfo == null) return false; - final String findingKey = (packageName != null) ? packageName + "." + key - : key; + final String findingKey = (packageName != null) ? packageName + "." + key : key; return StringUtils.containsInCommaSplittableText(findingKey, editorInfo.privateImeOptions); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 77d07019f..6a10131b0 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -68,7 +68,6 @@ import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.EventInterpreter; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; @@ -81,7 +80,6 @@ import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastRec import com.android.inputmethod.latin.personalization.PersonalizationDictionary; import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister; import com.android.inputmethod.latin.personalization.PersonalizationHelper; -import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsActivity; @@ -97,8 +95,8 @@ import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.RecapitalizeStatus; -import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; import com.android.inputmethod.latin.utils.TextRange; @@ -110,6 +108,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Locale; import java.util.TreeSet; +import java.util.concurrent.TimeUnit; /** * Input method implementation for Qwerty'ish keyboard. @@ -182,7 +181,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mIsMainDictionaryAvailable; private UserBinaryDictionary mUserDictionary; private UserHistoryDictionary mUserHistoryDictionary; - private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary; private PersonalizationDictionary mPersonalizationDictionary; private boolean mIsUserDictionaryAvailable; @@ -196,9 +194,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mLastSelectionStart = NOT_A_CURSOR_POSITION; private int mLastSelectionEnd = NOT_A_CURSOR_POSITION; - // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't - // "expect" it, it means the user actually moved the cursor. - private boolean mExpectingUpdateSelection; private int mDeleteCount; private long mLastKeyTime; private final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet(); @@ -227,7 +222,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public final UIHandler mHandler = new UIHandler(this); private InputUpdater mInputUpdater; - public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { + public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> { private static final int MSG_UPDATE_SHIFT_STATE = 0; private static final int MSG_PENDING_IMS_CALLBACK = 1; private static final int MSG_UPDATE_SUGGESTION_STRIP = 2; @@ -236,6 +231,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen 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; + // Update this when adding new messages + private static final int MSG_LAST = MSG_RESET_CACHES; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; @@ -248,12 +245,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private long mDoubleSpacePeriodTimeout; private long mDoubleSpacePeriodTimerStart; - public UIHandler(final LatinIME outerInstance) { - super(outerInstance); + public UIHandler(final LatinIME ownerInstance) { + super(ownerInstance); } public void onCreate() { - final Resources res = getOuterInstance().getResources(); + final Resources res = getOwnerInstance().getResources(); mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions); mDelayUpdateShiftState = @@ -264,7 +261,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void handleMessage(final Message msg) { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; switch (msg.what) { case MSG_UPDATE_SUGGESTION_STRIP: @@ -347,6 +344,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_UPDATE_SHIFT_STATE); } + @UsedForTesting + public void removeAllMessages() { + for (int i = 0; i <= MSG_LAST; ++i) { + removeMessages(i); + } + } + public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); @@ -401,7 +405,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_PENDING_IMS_CALLBACK); resetPendingImsCallback(); mIsOrientationChanging = true; - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); if (latinIme.isInputViewShown()) { latinIme.mKeyboardSwitcher.saveKeyboardState(); } @@ -434,7 +438,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mIsOrientationChanging = false; mPendingSuccessiveImsCallback = true; } - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, editorInfo, restarting); latinIme.onStartInputInternal(editorInfo, restarting); } @@ -453,7 +457,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), PENDING_IMS_CALLBACK_DURATION); } - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, editorInfo, restarting); latinIme.onStartInputViewInternal(editorInfo, restarting); mAppliedEditorInfo = editorInfo; @@ -465,7 +469,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Typically this is the first onFinishInputView after orientation changed. mHasPendingFinishInputView = true; } else { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); latinIme.onFinishInputViewInternal(finishingInput); mAppliedEditorInfo = null; } @@ -476,7 +480,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Typically this is the first onFinishInput after orientation changed. mHasPendingFinishInput = true; } else { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, null, false); latinIme.onFinishInputInternal(); } @@ -609,7 +613,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final String switcherLocaleStr = switcherSubtypeLocale.toString(); final Locale subtypeLocale; - final String localeStr; if (TextUtils.isEmpty(switcherLocaleStr)) { // This happens in very rare corner cases - for example, immediately after a switch // to LatinIME has been requested, about a frame later another switch happens. In this @@ -619,10 +622,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // of knowing anyway. Log.e(TAG, "System is reporting no current subtype."); subtypeLocale = getResources().getConfiguration().locale; - localeStr = subtypeLocale.toString(); } else { subtypeLocale = switcherSubtypeLocale; - localeStr = switcherLocaleStr; } final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale, @@ -637,21 +638,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.getInstance().initSuggest(newSuggest); } - mUserDictionary = new UserBinaryDictionary(this, localeStr); + mUserDictionary = new UserBinaryDictionary(this, subtypeLocale); mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); newSuggest.setUserDictionary(mUserDictionary); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - mUserHistoryDictionary = PersonalizationHelper.getUserHistoryDictionary( - this, localeStr, prefs); + this, subtypeLocale); newSuggest.setUserHistoryDictionary(mUserHistoryDictionary); - mPersonalizationDictionary = PersonalizationHelper - .getPersonalizationDictionary(this, localeStr, prefs); + mPersonalizationDictionary = + PersonalizationHelper.getPersonalizationDictionary(this, subtypeLocale); newSuggest.setPersonalizationDictionary(mPersonalizationDictionary); - mPersonalizationPredictionDictionary = PersonalizationHelper - .getPersonalizationPredictionDictionary(this, localeStr, prefs); - newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary); final Suggest oldSuggest = mSuggest; resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null); @@ -760,8 +756,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen .findViewById(android.R.id.extractArea); mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); - if (mSuggestionStripView != null) + if (mSuggestionStripView != null) { mSuggestionStripView.setListener(this, view); + } if (LatinImeLogger.sVISUALDEBUG) { mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); } @@ -907,7 +904,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // refresh later. final boolean canReachInputConnection; if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart, - false /* shouldFinishComposition */)) { + editorInfo.initialSelEnd, false /* shouldFinishComposition */)) { // We try resetting the caches up to 5 times before giving up. mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */); // mLastSelection{Start,End} are reset later in this method, don't need to do it here @@ -1088,16 +1085,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", ce=" + composingSpanEnd); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final boolean expectingUpdateSelectionFromLogger = - ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection(); ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, - composingSpanEnd, mExpectingUpdateSelection, - expectingUpdateSelectionFromLogger, mConnection); - if (expectingUpdateSelectionFromLogger) { - // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work - return; - } + composingSpanEnd, mConnection); } final boolean selectionChanged = mLastSelectionStart != newSelStart @@ -1116,14 +1106,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: revisit this when LatinIME supports hardware keyboards. // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown(). // TODO: find a better way to simulate actual execution. - if (isInputViewShown() && !mExpectingUpdateSelection - && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) { - // TAKE CARE: there is a race condition when we enter this test even when the user - // did not explicitly move the cursor. This happens when typing fast, where two keys - // turn this flag on in succession and both onUpdateSelection() calls arrive after - // the second one - the first call successfully avoids this test, but the second one - // enters. For the moment we rely on noComposingSpan to further reduce the impact. - + if (isInputViewShown() && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, + oldSelEnd, newSelEnd)) { // TODO: the following is probably better done in resetEntireInputState(). // it should only happen when the cursor moved, and the very purpose of the // test below is to narrow down whether this happened or not. Likewise with @@ -1149,13 +1133,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Another option would be to send suggestions each time we set the composing // text, but that is probably too expensive to do, so we decided to leave things // as is. - resetEntireInputState(newSelStart); + resetEntireInputState(newSelStart, newSelEnd); } else { - // resetEntireInputState calls resetCachesUponCursorMove, but with the second - // 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.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, + // resetEntireInputState calls resetCachesUponCursorMove, but forcing the + // composition to end. 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.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd, false /* shouldFinishComposition */); } @@ -1168,7 +1152,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mRecapitalizeStatus.deactivate(); mKeyboardSwitcher.updateShiftState(); } - mExpectingUpdateSelection = false; // Make a note of the cursor position mLastSelectionStart = newSelStart; @@ -1354,8 +1337,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onEvaluateFullscreenMode() { // Reread resource value here, because this method is called by framework anytime as needed. - final boolean isFullscreenModeAllowed = - Settings.readUseFullscreenMode(getResources()); + final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources()); if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) { // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI // implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI @@ -1380,7 +1362,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This will reset the whole input state to the starting state. It will clear // the composing word, reset the last composed word, tell the inputconnection about it. - private void resetEntireInputState(final int newCursorPosition) { + private void resetEntireInputState(final int newSelStart, final int newSelEnd) { final boolean shouldFinishComposition = mWordComposer.isComposingWord(); resetComposingState(true /* alsoResetLastComposedWord */); final SettingsValues settingsValues = mSettings.getCurrent(); @@ -1389,14 +1371,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { setSuggestedWords(settingsValues.mSuggestPuncList, false); } - mConnection.resetCachesUponCursorMoveAndReturnSuccess(newCursorPosition, + mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd, shouldFinishComposition); } private void resetComposingState(final boolean alsoResetLastComposedWord) { mWordComposer.reset(); - if (alsoResetLastComposedWord) + if (alsoResetLastComposedWord) { mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; + } } private void commitTyped(final String separatorString) { @@ -1443,15 +1426,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) { return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED; } - if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED; + if (0 != auto) { + return WordComposer.CAPS_MODE_AUTO_SHIFTED; + } return WordComposer.CAPS_MODE_OFF; } private void swapSwapperAndSpace() { final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. - if (lastTwo != null && lastTwo.length() == 2 - && lastTwo.charAt(0) == Constants.CODE_SPACE) { + if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); final String text = lastTwo.charAt(1) + " "; mConnection.commitText(text, 1); @@ -1730,7 +1714,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } else { commitTyped(LastComposedWord.NOT_A_SEPARATOR); } @@ -1746,7 +1730,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } handleCharacter(primaryCode, keyX, keyY, spaceState); } - mExpectingUpdateSelection = true; return didAutoCorrect; } @@ -1786,9 +1769,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputUpdater.onStartBatchInput(); mHandler.cancelUpdateSuggestionStrip(); mConnection.beginBatchEdit(); - final SettingsValues settingsValues = mSettings.getCurrent(); + final SettingsValues currentSettingsValues = mSettings.getCurrent(); if (mWordComposer.isComposingWord()) { - if (settingsValues.mIsInternal) { + if (currentSettingsValues.mIsInternal) { if (mWordComposer.isBatchMode()) { LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); @@ -1799,7 +1782,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the batch input at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } else if (wordComposerSize <= 1) { // We auto-correct the previous (typed, not gestured) string iff it's one character // long. The reason for this is, even in the middle of gesture typing, you'll still @@ -1812,15 +1795,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { commitTyped(LastComposedWord.NOT_A_SEPARATOR); } - mExpectingUpdateSelection = true; } final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); if (Character.isLetterOrDigit(codePointBeforeCursor) - || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { + || currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { mSpaceState = SPACE_STATE_PHANTOM; } mConnection.endBatchEdit(); - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + mKeyboardSwitcher.updateShiftState(); + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(getActualCapsMode(), + // Prev word is 1st word before cursor + getNthPreviousWordForSuggestion(currentSettingsValues, 1 /* nthPreviousWord */)); } static final class InputUpdater implements Handler.Callback { @@ -1993,7 +1978,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.commitText(commitParts[0], 0); mSpaceState = SPACE_STATE_PHANTOM; mKeyboardSwitcher.updateShiftState(); - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( + getActualCapsMode(), commitParts[0]); ++mAutoCommitSequenceNumber; } } @@ -2003,8 +1989,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This method must run in UI Thread. public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) { - final String batchInputText = suggestedWords.isEmpty() - ? null : suggestedWords.getWord(0); + final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0); if (TextUtils.isEmpty(batchInputText)) { return; } @@ -2026,7 +2011,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setBatchInputWord(batchInputText); mConnection.setComposingText(batchInputText, 1); } - mExpectingUpdateSelection = true; mConnection.endBatchEdit(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords); @@ -2080,9 +2064,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace(final int spaceState) { - // We revert these in this method if the deletion doesn't happen. mDeleteCount++; - mExpectingUpdateSelection = true; // In many cases, we may have to put the keyboard in auto-shift state again. However // we want to wait a few milliseconds before doing it to avoid the keyboard flashing @@ -2092,7 +2074,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can remove the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); // When we exit this if-clause, mWordComposer.isComposingWord() will return false. } if (mWordComposer.isComposingWord()) { @@ -2163,6 +2145,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // later (typically, in a subsequent press on backspace). mLastSelectionEnd = mLastSelectionStart; mConnection.deleteSurroundingText(numCharsDeleted, 0); + if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + ResearchLogger.latinIME_handleBackspace(numCharsDeleted, + false /* shouldUncommitLogUnit */); + } } else { // There is no selection, just delete one character. if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { @@ -2185,22 +2171,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); if (codePointBeforeCursor == Constants.NOT_A_CODE) { - // Nothing to delete before the cursor. We have to revert the deletion - // states that were updated at the beginning of this method. - mDeleteCount--; - mExpectingUpdateSelection = false; + // Nothing to delete before the cursor. return; } final int lengthToDelete = Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1; mConnection.deleteSurroundingText(lengthToDelete, 0); + if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + ResearchLogger.latinIME_handleBackspace(lengthToDelete, + true /* shouldUncommitLogUnit */); + } if (mDeleteCount > DELETE_ACCELERATE_AT) { final int codePointBeforeCursorToDeleteAgain = mConnection.getCodePointBeforeCursor(); if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) { final int lengthToDeleteAgain = Character.isSupplementaryCodePoint( - codePointBeforeCursorToDeleteAgain) ? 2 : 1; + codePointBeforeCursorToDeleteAgain) ? 2 : 1; mConnection.deleteSurroundingText(lengthToDeleteAgain, 0); + if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain, + true /* shouldUncommitLogUnit */); + } } } } @@ -2217,8 +2208,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen /* * Strip a trailing space if necessary and returns whether it's a swap weak space situation. */ - private boolean maybeStripSpace(final int code, - final int spaceState, final boolean isFromSuggestionStrip) { + private boolean maybeStripSpace(final int code, final int spaceState, + final boolean isFromSuggestionStrip) { if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { mConnection.removeTrailingSpace(); return false; @@ -2233,8 +2224,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return false; } - private void handleCharacter(final int primaryCode, final int x, - final int y, final int spaceState) { + private void handleCharacter(final int primaryCode, final int x, final int y, + final int spaceState) { // TODO: refactor this method to stop flipping isComposingWord around all the time, and // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter // which has the same name as other handle* methods but is not the same. @@ -2254,7 +2245,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); isComposingWord = false; } // We want to find out whether to start composing a new word with this character. If so, @@ -2284,25 +2275,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetComposingState(false /* alsoResetLastComposedWord */); } if (isComposingWord) { - final int keyX, keyY; - if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) { - final KeyDetector keyDetector = - mKeyboardSwitcher.getMainKeyboardView().getKeyDetector(); - keyX = keyDetector.getTouchX(x); - keyY = keyDetector.getTouchY(y); - } else { - keyX = x; - keyY = y; - } + final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + // TODO: We should reconsider which coordinate system should be used to represent + // keyboard event. + final int keyX = mainKeyboardView.getKeyX(x); + final int keyY = mainKeyboardView.getKeyY(y); mWordComposer.add(primaryCode, keyX, keyY); // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + // We pass 1 to getPreviousWordForSuggestion because we were not composing a word + // yet, so the word we want is the 1st word before the cursor. + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( + getActualCapsMode(), + getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord */)); } mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpace(primaryCode, - spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x); + final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, + Constants.SUGGESTION_STRIP_COORDINATE == x); sendKeyCodePoint(primaryCode); @@ -2366,7 +2356,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the separator at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing if (currentSettings.mCorrectionEnabled) { @@ -2539,6 +2529,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } + /** + * Get the nth previous word before the cursor as context for the suggestion process. + * @param currentSettings the current settings values. + * @param nthPreviousWord reverse index of the word to get (1-indexed) + * @return the nth previous word before the cursor. + */ + private String getNthPreviousWordForSuggestion(final SettingsValues currentSettings, + final int nthPreviousWord) { + if (currentSettings.mCurrentLanguageHasSpaces) { + // If we are typing in a language with spaces we can just look up the previous + // word from textview. + return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord); + } else { + return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null + : mLastComposedWord.mCommittedWord; + } + } + private void getSuggestedWords(final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); @@ -2552,17 +2560,31 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // should just skip whitespace if any, so 1. final SettingsValues currentSettings = mSettings.getCurrent(); final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues; - final String prevWord; - if (currentSettings.mCurrentLanguageHasSpaces) { - // If we are typing in a language with spaces we can just look up the previous - // word from textview. - prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, - mWordComposer.isComposingWord() ? 2 : 1); + + final String previousWord; + if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) { + previousWord = mWordComposer.getPreviousWord(); } else { - prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null - : mLastComposedWord.mCommittedWord; + // Not composing: this is for prediction. + // TODO: read the previous word earlier for prediction, like we are doing for + // normal suggestions. + previousWord = getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord*/); + } + if (DEBUG) { + // TODO: this is for checking consistency with older versions. Remove this when + // we are confident this is stable. + // We're checking the previous word in the text field against the memorized previous + // word. If we are composing a word we should have the second word before the cursor + // memorized, otherwise we should have the first. + final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings, + mWordComposer.isComposingWord() ? 2 : 1); + if (!TextUtils.equals(previousWord, rereadPrevWord)) { + throw new RuntimeException("Unexpected previous word: " + + previousWord + " <> " + rereadPrevWord); + } } - suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), + suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(), + keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId, sequenceNumber, callback); } @@ -2679,7 +2701,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, separator, mWordComposer.isBatchMode(), suggestedWords); } - mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator); if (!typedWord.equals(autoCorrection)) { @@ -2750,7 +2771,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // typed word. final String replacedWord = mWordComposer.getTypedWord(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords); - mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -2833,7 +2853,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary; if (userHistoryDictionary == null) return null; - final String prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, 2); + final String prevWord = mConnection.getNthPreviousWord(currentSettings, 2); final String secondWord; if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); @@ -2845,7 +2865,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int maxFreq = AutoCorrectionUtils.getMaxFrequency( suggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0); + userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0, + (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis()))); return prevWord; } @@ -2901,7 +2922,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(typedWord, + getNthPreviousWordForSuggestion(currentSettings, + // We want the previous word for suggestion. If we have chars in the word + // before the cursor, then we want the word before that, hence 2; otherwise, + // we want the word immediately before the cursor, hence 1. + 0 == numberOfCharsInWordBeforeCursor ? 1 : 2), + mKeyboardSwitcher.getKeyboard()); mWordComposer.setCursorPositionWithinWord( typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); mConnection.setComposingRegion( @@ -2979,7 +3006,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void restartSuggestionsOnWordBeforeCursor(final String word) { - mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(word, + // Previous word is the 2nd word before cursor because we are restarting on the + // 1st word before cursor. + getNthPreviousWordForSuggestion(mSettings.getCurrent(), 2 /* nthPreviousWord */), + mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); mConnection.setComposingText(word, 1); @@ -2997,7 +3028,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * @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 (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, + mLastSelectionEnd, false)) { if (0 < remainingTries) { mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return; @@ -3024,8 +3056,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen throw new RuntimeException("revertCommit, but we are composing a word"); } final CharSequence wordBeforeCursor = - mConnection.getTextBeforeCursor(deleteLength, 0) - .subSequence(0, cancelLength); + mConnection.getTextBeforeCursor(deleteLength, 0).subSequence(0, cancelLength); if (!TextUtils.equals(committedWord, wordBeforeCursor)) { throw new RuntimeException("revertCommit check failed: we thought we were " + "reverting \"" + committedWord @@ -3045,7 +3076,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { // For languages without spaces, we revert the typed string but the cursor is flush // with the typed word, so we need to resume suggestions right away. - mWordComposer.setComposingWord(stringToCommit, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(stringToCommit, previousWord, + mKeyboardSwitcher.getKeyboard()); mConnection.setComposingText(stringToCommit, 1); } if (mSettings.isInternal()) { @@ -3095,8 +3127,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void hapticAndAudioFeedback(final int code, final int repeatCount) { final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (keyboardView != null && keyboardView.isInSlidingKeyInput()) { - // No need to feedback while sliding input. + if (keyboardView != null && keyboardView.isInDraggingFinger()) { + // No need to feedback while finger is dragging. return; } if (repeatCount > 0) { @@ -3226,8 +3258,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Intent intent = IntentUtils.getInputLanguageSelectionIntent( mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); break; case 1: @@ -3236,9 +3268,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } }; - final AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setItems(items, listener) - .setTitle(title); + final AlertDialog.Builder builder = + new AlertDialog.Builder(this).setItems(items, listener).setTitle(title); showOptionDialog(builder.create()); } @@ -3299,11 +3330,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Printer p = new PrintWriterPrinter(fout); p.println("LatinIME state :"); + p.println(" VersionCode = " + ApplicationUtils.getVersionCode(this)); + p.println(" VersionName = " + ApplicationUtils.getVersionName(this)); final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); final SettingsValues settingsValues = mSettings.getCurrent(); - p.println(" mIsSuggestionsSuggestionsRequested = " + p.println(" mIsSuggestionsRequested = " + settingsValues.isSuggestionsRequested(mDisplayOrientation)); p.println(" mCorrectionEnabled=" + settingsValues.mCorrectionEnabled); p.println(" isComposingWord=" + mWordComposer.isComposingWord()); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 673d1b4c2..37311acf2 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -57,14 +57,19 @@ public final class RichInputConnection { private static final int INVALID_CURSOR_POSITION = -1; /** - * This variable contains an expected value for the cursor position. This is where the - * cursor may end up after all the keyboard-triggered updates have passed. We keep this to - * compare it to the actual cursor position to guess whether the move was caused by a - * keyboard command or not. - * It's not really the cursor position: the cursor may not be there yet, and it's also expected - * there be cases where it never actually comes to be there. + * This variable contains an expected value for the selection start position. This is where the + * cursor or selection start may end up after all the keyboard-triggered updates have passed. We + * keep this to compare it to the actual selection start to guess whether the move was caused by + * a keyboard command or not. + * It's not really the selection start position: the selection start may not be there yet, and + * in some cases, it may never arrive there. */ - private int mExpectedCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points + private int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points + /** + * The expected selection end. Only differs from mExpectedSelStart if a non-empty selection is + * expected. The same caveats as mExpectedSelStart apply. + */ + private int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points /** * This contains the committed text immediately preceding the cursor and the composing * text if any. It is refreshed when the cursor moves by calling upon the TextView. @@ -103,16 +108,16 @@ public final class RichInputConnection { final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString() : beforeCursor.subSequence(beforeCursor.length() - actualLength, beforeCursor.length()).toString(); - if (et.selectionStart != mExpectedCursorPosition + if (et.selectionStart != mExpectedSelStart || !(reference.equals(internal.toString()))) { - final String context = "Expected cursor position = " + mExpectedCursorPosition - + "\nActual cursor position = " + et.selectionStart + final String context = "Expected selection start = " + mExpectedSelStart + + "\nActual selection start = " + et.selectionStart + "\nExpected text = " + internal.length() + " " + internal + "\nActual text = " + reference.length() + " " + reference; ((LatinIME)mParent).debugDumpStateAndCrashWithException(context); } else { Log.e(TAG, DebugLogUtils.getStackTrace(2)); - Log.e(TAG, "Exp <> Actual : " + mExpectedCursorPosition + " <> " + et.selectionStart); + Log.e(TAG, "Exp <> Actual : " + mExpectedSelStart + " <> " + et.selectionStart); } } @@ -150,16 +155,50 @@ public final class RichInputConnection { * 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. + * @param newSelStart the new position of the selection start, as received from the system. + * @param newSelEnd the new position of the selection end, 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; + public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart, + final int newSelEnd, final boolean shouldFinishComposition) { + mExpectedSelStart = newSelStart; + mExpectedSelEnd = newSelEnd; mComposingText.setLength(0); + final boolean didReloadTextSuccessfully = reloadTextCache(); + if (!didReloadTextSuccessfully) { + Log.d(TAG, "Will try to retrieve text later."); + return false; + } + final int lengthOfTextBeforeCursor = mCommittedTextBeforeComposingText.length(); + if (lengthOfTextBeforeCursor > newSelStart + || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE + && newSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { + // newSelStart and newSelEnd 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 newSelStart says, then we know it was lying. In both + // cases the length is more reliable. Note that we only have to check newSelStart (not + // newSelEnd) since if newSelEnd is wrong, the newSelStart will be wrong as well. + mExpectedSelStart = lengthOfTextBeforeCursor; + mExpectedSelEnd = lengthOfTextBeforeCursor; + } + if (null != mIC && shouldFinishComposition) { + mIC.finishComposingText(); + if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + ResearchLogger.richInputConnection_finishComposingText(); + } + } + return true; + } + + /** + * Reload the cached text from the InputConnection. + * + * @return true if successful + */ + private boolean reloadTextCache() { mCommittedTextBeforeComposingText.setLength(0); mIC = mParent.getCurrentInputConnection(); // Call upon the inputconnection directly since our own method is using the cache, and @@ -169,27 +208,12 @@ public final class RichInputConnection { 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"); + mExpectedSelStart = INVALID_CURSOR_POSITION; + mExpectedSelEnd = INVALID_CURSOR_POSITION; + Log.e(TAG, "Unable to connect to the editor to retrieve text."); 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; } @@ -218,7 +242,8 @@ public final class RichInputConnection { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(text); - mExpectedCursorPosition += text.length() - mComposingText.length(); + mExpectedSelStart += text.length() - mComposingText.length(); + mExpectedSelEnd = mExpectedSelStart; mComposingText.setLength(0); if (null != mIC) { mIC.commitText(text, i); @@ -231,7 +256,7 @@ public final class RichInputConnection { } public boolean canDeleteCharacters() { - return mExpectedCursorPosition > 0; + return mExpectedSelStart > 0; } /** @@ -268,11 +293,10 @@ public final class RichInputConnection { // heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so. // 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) { - final CharSequence textBeforeCursor = getTextBeforeCursor( - Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); - if (!TextUtils.isEmpty(textBeforeCursor)) { - mCommittedTextBeforeComposingText.append(textBeforeCursor); + if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedSelStart) { + if (!reloadTextCache()) { + Log.w(TAG, "Unable to connect to the editor. " + + "Setting caps mode without knowing text."); } } // This never calls InputConnection#getCapsMode - in fact, it's a static method that @@ -295,8 +319,8 @@ public final class RichInputConnection { // However, if we don't have an expected cursor position, then we should always // go fetch the cache again (as it happens, INVALID_CURSOR_POSITION < 0, so we need to // test for this explicitly) - if (INVALID_CURSOR_POSITION != mExpectedCursorPosition - && (cachedLength >= n || cachedLength >= mExpectedCursorPosition)) { + if (INVALID_CURSOR_POSITION != mExpectedSelStart + && (cachedLength >= n || cachedLength >= mExpectedSelStart)) { final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText); // We call #toString() here to create a temporary object. // In some situations, this method is called on a worker thread, and it's possible @@ -336,10 +360,14 @@ public final class RichInputConnection { + remainingChars, 0); mCommittedTextBeforeComposingText.setLength(len); } - if (mExpectedCursorPosition > beforeLength) { - mExpectedCursorPosition -= beforeLength; + if (mExpectedSelStart > beforeLength) { + mExpectedSelStart -= beforeLength; + mExpectedSelEnd -= beforeLength; } else { - mExpectedCursorPosition = 0; + // There are fewer characters before the cursor in the buffer than we are being asked to + // delete. Only delete what is there. + mExpectedSelStart = 0; + mExpectedSelEnd -= mExpectedSelStart; } if (null != mIC) { mIC.deleteSurroundingText(beforeLength, afterLength); @@ -373,7 +401,8 @@ public final class RichInputConnection { switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_ENTER: mCommittedTextBeforeComposingText.append("\n"); - mExpectedCursorPosition += 1; + mExpectedSelStart += 1; + mExpectedSelEnd = mExpectedSelStart; break; case KeyEvent.KEYCODE_DEL: if (0 == mComposingText.length()) { @@ -385,18 +414,24 @@ public final class RichInputConnection { } else { mComposingText.delete(mComposingText.length() - 1, mComposingText.length()); } - if (mExpectedCursorPosition > 0) mExpectedCursorPosition -= 1; + if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) { + // TODO: Handle surrogate pairs. + mExpectedSelStart -= 1; + } + mExpectedSelEnd = mExpectedSelStart; break; case KeyEvent.KEYCODE_UNKNOWN: if (null != keyEvent.getCharacters()) { mCommittedTextBeforeComposingText.append(keyEvent.getCharacters()); - mExpectedCursorPosition += keyEvent.getCharacters().length(); + mExpectedSelStart += keyEvent.getCharacters().length(); + mExpectedSelEnd = mExpectedSelStart; } break; default: - final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1); + final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar()); mCommittedTextBeforeComposingText.append(text); - mExpectedCursorPosition += text.length(); + mExpectedSelStart += text.length(); + mExpectedSelEnd = mExpectedSelStart; break; } } @@ -430,10 +465,12 @@ public final class RichInputConnection { public void setComposingText(final CharSequence text, final int newCursorPosition) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); - mExpectedCursorPosition += text.length() - mComposingText.length(); + mExpectedSelStart += text.length() - mComposingText.length(); + mExpectedSelEnd = mExpectedSelStart; mComposingText.setLength(0); mComposingText.append(text); - // TODO: support values of i != 1. At this time, this is never called with i != 1. + // TODO: support values of newCursorPosition != 1. At this time, this is never called with + // newCursorPosition != 1. if (null != mIC) { mIC.setComposingText(text, newCursorPosition); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -443,19 +480,31 @@ public final class RichInputConnection { if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); } - public void setSelection(final int start, final int end) { + /** + * Set the selection of the text editor. + * + * Calls through to {@link InputConnection#setSelection(int, int)}. + * + * @param start the character index where the selection should start. + * @param end the character index where the selection should end. + * @return Returns true on success, false if the input connection is no longer valid either when + * setting the selection or when retrieving the text cache at that point. + */ + public boolean setSelection(final int start, final int end) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); + mExpectedSelStart = start; + mExpectedSelEnd = end; if (null != mIC) { - mIC.setSelection(start, end); + final boolean isIcValid = mIC.setSelection(start, end); + if (!isIcValid) { + return false; + } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.richInputConnection_setSelection(start, end); } } - mExpectedCursorPosition = start; - mCommittedTextBeforeComposingText.setLength(0); - mCommittedTextBeforeComposingText.append( - getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0)); + return reloadTextCache(); } public void commitCorrection(final CorrectionInfo correctionInfo) { @@ -476,7 +525,8 @@ public final class RichInputConnection { // text should never be null, but just in case, it's better to insert nothing than to crash if (null == text) text = ""; mCommittedTextBeforeComposingText.append(text); - mExpectedCursorPosition += text.length() - mComposingText.length(); + mExpectedSelStart += text.length() - mComposingText.length(); + mExpectedSelEnd = mExpectedSelStart; mComposingText.setLength(0); if (null != mIC) { mIC.commitCompletion(completionInfo); @@ -488,7 +538,7 @@ public final class RichInputConnection { } @SuppressWarnings("unused") - public String getNthPreviousWord(final String sentenceSeperators, final int n) { + public String getNthPreviousWord(final SettingsValues currentSettingsValues, final int n) { mIC = mParent.getCurrentInputConnection(); if (null == mIC) return null; final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); @@ -507,7 +557,7 @@ public final class RichInputConnection { } } } - return getNthPreviousWord(prev, sentenceSeperators, n); + return getNthPreviousWord(prev, currentSettingsValues, n); } private static boolean isSeparator(int code, String sep) { @@ -531,7 +581,7 @@ public final class RichInputConnection { // (n = 2) "abc |" -> null // (n = 2) "abc. def|" -> null public static String getNthPreviousWord(final CharSequence prev, - final String sentenceSeperators, final int n) { + final SettingsValues currentSettingsValues, final int n) { if (prev == null) return null; final String[] w = spaceRegex.split(prev); @@ -543,7 +593,8 @@ public final class RichInputConnection { // If ends in a separator, return null final char lastChar = nthPrevWord.charAt(length - 1); - if (sentenceSeperators.contains(String.valueOf(lastChar))) return null; + if (currentSettingsValues.isWordSeparator(lastChar) + || currentSettingsValues.isWordConnector(lastChar)) return null; return nthPrevWord; } @@ -758,20 +809,30 @@ public final class RichInputConnection { * this update and not the ones in-between. This is almost impossible to achieve even trying * very very hard. * - * @param oldSelStart The value of the old cursor position in the update. - * @param newSelStart The value of the new cursor position in the update. + * @param oldSelStart The value of the old selection in the update. + * @param newSelStart The value of the new selection in the update. + * @param oldSelEnd The value of the old selection end in the update. + * @param newSelEnd The value of the new selection end in the update. * @return whether this is a belated expected update or not. */ - public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) { - // If this is an update that arrives at our expected position, it's a belated update. - if (newSelStart == mExpectedCursorPosition) return true; - // If this is an update that moves the cursor from our expected position, it must be - // an explicit move. - if (oldSelStart == mExpectedCursorPosition) return false; - // The following returns true if newSelStart is between oldSelStart and - // mCurrentCursorPosition. We assume that if the updated position is between the old - // position and the expected position, then it must be a belated update. - return (newSelStart - oldSelStart) * (mExpectedCursorPosition - newSelStart) >= 0; + public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart, + final int oldSelEnd, final int newSelEnd) { + // This update is "belated" if we are expecting it. That is, mExpectedSelStart and + // mExpectedSelEnd match the new values that the TextView is updating TO. + if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true; + // This update is not belated if mExpectedSelStart and mExpeectedSelend match the old + // values, and one of newSelStart or newSelEnd is updated to a different value. In this + // case, there is likely something other than the IME that has moved the selection endpoint + // to the new value. + if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd + && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false; + // If nether of the above two cases holds, then the system may be having trouble keeping up + // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart + // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then + // assume a belated update. + return (newSelStart == newSelEnd) + && (newSelStart - oldSelStart) * (mExpectedSelStart - newSelStart) >= 0 + && (newSelEnd - oldSelEnd) * (mExpectedSelEnd - newSelEnd) >= 0; } /** diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index cd9c89f04..379eaaa3d 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -32,7 +32,9 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.List; @@ -56,23 +58,34 @@ public final class SubtypeSwitcher { private InputMethodSubtype mEmojiSubtype; private boolean mIsNetworkConnected; + private static final String KEYBOARD_MODE = "keyboard"; // 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_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 */); + private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3; + private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE = + "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = + InputMethodSubtypeCompatUtils.newInputMethodSubtype( + R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, + EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE, + false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, + SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE); // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}. // Dummy Emoji subtype. See {@link R.xml.method}. - private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype( - 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 */); + private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0; + private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE = + "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = + InputMethodSubtypeCompatUtils.newInputMethodSubtype( + R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark, + SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE, + EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE, + false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */, + SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE); static final class NeedsToDisplayLanguage { private int mEnabledSubtypeCount; @@ -256,18 +269,23 @@ public final class SubtypeSwitcher { return mNeedsToDisplayLanguage.getValue(); } - private static Locale sForcedLocaleForTesting = null; + private static InputMethodSubtype sForcedSubtypeForTesting = null; @UsedForTesting - void forceLocale(final Locale locale) { - sForcedLocaleForTesting = locale; + void forceSubtype(final InputMethodSubtype subtype) { + sForcedSubtypeForTesting = subtype; } public Locale getCurrentSubtypeLocale() { - if (null != sForcedLocaleForTesting) return sForcedLocaleForTesting; + if (null != sForcedSubtypeForTesting) { + return LocaleUtils.constructLocaleFromString(sForcedSubtypeForTesting.getLocale()); + } return SubtypeLocaleUtils.getSubtypeLocale(getCurrentSubtype()); } public InputMethodSubtype getCurrentSubtype() { + if (null != sForcedSubtypeForTesting) { + return sForcedSubtypeForTesting; + } return mRichImm.getCurrentInputMethodSubtype(getNoLanguageSubtype()); } @@ -279,8 +297,8 @@ public final class SubtypeSwitcher { if (mNoLanguageSubtype != null) { return mNoLanguageSubtype; } - Log.w(TAG, "Can't find no lanugage with QWERTY subtype"); - Log.w(TAG, "No input method subtype found; return dummy subtype: " + Log.w(TAG, "Can't find any language with QWERTY subtype"); + Log.w(TAG, "No input method subtype found; returning dummy subtype: " + DUMMY_NO_LANGUAGE_SUBTYPE); return DUMMY_NO_LANGUAGE_SUBTYPE; } @@ -293,8 +311,9 @@ public final class SubtypeSwitcher { if (mEmojiSubtype != null) { return mEmojiSubtype; } - Log.w(TAG, "Can't find Emoji subtype"); - Log.w(TAG, "No input method subtype found; return dummy subtype: " + DUMMY_EMOJI_SUBTYPE); + Log.w(TAG, "Can't find emoji subtype"); + Log.w(TAG, "No input method subtype found; returning dummy subtype: " + + DUMMY_EMOJI_SUBTYPE); return DUMMY_EMOJI_SUBTYPE; } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 0a4c7a55d..0ecb41100 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -25,7 +25,6 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.personalization.PersonalizationDictionary; -import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; @@ -90,7 +89,6 @@ public final class Suggest { PreferenceManager.getDefaultSharedPreferences(context))) { mOnlyDictionarySetForDebug = new HashSet<String>(); mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION); - mOnlyDictionarySetForDebug.add(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA); } } @@ -194,12 +192,6 @@ public final class Suggest { addOrReplaceDictionaryInternal(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); } - public void setPersonalizationPredictionDictionary( - final PersonalizationPredictionDictionary personalizationPredictionDictionary) { - addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA, - personalizationPredictionDictionary); - } - public void setPersonalizationDictionary( final PersonalizationDictionary personalizationDictionary) { addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION, @@ -432,7 +424,8 @@ public final class Suggest { final String scoreInfoString; if (normalizedScore > 0) { scoreInfoString = String.format( - Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore); + Locale.ROOT, "%d (%4.2f), %s", cur.mScore, normalizedScore, + cur.mSourceDict.mDictType); } else { scoreInfoString = Integer.toString(cur.mScore); } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java index 6405b5e46..9cb2f5bc4 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java @@ -22,14 +22,15 @@ import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; +import java.util.Locale; public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary { - public SynchronouslyLoadedUserBinaryDictionary(final Context context, final String locale) { + public SynchronouslyLoadedUserBinaryDictionary(final Context context, final Locale locale) { this(context, locale, false); } - public SynchronouslyLoadedUserBinaryDictionary(final Context context, final String locale, + public SynchronouslyLoadedUserBinaryDictionary(final Context context, final Locale locale, final boolean alsoUseMoreRestrictiveLocales) { super(context, locale, alsoUseMoreRestrictiveLocales); } diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index 15b3d8d02..1dc3c54bb 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin; import android.content.ContentProviderClient; import android.content.ContentResolver; -import android.content.ContentUris; import android.content.Context; import android.database.ContentObserver; import android.database.Cursor; @@ -75,20 +74,21 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { final private String mLocale; final private boolean mAlsoUseMoreRestrictiveLocales; - public UserBinaryDictionary(final Context context, final String locale) { + public UserBinaryDictionary(final Context context, final Locale locale) { this(context, locale, false); } - public UserBinaryDictionary(final Context context, final String locale, + public UserBinaryDictionary(final Context context, final Locale locale, final boolean alsoUseMoreRestrictiveLocales) { - super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER, + super(context, getDictNameWithLocale(NAME, locale), locale, Dictionary.TYPE_USER, false /* isUpdatable */); if (null == locale) throw new NullPointerException(); // Catch the error earlier - if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) { + final String localeStr = locale.toString(); + if (SubtypeLocaleUtils.NO_LANGUAGE.equals(localeStr)) { // If we don't have a locale, insert into the "all locales" user dictionary. mLocale = USER_DICTIONARY_ALL_LANGUAGES; } else { - mLocale = locale; + mLocale = localeStr; } mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales; // Perform a managed query. The Activity will handle closing and re-querying the cursor diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 039dadc66..2f81d15d5 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -48,6 +48,10 @@ public final class WordComposer { // at any given time. However this is not limited in size, while mPrimaryKeyCodes is limited // to MAX_WORD_LENGTH code points. private final StringBuilder mTypedWord; + // The previous word (before the composing word). Used as context for suggestions. May be null + // after resetting and before starting a new composing word, or when there is no context like + // at the start of text for example. + private String mPreviousWord; private String mAutoCorrection; private boolean mIsResumed; private boolean mIsBatchMode; @@ -85,6 +89,7 @@ public final class WordComposer { mIsBatchMode = false; mCursorPositionWithinWord = 0; mRejectedBatchModeSuggestion = null; + mPreviousWord = null; refreshSize(); } @@ -101,6 +106,7 @@ public final class WordComposer { mIsBatchMode = source.mIsBatchMode; mCursorPositionWithinWord = source.mCursorPositionWithinWord; mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion; + mPreviousWord = source.mPreviousWord; refreshSize(); } @@ -118,6 +124,7 @@ public final class WordComposer { mIsBatchMode = false; mCursorPositionWithinWord = 0; mRejectedBatchModeSuggestion = null; + mPreviousWord = null; refreshSize(); } @@ -284,8 +291,13 @@ public final class WordComposer { /** * Set the currently composing word to the one passed as an argument. * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity. + * @param word the char sequence to set as the composing word. + * @param previousWord the previous word, to use as context for suggestions. Can be null if + * the context is nil (typically, at start of text). + * @param keyboard the keyboard this is typed on, for coordinate info/proximity. */ - public void setComposingWord(final CharSequence word, final Keyboard keyboard) { + public void setComposingWord(final CharSequence word, final String previousWord, + final Keyboard keyboard) { reset(); final int length = word.length(); for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { @@ -293,6 +305,7 @@ public final class WordComposer { addKeyInfo(codePoint, keyboard); } mIsResumed = true; + mPreviousWord = previousWord; } /** @@ -343,6 +356,10 @@ public final class WordComposer { return mTypedWord.toString(); } + public String getPreviousWord() { + return mPreviousWord; + } + /** * Whether or not the user typed a capital letter as the first letter in the word * @return capitalization preference @@ -388,18 +405,21 @@ public final class WordComposer { } /** - * Saves the caps mode at the start of composing. + * Saves the caps mode and the previous word at the start of composing. * - * WordComposer needs to know about this for several reasons. The first is, we need to know - * after the fact what the reason was, to register the correct form into the user history - * dictionary: if the word was automatically capitalized, we should insert it in all-lower - * case but if it's a manual pressing of shift, then it should be inserted as is. + * WordComposer needs to know about the caps mode for several reasons. The first is, we need + * to know after the fact what the reason was, to register the correct form into the user + * history dictionary: if the word was automatically capitalized, we should insert it in + * all-lower case but if it's a manual pressing of shift, then it should be inserted as is. * Also, batch input needs to know about the current caps mode to display correctly * capitalized suggestions. * @param mode the mode at the time of start + * @param previousWord the previous word as context for suggestions. May be null if none. */ - public void setCapitalizedModeAtStartComposingTime(final int mode) { + public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, + final String previousWord) { mCapitalizedMode = mode; + mPreviousWord = previousWord; } /** @@ -451,6 +471,7 @@ public final class WordComposer { mCapsCount = 0; mDigitsCount = 0; mIsBatchMode = false; + mPreviousWord = mTypedWord.toString(); mTypedWord.setLength(0); mCodePointSize = 0; mTrailingSingleQuotesCount = 0; @@ -464,7 +485,8 @@ public final class WordComposer { return lastComposedWord; } - public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) { + public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord, + final String previousWord) { mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes; mInputPointers.set(lastComposedWord.mInputPointers); mTypedWord.setLength(0); @@ -475,6 +497,7 @@ public final class WordComposer { mCursorPositionWithinWord = mCodePointSize; mRejectedBatchModeSuggestion = null; mIsResumed = true; + mPreviousWord = previousWord; } public boolean isBatchMode() { diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java index fda97dafc..bbbb8e461 100644 --- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java @@ -32,27 +32,29 @@ import java.util.TreeMap; * A base class of the binary dictionary decoder. */ public abstract class AbstractDictDecoder implements DictDecoder { - protected FileHeader readHeader(final DictBuffer dictBuffer) + private static final int SUCCESS = 0; + private static final int ERROR_CANNOT_READ = 1; + private static final int ERROR_WRONG_FORMAT = 2; + + protected FileHeader readHeader(final DictBuffer headerBuffer) throws IOException, UnsupportedFormatException { - if (dictBuffer == null) { + if (headerBuffer == null) { openDictBuffer(); } - final int version = HeaderReader.readVersion(dictBuffer); + final int version = HeaderReader.readVersion(headerBuffer); 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); - + final int optionsFlags = HeaderReader.readOptionFlags(headerBuffer); + final int headerSize = HeaderReader.readHeaderSize(headerBuffer); if (headerSize < 0) { throw new UnsupportedFormatException("header size can't be negative."); } - final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer, + final HashMap<String, String> attributes = HeaderReader.readAttributes(headerBuffer, headerSize); final FileHeader header = new FileHeader(headerSize, @@ -204,4 +206,25 @@ public abstract class AbstractDictDecoder implements DictDecoder { return readLength; } } + + /** + * Check whether the header contains the expected information. This is a no-error method, + * that will return an error code and never throw a checked exception. + * @return an error code, either ERROR_* or SUCCESS. + */ + private int checkHeader() { + try { + readHeader(); + } catch (IOException e) { + return ERROR_CANNOT_READ; + } catch (UnsupportedFormatException e) { + return ERROR_WRONG_FORMAT; + } + return SUCCESS; + } + + @Override + public boolean hasValidRawBinaryDictionary() { + return checkHeader() == SUCCESS; + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 216492b4d..83ee7d685 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -24,12 +24,9 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Map; import java.util.TreeMap; @@ -169,6 +166,14 @@ public final class BinaryDictDecoderUtils { return size; } + static int getCharArraySize(final int[] chars, final int start, final int end) { + int size = 0; + for (int i = start; i < end; ++i) { + size += getCharSize(chars[i]); + } + return size; + } + /** * Writes a char array to a byte buffer. * @@ -200,8 +205,7 @@ public final class BinaryDictDecoderUtils { * @param word the string to write. * @return the size written, in bytes. */ - static int writeString(final byte[] buffer, final int origin, - final String word) { + static int writeString(final byte[] buffer, final int origin, final String word) { final int length = word.length(); int index = origin; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { @@ -223,22 +227,62 @@ public final class BinaryDictDecoderUtils { * * This will also write the terminator byte. * - * @param buffer the OutputStream to write to. + * @param stream the OutputStream to write to. * @param word the string to write. + * @return the size written, in bytes. */ - static void writeString(final OutputStream buffer, final String word) throws IOException { + static int writeString(final OutputStream stream, final String word) throws IOException { final int length = word.length(); + int written = 0; for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { final int codePoint = word.codePointAt(i); - if (1 == getCharSize(codePoint)) { - buffer.write((byte) codePoint); + final int charSize = getCharSize(codePoint); + if (1 == charSize) { + stream.write((byte) codePoint); } else { - buffer.write((byte) (0xFF & (codePoint >> 16))); - buffer.write((byte) (0xFF & (codePoint >> 8))); - buffer.write((byte) (0xFF & codePoint)); + stream.write((byte) (0xFF & (codePoint >> 16))); + stream.write((byte) (0xFF & (codePoint >> 8))); + stream.write((byte) (0xFF & codePoint)); } + written += charSize; } - buffer.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR); + stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR); + written += FormatSpec.PTNODE_TERMINATOR_SIZE; + return written; + } + + /** + * Writes an array of code points with our character format to an OutputStream. + * + * This will also write the terminator byte. + * + * @param stream the OutputStream to write to. + * @param codePoints the array of code points + * @return the size written, in bytes. + */ + // TODO: Merge this method with writeCharArray and rename the various write* methods to + // make the difference clear. + static int writeCodePoints(final OutputStream stream, final int[] codePoints, + final int startIndex, final int endIndex) + throws IOException { + int written = 0; + for (int i = startIndex; i < endIndex; ++i) { + final int codePoint = codePoints[i]; + final int charSize = getCharSize(codePoint); + if (1 == charSize) { + stream.write((byte) codePoint); + } else { + stream.write((byte) (0xFF & (codePoint >> 16))); + stream.write((byte) (0xFF & (codePoint >> 8))); + stream.write((byte) (0xFF & codePoint)); + } + written += charSize; + } + if (endIndex - startIndex > 1) { + stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR); + written += FormatSpec.PTNODE_TERMINATOR_SIZE; + } + return written; } /** @@ -556,7 +600,7 @@ public final class BinaryDictDecoderUtils { Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>(); Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>(); - final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mHeaderSize, + final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mBodyOffset, reverseNodeArrayMapping, reversePtNodeMapping, fileHeader.mFormatOptions); FusionDictionary newDict = new FusionDictionary(root, fileHeader.mDictionaryOptions); @@ -592,32 +636,10 @@ public final class BinaryDictDecoderUtils { /** * Basic test to find out whether the file is a binary dictionary or not. * - * Concretely this only tests the magic number. - * * @param file The file to test. * @return true if it's a binary dictionary, false otherwise */ public static boolean isBinaryDictionary(final File file) { - FileInputStream inStream = null; - try { - inStream = new FileInputStream(file); - final ByteBuffer buffer = inStream.getChannel().map( - FileChannel.MapMode.READ_ONLY, 0, file.length()); - final int version = getFormatVersion(new ByteBufferDictBuffer(buffer)); - return (version >= FormatSpec.MINIMUM_SUPPORTED_VERSION - && version <= FormatSpec.MAXIMUM_SUPPORTED_VERSION); - } catch (FileNotFoundException e) { - return false; - } catch (IOException e) { - return false; - } finally { - if (inStream != null) { - try { - inStream.close(); - } catch (IOException e) { - // do nothing - } - } - } + return FormatSpec.getDictDecoder(file).hasValidRawBinaryDictionary(); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index f761829de..c0dad3db2 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; @@ -245,6 +246,26 @@ public class BinaryDictEncoderUtils { } } + static void writeUIntToDictBuffer(final DictBuffer dictBuffer, final int value, + final int size) { + switch(size) { + case 4: + dictBuffer.put((byte) ((value >> 24) & 0xFF)); + /* fall through */ + case 3: + dictBuffer.put((byte) ((value >> 16) & 0xFF)); + /* fall through */ + case 2: + dictBuffer.put((byte) ((value >> 8) & 0xFF)); + /* fall through */ + case 1: + dictBuffer.put((byte) (value & 0xFF)); + break; + default: + /* nop */ + } + } + // End utility methods // This method is responsible for finding a nice ordering of the nodes that favors run-time @@ -690,6 +711,13 @@ public class BinaryDictEncoderUtils { + word + " is " + unigramFrequency); bigramFrequency = unigramFrequency; } + bigramFlags += getBigramFrequencyDiff(unigramFrequency, bigramFrequency) + & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; + return bigramFlags; + } + + public static int getBigramFrequencyDiff(final int unigramFrequency, + final int bigramFrequency) { // We compute the difference between 255 (which means probability = 1) and the // unigram score. We split this into a number of discrete steps. // Now, the steps are numbered 0~15; 0 represents an increase of 1 step while 15 @@ -723,9 +751,7 @@ public class BinaryDictEncoderUtils { // include this bigram in the dictionary. For now, register as 0, and live with the // small over-estimation that we get in this case. TODO: actually remove this bigram // if discretizedFrequency < 0. - final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0; - bigramFlags += finalBigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; - return bigramFlags; + return discretizedFrequency > 0 ? discretizedFrequency : 0; } /** diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index d5516ef46..86ebf5844 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -62,7 +62,7 @@ public final class BinaryDictIOUtils { * Retrieves all node arrays without recursive call. */ private static void readUnigramsAndBigramsBinaryInner(final DictDecoder dictDecoder, - final int headerSize, final Map<Integer, String> words, + final int bodyOffset, final Map<Integer, String> words, final Map<Integer, Integer> frequencies, final Map<Integer, ArrayList<PendingAttribute>> bigrams, final FormatOptions formatOptions) { @@ -71,7 +71,7 @@ public final class BinaryDictIOUtils { Stack<Position> stack = new Stack<Position>(); int index = 0; - Position initPos = new Position(headerSize, 0); + Position initPos = new Position(bodyOffset, 0); stack.push(initPos); while (!stack.empty()) { @@ -154,7 +154,7 @@ public final class BinaryDictIOUtils { UnsupportedFormatException { // Read header final FileHeader header = dictDecoder.readHeader(); - readUnigramsAndBigramsBinaryInner(dictDecoder, header.mHeaderSize, words, + readUnigramsAndBigramsBinaryInner(dictDecoder, header.mBodyOffset, words, frequencies, bigrams, header.mFormatOptions); } @@ -245,8 +245,7 @@ public final class BinaryDictIOUtils { /** * @return the size written, in bytes. Always 3 bytes. */ - static int writeSInt24ToBuffer(final DictBuffer dictBuffer, - final int value) { + static int writeSInt24ToBuffer(final DictBuffer dictBuffer, final int value) { final int absValue = Math.abs(value); dictBuffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF)); dictBuffer.put((byte)((absValue >> 8) & 0xFF)); @@ -301,35 +300,6 @@ public final class BinaryDictIOUtils { } /** - * Write a string to a stream. - * - * @param destination the stream to write. - * @param word the string to be written. - * @return the size written, in bytes. - * @throws IOException - */ - private static int writeString(final OutputStream destination, final String word) - throws IOException { - int size = 0; - final int length = word.length(); - for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { - final int codePoint = word.codePointAt(i); - if (CharEncoding.getCharSize(codePoint) == 1) { - destination.write((byte)codePoint); - size++; - } else { - destination.write((byte)(0xFF & (codePoint >> 16))); - destination.write((byte)(0xFF & (codePoint >> 8))); - destination.write((byte)(0xFF & codePoint)); - size += 3; - } - } - destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR); - size += FormatSpec.PTNODE_TERMINATOR_SIZE; - return size; - } - - /** * Write a PtNode to an output stream from a PtNodeInfo. * A PtNode is an in-memory representation of a node in the patricia trie. * A PtNode info is a container for low-level information about how the @@ -387,7 +357,7 @@ public final class BinaryDictIOUtils { destination.write((byte)BinaryDictEncoderUtils.makeShortcutFlags( shortcutIterator.hasNext(), target.mFrequency)); size++; - size += writeString(destination, target.mWord); + size += CharEncoding.writeString(destination, target.mWord); } } @@ -445,6 +415,27 @@ public final class BinaryDictIOUtils { } /** + * Writes a PtNodeCount to the stream. + * + * @param destination the stream to write. + * @param ptNodeCount the count. + * @return the size written in bytes. + */ + static int writePtNodeCount(final OutputStream destination, final int ptNodeCount) + throws IOException { + final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount); + // the count must fit on one byte or two bytes. + // Please see comments in FormatSpec. + if (countSize != 1 && countSize != 2) { + throw new RuntimeException("Strange size from getPtNodeCountSize : " + countSize); + } + final int encodedPtNodeCount = (countSize == 2) ? + (ptNodeCount | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG) : ptNodeCount; + BinaryDictEncoderUtils.writeUIntToStream(destination, encodedPtNodeCount, countSize); + return countSize; + } + + /** * Write a node array to the stream. * * @param destination the stream to write. @@ -454,20 +445,7 @@ public final class BinaryDictIOUtils { */ static int writeNodes(final OutputStream destination, final PtNodeInfo[] infos) throws IOException { - int size = getPtNodeCountSize(infos.length); - switch (getPtNodeCountSize(infos.length)) { - case 1: - destination.write((byte)infos.length); - break; - case 2: - final int encodedPtNodeCount = - infos.length | FormatSpec.LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; - destination.write((byte)(encodedPtNodeCount >> 8)); - destination.write((byte)(encodedPtNodeCount & 0xFF)); - break; - default: - throw new RuntimeException("Invalid node count size."); - } + int size = writePtNodeCount(destination, infos.length); for (final PtNodeInfo info : infos) size += writePtNode(destination, info); writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS); return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE; diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java index 3dbeee099..b4838f00f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java @@ -35,6 +35,7 @@ import java.util.TreeMap; /** * An interface of binary dictionary decoders. */ +// TODO: Straighten out responsibility for the buffer's file pointer. public interface DictDecoder { /** @@ -43,7 +44,7 @@ public interface DictDecoder { public FileHeader readHeader() throws IOException, UnsupportedFormatException; /** - * Reads PtNode from nodeAddress. + * Reads PtNode from ptNodePos. * @param ptNodePos the position of PtNode. * @param formatOptions the format options. * @return PtNodeInfo. @@ -127,7 +128,8 @@ public interface DictDecoder { * Opens the dictionary file and makes DictBuffer. */ @UsedForTesting - public void openDictBuffer() throws FileNotFoundException, IOException; + public void openDictBuffer() throws FileNotFoundException, IOException, + UnsupportedFormatException; @UsedForTesting public boolean isDictBufferOpen(); @@ -228,4 +230,9 @@ public interface DictDecoder { } public void skipPtNode(final FormatOptions formatOptions); + + /** + * @return whether this decoder has a valid binary dictionary that it can decode. + */ + public boolean hasValidRawBinaryDictionary(); } diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java index 28da9ffdd..971b4ff9f 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java @@ -37,7 +37,7 @@ import java.util.Arrays; @UsedForTesting public final class DynamicBinaryDictIOUtils { private static final boolean DBG = false; - private static final int MAX_JUMPS = 10000; + static final int MAX_JUMPS = 10000; private DynamicBinaryDictIOUtils() { // This utility class is not publicly instantiable. diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index b56234f6d..846aacf11 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -199,13 +199,19 @@ public final class FormatSpec { */ public static final int MAGIC_NUMBER = 0x9BC13AFE; - static final int MINIMUM_SUPPORTED_VERSION = 2; - static final int MAXIMUM_SUPPORTED_VERSION = 4; static final int NOT_A_VERSION_NUMBER = -1; static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3; static final int FIRST_VERSION_WITH_TERMINAL_ID = 4; - static final int VERSION3 = 3; - static final int VERSION4 = 4; + + // These MUST have the same values as the relevant constants in format_utils.h. + // From version 4 on, we use version * 100 + revision as a version number. That allows + // us to change the format during development while having testing devices remove + // older files with each upgrade, while still having a readable versioning scheme. + public static final int VERSION2 = 2; + public static final int VERSION3 = 3; + public static final int VERSION4 = 400; + static final int MINIMUM_SUPPORTED_VERSION = VERSION2; + static final int MAXIMUM_SUPPORTED_VERSION = VERSION4; // These options need to be the same numeric values as the one in the native reading code. static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; @@ -263,8 +269,10 @@ public final class FormatSpec { static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3; static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2; - // These values are used only by version 4 or later. + // These values are used only by version 4 or later. They MUST match the definitions in + // ver4_dict_constants.cpp. static final String TRIE_FILE_EXTENSION = ".trie"; + public static final String HEADER_FILE_EXTENSION = ".header"; static final String FREQ_FILE_EXTENSION = ".freq"; static final String UNIGRAM_TIMESTAMP_FILE_EXTENSION = ".timestamp"; // tat = Terminal Address Table @@ -278,9 +286,9 @@ public final class FormatSpec { static final int UNIGRAM_TIMESTAMP_SIZE = 4; // With the English main dictionary as of October 2013, the size of bigram address table is - // is 584KB with the block size being 4. - // This is 91% of that of full address table. - static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4; + // is 345KB with the block size being 16. + // This is 54% of that of full address table. + static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16; static final int BIGRAM_CONTENT_COUNT = 2; static final int BIGRAM_FREQ_CONTENT_INDEX = 0; static final int BIGRAM_TIMESTAMP_CONTENT_INDEX = 1; @@ -293,7 +301,7 @@ public final class FormatSpec { static final int SHORTCUT_CONTENT_COUNT = 1; static final int SHORTCUT_CONTENT_INDEX = 0; // With the English main dictionary as of October 2013, the size of shortcut address table is - // 29KB with the block size being 64. + // 26KB with the block size being 64. // This is only 4.4% of that of full address table. static final int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64; static final String SHORTCUT_CONTENT_ID = "_shortcut"; @@ -361,13 +369,14 @@ public final class FormatSpec { * Class representing file header. */ public static final class FileHeader { - public final int mHeaderSize; + public final int mBodyOffset; public final DictionaryOptions mDictionaryOptions; public final FormatOptions mFormatOptions; // Note that these are corresponding definitions in native code in latinime::HeaderPolicy // and latinime::HeaderReadWriteUtils. public static final String SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE = "SUPPORTS_DYNAMIC_UPDATE"; public static final String USES_FORGETTING_CURVE_ATTRIBUTE = "USES_FORGETTING_CURVE"; + public static final String HAS_HISTORICAL_INFO_ATTRIBUTE = "HAS_HISTORICAL_INFO"; public static final String ATTRIBUTE_VALUE_TRUE = "1"; public static final String DICTIONARY_VERSION_ATTRIBUTE = "version"; @@ -376,9 +385,18 @@ public final class FormatSpec { private static final String DICTIONARY_DESCRIPTION_ATTRIBUTE = "description"; public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions, final FormatOptions formatOptions) { - mHeaderSize = headerSize; mDictionaryOptions = dictionaryOptions; mFormatOptions = formatOptions; + mBodyOffset = formatOptions.mVersion < VERSION4 ? headerSize : 0; + if (null == getLocaleString()) { + throw new RuntimeException("Cannot create a FileHeader without a locale"); + } + if (null == getVersion()) { + throw new RuntimeException("Cannot create a FileHeader without a version"); + } + if (null == getId()) { + throw new RuntimeException("Cannot create a FileHeader without an ID"); + } } // Helper method to get the locale as a String diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java new file mode 100644 index 000000000..06088b651 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; +import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * An auxiliary class for reading SparseTable and data written by SparseTableContentWriter. + */ +public class SparseTableContentReader { + + /** + * An interface of a function which is passed to SparseTableContentReader.read. + */ + public interface SparseTableContentReaderInterface { + /** + * Reads data. + * + * @param buffer the DictBuffer. The position of the buffer is set to the head of data. + */ + public void read(final DictBuffer buffer); + } + + protected final int mContentCount; + protected final int mBlockSize; + protected final File mBaseDir; + protected final File mLookupTableFile; + protected final File[] mAddressTableFiles; + protected final File[] mContentFiles; + protected DictBuffer mLookupTableBuffer; + protected final DictBuffer[] mAddressTableBuffers; + private final DictBuffer[] mContentBuffers; + protected final DictionaryBufferFactory mFactory; + + /** + * Sole constructor of SparseTableContentReader. + * + * @param name the name of SparseTable. + * @param blockSize the block size of the content table. + * @param baseDir the directory which contains the files of the content table. + * @param contentFilenames the file names of content files. + * @param contentIds the ids of contents. These ids are used for a suffix of a name of + * address files and content files. + * @param factory the DictionaryBufferFactory which is used for opening the files. + */ + public SparseTableContentReader(final String name, final int blockSize, final File baseDir, + final String[] contentFilenames, final String[] contentIds, + final DictionaryBufferFactory factory) { + if (contentFilenames.length != contentIds.length) { + throw new RuntimeException("The length of contentFilenames and the length of" + + " contentIds are different " + contentFilenames.length + ", " + + contentIds.length); + } + mBlockSize = blockSize; + mBaseDir = baseDir; + mFactory = factory; + mContentCount = contentFilenames.length; + mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); + mAddressTableFiles = new File[mContentCount]; + mContentFiles = new File[mContentCount]; + for (int i = 0; i < mContentCount; ++i) { + mAddressTableFiles[i] = new File(mBaseDir, + name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]); + mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]); + } + mAddressTableBuffers = new DictBuffer[mContentCount]; + mContentBuffers = new DictBuffer[mContentCount]; + } + + public void openBuffers() throws FileNotFoundException, IOException { + mLookupTableBuffer = mFactory.getDictionaryBuffer(mLookupTableFile); + for (int i = 0; i < mContentCount; ++i) { + mAddressTableBuffers[i] = mFactory.getDictionaryBuffer(mAddressTableFiles[i]); + mContentBuffers[i] = mFactory.getDictionaryBuffer(mContentFiles[i]); + } + } + + protected void read(final int contentIndex, final int index, + final SparseTableContentReaderInterface reader) { + if (index < 0 || (index / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES + >= mLookupTableBuffer.limit()) { + return; + } + + mLookupTableBuffer.position((index / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES); + final int posInAddressTable = mLookupTableBuffer.readInt(); + if (posInAddressTable == SparseTable.NOT_EXIST) { + return; + } + + mAddressTableBuffers[contentIndex].position( + (posInAddressTable + index % mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES); + final int address = mAddressTableBuffers[contentIndex].readInt(); + if (address == SparseTable.NOT_EXIST) { + return; + } + + mContentBuffers[contentIndex].position(address); + reader.read(mContentBuffers[contentIndex]); + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java new file mode 100644 index 000000000..4518f21b9 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentUpdater.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An auxiliary class for updating data associated with SparseTable. + */ +public class SparseTableContentUpdater extends SparseTableContentReader { + protected OutputStream mLookupTableOutStream; + protected OutputStream[] mAddressTableOutStreams; + protected OutputStream[] mContentOutStreams; + + public SparseTableContentUpdater(final String name, final int blockSize, + final File baseDir, final String[] contentFilenames, final String[] contentIds, + final DictionaryBufferFactory factory) { + super(name, blockSize, baseDir, contentFilenames, contentIds, factory); + mAddressTableOutStreams = new OutputStream[mContentCount]; + mContentOutStreams = new OutputStream[mContentCount]; + } + + protected void openStreamsAndBuffers() throws IOException { + openBuffers(); + mLookupTableOutStream = new FileOutputStream(mLookupTableFile, true /* append */); + for (int i = 0; i < mContentCount; ++i) { + mAddressTableOutStreams[i] = new FileOutputStream(mAddressTableFiles[i], + true /* append */); + mContentOutStreams[i] = new FileOutputStream(mContentFiles[i], true /* append */); + } + } + + /** + * Set the contentIndex-th elements of contentId-th table. + * + * @param contentId the id of the content table. + * @param contentIndex the index where to set the valie. + * @param value the value to set. + */ + protected void setContentValue(final int contentId, final int contentIndex, final int value) + throws IOException { + if ((contentIndex / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES + >= mLookupTableBuffer.limit()) { + // Need to extend the lookup table + final int currentSize = mLookupTableBuffer.limit() + / SparseTable.SIZE_OF_INT_IN_BYTES; + final int target = contentIndex / mBlockSize + 1; + for (int i = currentSize; i < target; ++i) { + BinaryDictEncoderUtils.writeUIntToStream(mLookupTableOutStream, + SparseTable.NOT_EXIST, SparseTable.SIZE_OF_INT_IN_BYTES); + } + // We need to reopen the byte buffer of the lookup table because a MappedByteBuffer in + // Java isn't expanded automatically when the underlying file is expanded. + reopenLookupTable(); + } + + mLookupTableBuffer.position((contentIndex / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES); + int posInAddressTable = mLookupTableBuffer.readInt(); + if (posInAddressTable == SparseTable.NOT_EXIST) { + // Need to extend the address table + mLookupTableBuffer.position(mLookupTableBuffer.position() + - SparseTable.SIZE_OF_INT_IN_BYTES); + posInAddressTable = mAddressTableBuffers[0].limit() / mBlockSize; + BinaryDictEncoderUtils.writeUIntToDictBuffer(mLookupTableBuffer, + posInAddressTable, SparseTable.SIZE_OF_INT_IN_BYTES); + for (int i = 0; i < mContentCount; ++i) { + for (int j = 0; j < mBlockSize; ++j) { + BinaryDictEncoderUtils.writeUIntToStream(mAddressTableOutStreams[i], + SparseTable.NOT_EXIST, SparseTable.SIZE_OF_INT_IN_BYTES); + } + } + // We need to reopen the byte buffers of the address tables because a MappedByteBuffer + // in Java isn't expanded automatically when the underlying file is expanded. + reopenAddressTables(); + } + posInAddressTable += (contentIndex % mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES; + + mAddressTableBuffers[contentId].position(posInAddressTable); + BinaryDictEncoderUtils.writeUIntToDictBuffer(mAddressTableBuffers[contentId], + value, SparseTable.SIZE_OF_INT_IN_BYTES); + } + + private void reopenLookupTable() throws IOException { + mLookupTableOutStream.flush(); + mLookupTableBuffer = mFactory.getDictionaryBuffer(mLookupTableFile); + } + + private void reopenAddressTables() throws IOException { + for (int i = 0; i < mContentCount; ++i) { + mAddressTableOutStreams[i].flush(); + mAddressTableBuffers[i] = mFactory.getDictionaryBuffer(mAddressTableFiles[i]); + } + } + + protected void close() throws IOException { + mLookupTableOutStream.close(); + for (final OutputStream stream : mAddressTableOutStreams) { + stream.close(); + } + for (final OutputStream stream : mContentOutStreams) { + stream.close(); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java new file mode 100644 index 000000000..49f0fd624 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * An auxiliary class for writing data associated with SparseTable to files. + */ +public class SparseTableContentWriter { + public interface SparseTableContentWriterInterface { + public void write(final OutputStream outStream) throws IOException; + } + + private final int mContentCount; + private final SparseTable mSparseTable; + private final File mLookupTableFile; + protected final File mBaseDir; + private final File[] mAddressTableFiles; + private final File[] mContentFiles; + protected final OutputStream[] mContentOutStreams; + + /** + * Sole constructor of SparseTableContentWriter. + * + * @param name the name of SparseTable. + * @param initialCapacity the initial capacity of SparseTable. + * @param blockSize the block size of the content table. + * @param baseDir the directory which contains the files of the content table. + * @param contentFilenames the file names of content files. + * @param contentIds the ids of contents. These ids are used for a suffix of a name of address + * files and content files. + */ + public SparseTableContentWriter(final String name, final int initialCapacity, + final int blockSize, final File baseDir, final String[] contentFilenames, + final String[] contentIds) { + if (contentFilenames.length != contentIds.length) { + throw new RuntimeException("The length of contentFilenames and the length of" + + " contentIds are different " + contentFilenames.length + ", " + + contentIds.length); + } + mContentCount = contentFilenames.length; + mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount); + mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); + mAddressTableFiles = new File[mContentCount]; + mContentFiles = new File[mContentCount]; + mBaseDir = baseDir; + for (int i = 0; i < mContentCount; ++i) { + mAddressTableFiles[i] = new File(mBaseDir, + name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]); + mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]); + } + mContentOutStreams = new OutputStream[mContentCount]; + } + + public void openStreams() throws FileNotFoundException { + for (int i = 0; i < mContentCount; ++i) { + mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]); + } + } + + protected void write(final int contentIndex, final int index, + final SparseTableContentWriterInterface writer) throws IOException { + mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length()); + writer.write(mContentOutStreams[contentIndex]); + mContentOutStreams[contentIndex].flush(); + } + + public void closeStreams() throws IOException { + mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles); + for (int i = 0; i < mContentCount; ++i) { + mContentOutStreams[i].close(); + } + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 734223ec2..07522b54b 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -40,26 +40,52 @@ import java.util.Arrays; public class Ver4DictDecoder extends AbstractDictDecoder { private static final String TAG = Ver4DictDecoder.class.getSimpleName(); - private static final int FILETYPE_TRIE = 1; - private static final int FILETYPE_FREQUENCY = 2; - private static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3; - private static final int FILETYPE_BIGRAM_FREQ = 4; - private static final int FILETYPE_SHORTCUT = 5; - - private final File mDictDirectory; - private final DictionaryBufferFactory mBufferFactory; + protected static final int FILETYPE_TRIE = 1; + protected static final int FILETYPE_FREQUENCY = 2; + protected static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3; + protected static final int FILETYPE_BIGRAM_FREQ = 4; + protected static final int FILETYPE_SHORTCUT = 5; + protected static final int FILETYPE_HEADER = 6; + + protected final File mDictDirectory; + protected final DictionaryBufferFactory mBufferFactory; protected DictBuffer mDictBuffer; - private DictBuffer mFrequencyBuffer; - private DictBuffer mTerminalAddressTableBuffer; - private DictBuffer mBigramBuffer; - private DictBuffer mShortcutBuffer; - private SparseTable mBigramAddressTable; - private SparseTable mShortcutAddressTable; + protected DictBuffer mHeaderBuffer; + protected DictBuffer mFrequencyBuffer; + protected DictBuffer mTerminalAddressTableBuffer; + private BigramContentReader mBigramReader; + private ShortcutContentReader mShortcutReader; + + /** + * Raw PtNode info straight out of a trie file in version 4 dictionary. + */ + protected static final class Ver4PtNodeInfo { + public final int mFlags; + public final int[] mCharacters; + public final int mTerminalId; + public final int mChildrenPos; + public final int mParentPos; + public final int mNodeSize; + public int mStartIndexOfCharacters; + public int mEndIndexOfCharacters; // exclusive + + public Ver4PtNodeInfo(final int flags, final int[] characters, final int terminalId, + final int childrenPos, final int parentPos, final int nodeSize) { + mFlags = flags; + mCharacters = characters; + mTerminalId = terminalId; + mChildrenPos = childrenPos; + mParentPos = parentPos; + mNodeSize = nodeSize; + mStartIndexOfCharacters = 0; + mEndIndexOfCharacters = characters.length; + } + } @UsedForTesting /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { mDictDirectory = dictDirectory; - mDictBuffer = mFrequencyBuffer = null; + mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null; if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) { mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); @@ -76,13 +102,16 @@ public class Ver4DictDecoder extends AbstractDictDecoder { /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { mDictDirectory = dictDirectory; mBufferFactory = factory; - mDictBuffer = mFrequencyBuffer = null; + mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null; } - private File getFile(final int fileType) { + protected File getFile(final int fileType) throws UnsupportedFormatException { if (fileType == FILETYPE_TRIE) { return new File(mDictDirectory, mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION); + } else if (fileType == FILETYPE_HEADER) { + return new File(mDictDirectory, + mDictDirectory.getName() + FormatSpec.HEADER_FILE_EXTENSION); } else if (fileType == FILETYPE_FREQUENCY) { return new File(mDictDirectory, mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION); @@ -98,20 +127,27 @@ public class Ver4DictDecoder extends AbstractDictDecoder { mDictDirectory.getName() + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.SHORTCUT_CONTENT_ID); } else { - throw new RuntimeException("Unsupported kind of file : " + fileType); + throw new UnsupportedFormatException("Unsupported kind of file : " + fileType); } } @Override - public void openDictBuffer() throws FileNotFoundException, IOException { + public void openDictBuffer() throws FileNotFoundException, IOException, + UnsupportedFormatException { + if (!mDictDirectory.isDirectory()) { + throw new UnsupportedFormatException("Format 4 dictionary needs a directory"); + } + mHeaderBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_HEADER)); mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE)); mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY)); mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer( getFile(FILETYPE_TERMINAL_ADDRESS_TABLE)); - mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM_FREQ)); - loadBigramAddressSparseTable(); - mShortcutBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_SHORTCUT)); - loadShortcutAddressSparseTable(); + mBigramReader = new BigramContentReader(mDictDirectory.getName(), + mDictDirectory, mBufferFactory, false); + mBigramReader.openBuffers(); + mShortcutReader = new ShortcutContentReader(mDictDirectory.getName(), mDictDirectory, + mBufferFactory); + mShortcutReader.openBuffers(); } @Override @@ -119,46 +155,137 @@ public class Ver4DictDecoder extends AbstractDictDecoder { return mDictBuffer != null; } + @UsedForTesting + /* package */ DictBuffer getHeaderBuffer() { + return mHeaderBuffer; + } + + @UsedForTesting /* package */ DictBuffer getDictBuffer() { return mDictBuffer; } @Override public FileHeader readHeader() throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) { + if (mHeaderBuffer == null) { openDictBuffer(); } - final FileHeader header = super.readHeader(mDictBuffer); + mHeaderBuffer.position(0); + final FileHeader header = super.readHeader(mHeaderBuffer); final int version = header.mFormatOptions.mVersion; - if (version != 4) { + if (version != FormatSpec.VERSION4) { throw new UnsupportedFormatException("File header has a wrong version : " + version); } return header; } - private void loadBigramAddressSparseTable() throws IOException { - final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName() - + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); - final File freqsFile = new File(mDictDirectory, mDictDirectory.getName() - + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX - + FormatSpec.BIGRAM_FREQ_CONTENT_ID); - mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, new File[] { freqsFile }, - FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE); + /** + * An auxiliary class for reading bigrams. + */ + protected static class BigramContentReader extends SparseTableContentReader { + private final boolean mHasTimestamp; + + public BigramContentReader(final String name, final File baseDir, + final DictionaryBufferFactory factory, final boolean hasTimestamp) { + super(name + FormatSpec.BIGRAM_FILE_EXTENSION, + FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir, + getContentFilenames(name, hasTimestamp), getContentIds(hasTimestamp), factory); + mHasTimestamp = hasTimestamp; + } + + // TODO: Consolidate this method and BigramContentWriter.getContentFilenames. + protected static String[] getContentFilenames(final String name, + final boolean hasTimestamp) { + final String[] contentFilenames; + if (hasTimestamp) { + contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION, + name + FormatSpec.BIGRAM_FILE_EXTENSION }; + } else { + contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION }; + } + return contentFilenames; + } + + // TODO: Consolidate this method and BigramContentWriter.getContentIds. + protected static String[] getContentIds(final boolean hasTimestamp) { + final String[] contentIds; + if (hasTimestamp) { + contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID, + FormatSpec.BIGRAM_TIMESTAMP_CONTENT_ID }; + } else { + contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID }; + } + return contentIds; + } + + public ArrayList<PendingAttribute> readTargetsAndFrequencies(final int terminalId, + final DictBuffer terminalAddressTableBuffer) { + final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList(); + read(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, + new SparseTableContentReaderInterface() { + @Override + public void read(final DictBuffer buffer) { + while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE, + // remaining bigram entries are ignored. + final int bigramFlags = buffer.readUnsignedByte(); + final int targetTerminalId = buffer.readUnsignedInt24(); + terminalAddressTableBuffer.position(targetTerminalId + * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); + final int targetAddress = + terminalAddressTableBuffer.readUnsignedInt24(); + bigrams.add(new PendingAttribute(bigramFlags + & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, + targetAddress)); + if (0 == (bigramFlags + & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) { + break; + } + } + if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + throw new RuntimeException("Too many bigrams in a PtNode (" + + bigrams.size() + " but max is " + + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); + } + } + }); + if (bigrams.isEmpty()) return null; + return bigrams; + } } - // TODO: Let's have something like SparseTableContentsReader in this class. - private void loadShortcutAddressSparseTable() throws IOException { - final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName() - + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); - final File contentFile = new File(mDictDirectory, mDictDirectory.getName() - + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX - + FormatSpec.SHORTCUT_CONTENT_ID); - final File timestampsFile = new File(mDictDirectory, mDictDirectory.getName() - + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX - + FormatSpec.SHORTCUT_CONTENT_ID); - mShortcutAddressTable = SparseTable.readFromFiles(lookupIndexFile, - new File[] { contentFile, timestampsFile }, - FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE); + /** + * An auxiliary class for reading shortcuts. + */ + protected static class ShortcutContentReader extends SparseTableContentReader { + public ShortcutContentReader(final String name, final File baseDir, + final DictionaryBufferFactory factory) { + super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, + FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir, + new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION }, + new String[] { FormatSpec.SHORTCUT_CONTENT_ID }, factory); + } + + public ArrayList<WeightedString> readShortcuts(final int terminalId) { + final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList(); + read(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId, + new SparseTableContentReaderInterface() { + @Override + public void read(final DictBuffer buffer) { + while (true) { + final int flags = buffer.readUnsignedByte(); + final String word = CharEncoding.readString(buffer); + shortcuts.add(new WeightedString(word, + flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); + if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) { + break; + } + } + } + }); + if (shortcuts.isEmpty()) return null; + return shortcuts; + } } protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader { @@ -172,102 +299,82 @@ public class Ver4DictDecoder extends AbstractDictDecoder { } } - private ArrayList<WeightedString> readShortcuts(final int terminalId) { - if (mShortcutAddressTable.get(0, terminalId) == SparseTable.NOT_EXIST) return null; - - final ArrayList<WeightedString> ret = CollectionUtils.newArrayList(); - final int posOfShortcuts = mShortcutAddressTable.get(FormatSpec.SHORTCUT_CONTENT_INDEX, - terminalId); - mShortcutBuffer.position(posOfShortcuts); - while (true) { - final int flags = mShortcutBuffer.readUnsignedByte(); - final String word = CharEncoding.readString(mShortcutBuffer); - ret.add(new WeightedString(word, - flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return ret; - } + private final int[] mCharacterBufferForReadingVer4PtNodeInfo + = new int[FormatSpec.MAX_WORD_LENGTH]; + /** + * Reads PtNode from ptNodePos in the trie file and returns Ver4PtNodeInfo. + * + * @param ptNodePos the position of PtNode. + * @param options the format options. + * @return Ver4PtNodeInfo. + */ // 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; + protected Ver4PtNodeInfo readVer4PtNodeInfo(final int ptNodePos, final FormatOptions options) { + int readingPos = ptNodePos; final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; + readingPos += FormatSpec.PTNODE_FLAGS_SIZE; - final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); + final int parentPos = PtNodeReader.readParentAddress(mDictBuffer, options); if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { - addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; + readingPos += 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); + readingPos += CharEncoding.getCharSize(character); while (FormatSpec.INVALID_CHARACTER != character && index < FormatSpec.MAX_WORD_LENGTH) { - mCharacterBuffer[index++] = character; + mCharacterBufferForReadingVer4PtNodeInfo[index++] = character; character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); + readingPos += CharEncoding.getCharSize(character); } - characters = Arrays.copyOfRange(mCharacterBuffer, 0, index); + characters = Arrays.copyOfRange(mCharacterBufferForReadingVer4PtNodeInfo, 0, index); } else { final int character = CharEncoding.readChar(mDictBuffer); - addressPointer += CharEncoding.getCharSize(character); + readingPos += 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; + readingPos += FormatSpec.PTNODE_TERMINAL_ID_SIZE; } else { terminalId = PtNode.NOT_A_TERMINAL; } + int childrenPos = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options); + if (childrenPos != FormatSpec.NO_CHILDREN_ADDRESS) { + childrenPos += readingPos; + } + readingPos += BinaryDictIOUtils.getChildrenAddressSize(flags, options); + + return new Ver4PtNodeInfo(flags, characters, terminalId, childrenPos, parentPos, + readingPos - ptNodePos); + } + + @Override + public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) { + final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(ptNodePos, options); + final int frequency; - if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - frequency = PtNodeReader.readFrequency(mFrequencyBuffer, terminalId); + if (0 != (FormatSpec.FLAG_IS_TERMINAL & nodeInfo.mFlags)) { + frequency = PtNodeReader.readFrequency(mFrequencyBuffer, nodeInfo.mTerminalId); } 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 = readShortcuts(terminalId); - - final ArrayList<PendingAttribute> bigrams; - if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) { - bigrams = new ArrayList<PendingAttribute>(); - final int posOfBigrams = mBigramAddressTable.get(0 /* contentTableIndex */, terminalId); - mBigramBuffer.position(posOfBigrams); - while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE, - // remaining bigram entries are ignored. - final int bigramFlags = mBigramBuffer.readUnsignedByte(); - final int targetTerminalId = mBigramBuffer.readUnsignedInt24(); - mTerminalAddressTableBuffer.position( - targetTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); - final int targetAddress = mTerminalAddressTableBuffer.readUnsignedInt24(); - bigrams.add(new PendingAttribute( - bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - targetAddress)); - if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size() - + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); - } - } else { - bigrams = null; - } - return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency, - parentAddress, childrenAddress, shortcutTargets, bigrams); + + final ArrayList<WeightedString> shortcutTargets = mShortcutReader.readShortcuts( + nodeInfo.mTerminalId); + final ArrayList<PendingAttribute> bigrams = mBigramReader.readTargetsAndFrequencies( + nodeInfo.mTerminalId, mTerminalAddressTableBuffer); + + return new PtNodeInfo(ptNodePos, ptNodePos + nodeInfo.mNodeSize, nodeInfo.mFlags, + nodeInfo.mCharacters, frequency, nodeInfo.mParentPos, nodeInfo.mChildrenPos, + shortcutTargets, bigrams); } private void deleteDictFiles() { @@ -318,10 +425,14 @@ public class Ver4DictDecoder extends AbstractDictDecoder { @Override public boolean readAndFollowForwardLink() { - final int nextAddress = mDictBuffer.readUnsignedInt24(); - if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) { - mDictBuffer.position(nextAddress); - return true; + final int forwardLinkPos = mDictBuffer.position(); + int nextRelativePos = BinaryDictDecoderUtils.readSInt24(mDictBuffer); + if (nextRelativePos != FormatSpec.NO_FORWARD_LINK_ADDRESS) { + final int nextPos = forwardLinkPos + nextRelativePos; + if (nextPos >= 0 && nextPos < mDictBuffer.limit()) { + mDictBuffer.position(nextPos); + return true; + } } return false; } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index 8d5b48a9b..1a245b6db 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -25,6 +25,8 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.FileNotFoundException; @@ -32,6 +34,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; /** @@ -42,8 +46,8 @@ public class Ver4DictEncoder implements DictEncoder { private final File mDictPlacedDir; private byte[] mTrieBuf; private int mTriePos; - private int mHeaderSize; private OutputStream mTrieOutStream; + private OutputStream mHeaderOutStream; private OutputStream mFreqOutStream; private OutputStream mUnigramTimestampOutStream; private OutputStream mTerminalAddressTableOutStream; @@ -57,62 +61,6 @@ public class Ver4DictEncoder implements DictEncoder { mDictPlacedDir = dictPlacedDir; } - private interface SparseTableContentWriterInterface { - public void write(final OutputStream outStream) throws IOException; - } - - private static class SparseTableContentWriter { - private final int mContentCount; - private final SparseTable mSparseTable; - private final File mLookupTableFile; - protected final File mBaseDir; - private final File[] mAddressTableFiles; - private final File[] mContentFiles; - protected final OutputStream[] mContentOutStreams; - - public SparseTableContentWriter(final String name, final int initialCapacity, - final int blockSize, final File baseDir, final String[] contentFilenames, - final String[] contentIds) { - if (contentFilenames.length != contentIds.length) { - throw new RuntimeException("The length of contentFilenames and the length of" - + " contentIds are different " + contentFilenames.length + ", " - + contentIds.length); - } - mContentCount = contentFilenames.length; - mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount); - mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); - mAddressTableFiles = new File[mContentCount]; - mContentFiles = new File[mContentCount]; - mBaseDir = baseDir; - for (int i = 0; i < mContentCount; ++i) { - mAddressTableFiles[i] = new File(mBaseDir, - name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]); - mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]); - } - mContentOutStreams = new OutputStream[mContentCount]; - } - - public void openStreams() throws FileNotFoundException { - for (int i = 0; i < mContentCount; ++i) { - mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]); - } - } - - protected void write(final int contentIndex, final int index, - final SparseTableContentWriterInterface writer) throws IOException { - mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length()); - writer.write(mContentOutStreams[contentIndex]); - mContentOutStreams[contentIndex].flush(); - } - - public void closeStreams() throws IOException { - mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles); - for (int i = 0; i < mContentCount; ++i) { - mContentOutStreams[i].close(); - } - } - } - private static class BigramContentWriter extends SparseTableContentWriter { private final boolean mWriteTimestamp; @@ -238,16 +186,21 @@ public class Ver4DictEncoder implements DictEncoder { mBaseFilename = header.getId() + "." + header.getVersion(); mDictDir = new File(mDictPlacedDir, mBaseFilename); final File trieFile = new File(mDictDir, mBaseFilename + FormatSpec.TRIE_FILE_EXTENSION); + final File headerFile = new File(mDictDir, + mBaseFilename + FormatSpec.HEADER_FILE_EXTENSION); final File freqFile = new File(mDictDir, mBaseFilename + FormatSpec.FREQ_FILE_EXTENSION); final File timestampFile = new File(mDictDir, mBaseFilename + FormatSpec.UNIGRAM_TIMESTAMP_FILE_EXTENSION); final File terminalAddressTableFile = new File(mDictDir, mBaseFilename + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); if (!mDictDir.isDirectory()) { - if (mDictDir.exists()) mDictDir.delete(); + if (mDictDir.exists()) { + FileUtils.deleteRecursively(mDictDir); + } mDictDir.mkdirs(); } mTrieOutStream = new FileOutputStream(trieFile); + mHeaderOutStream = new FileOutputStream(headerFile); mFreqOutStream = new FileOutputStream(freqFile); mTerminalAddressTableOutStream = new FileOutputStream(terminalAddressTableFile); if (formatOptions.mHasTimestamp) { @@ -260,6 +213,9 @@ public class Ver4DictEncoder implements DictEncoder { if (mTrieOutStream != null) { mTrieOutStream.close(); } + if (mHeaderOutStream != null) { + mHeaderOutStream.close(); + } if (mFreqOutStream != null) { mFreqOutStream.close(); } @@ -271,6 +227,7 @@ public class Ver4DictEncoder implements DictEncoder { } } finally { mTrieOutStream = null; + mHeaderOutStream = null; mFreqOutStream = null; mTerminalAddressTableOutStream = null; } @@ -291,16 +248,34 @@ public class Ver4DictEncoder implements DictEncoder { openStreams(formatOptions, dict.mOptions); } - mHeaderSize = BinaryDictEncoderUtils.writeDictionaryHeader(mTrieOutStream, dict, - formatOptions); + BinaryDictEncoderUtils.writeDictionaryHeader(mHeaderOutStream, dict, formatOptions); MakedictLog.i("Flattening the tree..."); ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray); int terminalCount = 0; + final ArrayList<PtNode> nodes = CollectionUtils.newArrayList(); for (final PtNodeArray array : flatNodes) { for (final PtNode node : array.mData) { - if (node.isTerminal()) node.mTerminalId = terminalCount++; + if (node.isTerminal()) { + nodes.add(node); + node.mTerminalId = terminalCount++; + } + } + } + Collections.sort(nodes, new Comparator<PtNode>() { + @Override + public int compare(final PtNode lhs, final PtNode rhs) { + if (lhs.mFrequency != rhs.mFrequency) { + return lhs.mFrequency < rhs.mFrequency ? -1 : 1; + } + if (lhs.mTerminalId < rhs.mTerminalId) return -1; + if (lhs.mTerminalId > rhs.mTerminalId) return 1; + return 0; } + }); + int count = 0; + for (final PtNode node : nodes) { + node.mTerminalId = count++; } MakedictLog.i("Computing addresses..."); @@ -337,7 +312,7 @@ public class Ver4DictEncoder implements DictEncoder { @Override public void setPosition(int position) { - if (mTrieBuf == null || position < 0 || position >- mTrieBuf.length) return; + if (mTrieBuf == null || position < 0 || position > mTrieBuf.length) return; mTriePos = position; } @@ -457,7 +432,7 @@ public class Ver4DictEncoder implements DictEncoder { ptNode.mFrequency, FormatSpec.FREQUENCY_AND_FLAGS_SIZE); BinaryDictEncoderUtils.writeUIntToBuffer(terminalAddressTableBuf, ptNode.mTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, - ptNode.mCachedAddressAfterUpdate + mHeaderSize, + ptNode.mCachedAddressAfterUpdate, FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java index 3d8f186ba..91d9cf345 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java @@ -17,29 +17,133 @@ 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.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.CollectionUtils; + +import android.util.Log; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; /** * An implementation of DictUpdater for version 4 binary dictionary. */ @UsedForTesting public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater { + private static final String TAG = Ver4DictUpdater.class.getSimpleName(); + + private OutputStream mDictStream; + private final File mFrequencyFile; @UsedForTesting - public Ver4DictUpdater(final File dictDirectory, final int factoryType) { + public Ver4DictUpdater(final File dictDirectory, final int factoryType) + throws UnsupportedFormatException { // DictUpdater must have an updatable DictBuffer. super(dictDirectory, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY) ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER); + mFrequencyFile = getFile(FILETYPE_FREQUENCY); + } + + private static class BigramContentUpdater extends SparseTableContentUpdater { + private final boolean mHasTimestamp; + + public BigramContentUpdater(final String name, final File baseDir, + final boolean hasTimestamp) { + super(name + FormatSpec.BIGRAM_FILE_EXTENSION, + FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir, + BigramContentReader.getContentFilenames(name, hasTimestamp), + BigramContentReader.getContentIds(hasTimestamp), + new DictionaryBufferFromWritableByteBufferFactory()); + mHasTimestamp = hasTimestamp; + } + + public void insertBigramEntries(final int terminalId, final int frequency, + final ArrayList<PendingAttribute> entries) throws IOException { + if (terminalId < 0) { + throw new RuntimeException("Invalid terminal id : " + terminalId); + } + openStreamsAndBuffers(); + + if (entries == null || entries.isEmpty()) { + setContentValue(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, + SparseTable.NOT_EXIST); + return; + } + final int positionOfEntries = + (int) mContentFiles[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX].length(); + setContentValue(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, positionOfEntries); + + final Iterator<PendingAttribute> bigramIterator = entries.iterator(); + while (bigramIterator.hasNext()) { + final PendingAttribute entry = bigramIterator.next(); + final int flags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(), + 0 /* offset */, entry.mFrequency, frequency, "" /* word */); + BinaryDictEncoderUtils.writeUIntToStream( + mContentOutStreams[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX], flags, + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); + BinaryDictEncoderUtils.writeUIntToStream( + mContentOutStreams[FormatSpec.BIGRAM_FREQ_CONTENT_INDEX], entry.mAddress, + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE); + } + close(); + } + } + + private static class ShortcutContentUpdater extends SparseTableContentUpdater { + public ShortcutContentUpdater(final String name, final File baseDir) { + super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, + FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir, + new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION }, + new String[] { FormatSpec.SHORTCUT_CONTENT_ID }, + new DictionaryBufferFromWritableByteBufferFactory()); + } + + public void insertShortcuts(final int terminalId, + final ArrayList<WeightedString> shortcuts) throws IOException { + if (terminalId < 0) { + throw new RuntimeException("Invalid terminal id : " + terminalId); + } + openStreamsAndBuffers(); + if (shortcuts == null || shortcuts.isEmpty()) { + setContentValue(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId, + SparseTable.NOT_EXIST); + return; + } + + final int positionOfShortcuts = + (int) mContentFiles[FormatSpec.SHORTCUT_CONTENT_INDEX].length(); + setContentValue(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId, positionOfShortcuts); + + final Iterator<WeightedString> shortcutIterator = shortcuts.iterator(); + while (shortcutIterator.hasNext()) { + final WeightedString target = shortcutIterator.next(); + final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags( + shortcutIterator.hasNext(), target.mFrequency); + BinaryDictEncoderUtils.writeUIntToStream( + mContentOutStreams[FormatSpec.SHORTCUT_CONTENT_INDEX], shortcutFlags, + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); + CharEncoding.writeString(mContentOutStreams[FormatSpec.SHORTCUT_CONTENT_INDEX], + target.mWord); + } + close(); + } } @Override public void deleteWord(final String word) throws IOException, UnsupportedFormatException { - if (mDictBuffer == null) openDictBuffer(); - readHeader(); + if (mDictBuffer == null) { + openDictBuffer(); + readHeader(); + } final int wordPos = getTerminalPosition(word); if (wordPos != FormatSpec.NOT_VALID_WORD) { mDictBuffer.position(wordPos); @@ -49,11 +153,623 @@ public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater { } } - @Override + private int getNewTerminalId() { + // The size of frequency file is FormatSpec.FREQUENCY_AND_FLAGS_SIZE * number of terminals + // because each terminal always has a frequency. + // So we can get a fresh terminal id by this logic. + // CAVEAT: we are reading the file size from the disk each time: beware of race conditions, + // even on one thread. + return (int) (mFrequencyFile.length() / FormatSpec.FREQUENCY_AND_FLAGS_SIZE); + } + + private void updateParentPosIfNotMoved(final int nodePos, final int newParentPos, + final FormatOptions formatOptions) { + final int originalPos = getPosition(); + setPosition(nodePos); + final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); + if (!BinaryDictIOUtils.isMovedPtNode(flags, formatOptions)) { + final int parentOffset = newParentPos - nodePos; + BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, parentOffset); + } + setPosition(originalPos); + } + + private void updateParentPositions(final int nodeArrayPos, final int newParentPos, + final FormatOptions formatOptions) { + final int originalPos = mDictBuffer.position(); + mDictBuffer.position(nodeArrayPos); + int jumpCount = 0; + do { + final int count = readPtNodeCount(); + for (int i = 0; i < count; ++i) { + updateParentPosIfNotMoved(getPosition(), newParentPos, formatOptions); + skipPtNode(formatOptions); + } + if (!readAndFollowForwardLink()) break; + } while (jumpCount++ < DynamicBinaryDictIOUtils.MAX_JUMPS); + setPosition(originalPos); + } + + private void updateChildrenPos(final int nodePos, final int newChildrenPos, + final FormatOptions options) { + final int originalPos = getPosition(); + setPosition(nodePos); + final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); + PtNodeReader.readParentAddress(mDictBuffer, options); + BinaryDictIOUtils.skipString(mDictBuffer, + (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); + if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readTerminalId(mDictBuffer); + final int basePos = getPosition(); + BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, newChildrenPos - basePos); + setPosition(originalPos); + } + + private void updateTerminalPosition(final int terminalId, final int position) { + if (terminalId == PtNode.NOT_A_TERMINAL + || terminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE + >= mTerminalAddressTableBuffer.limit()) return; + mTerminalAddressTableBuffer.position(terminalId + * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); + BinaryDictEncoderUtils.writeUIntToDictBuffer(mTerminalAddressTableBuffer, position, + FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); + } + + private void updateForwardLink(final int nodeArrayPos, final int newForwardLink, + final FormatOptions formatOptions) { + final int originalPos = getPosition(); + setPosition(nodeArrayPos); + int jumpCount = 0; + while (jumpCount++ < DynamicBinaryDictIOUtils.MAX_JUMPS) { + final int ptNodeCount = readPtNodeCount(); + for (int i = 0; i < ptNodeCount; ++i) { + skipPtNode(formatOptions); + } + final int forwardLinkPos = getPosition(); + if (!readAndFollowForwardLink()) { + setPosition(forwardLinkPos); + BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, newForwardLink - forwardLinkPos); + break; + } + } + setPosition(originalPos); + } + + private void markPtNodeAsMoved(final int nodePos, final int newNodePos, + final FormatOptions options) { + final int originalPos = getPosition(); + updateParentPosIfNotMoved(nodePos, newNodePos, options); + setPosition(nodePos); + final int currentFlags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); + setPosition(nodePos); + mDictBuffer.put((byte) (FormatSpec.FLAG_IS_MOVED + | (currentFlags & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG)))); + final int offset = newNodePos - nodePos; + BinaryDictIOUtils.writeSInt24ToBuffer(mDictBuffer, offset); + setPosition(originalPos); + } + + /** + * Writes a PtNode to an output stream from a Ver4PtNodeInfo. + * + * @param nodePos the position of the head of the PtNode. + * @param info the PtNode info to be written. + * @return the size written, in bytes. + */ + private int writePtNode(final int nodePos, final Ver4PtNodeInfo info) throws IOException { + int written = 0; + + // Write flags. + mDictStream.write((byte) (info.mFlags & 0xFF)); + written += FormatSpec.PTNODE_FLAGS_SIZE; + + // Write the parent position. + final int parentOffset = info.mParentPos == FormatSpec.NO_PARENT_ADDRESS ? + FormatSpec.NO_PARENT_ADDRESS : info.mParentPos - nodePos; + BinaryDictIOUtils.writeSInt24ToStream(mDictStream, parentOffset); + written += FormatSpec.PARENT_ADDRESS_SIZE; + + // Write a string. + if (((info.mFlags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0) + != (info.mEndIndexOfCharacters - info.mStartIndexOfCharacters > 1)) { + throw new RuntimeException("Inconsistent flags : hasMultipleChars = " + + ((info.mFlags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0) + ", length = " + + (info.mEndIndexOfCharacters - info.mStartIndexOfCharacters)); + } + written += CharEncoding.writeCodePoints(mDictStream, info.mCharacters, + info.mStartIndexOfCharacters, info.mEndIndexOfCharacters); + + // Write the terminal id. + if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) { + BinaryDictEncoderUtils.writeUIntToStream(mDictStream, info.mTerminalId, + FormatSpec.PTNODE_TERMINAL_ID_SIZE); + written += FormatSpec.PTNODE_TERMINAL_ID_SIZE; + } + + // Write the children position. + final int childrenOffset = info.mChildrenPos == FormatSpec.NO_CHILDREN_ADDRESS + ? 0 : info.mChildrenPos - (nodePos + written); + BinaryDictIOUtils.writeSInt24ToStream(mDictStream, childrenOffset); + written += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + + return written; + } + + /** + * Helper method to split and move PtNode. + * + * @param ptNodeArrayPos the position of PtNodeArray which contains the split and moved PtNode. + * @param splittedPtNodeToMovePos the position of the split and moved PtNode. + * @param newParent the parent PtNode after splitting. + * @param newChildren the children PtNodes after splitting. + * @param newParentStartPos where to write the new parent. + * @param formatOptions the format options. + */ + private void writeSplittedPtNodes(final int ptNodeArrayPos, final int splittedPtNodeToMovePos, + final Ver4PtNodeInfo newParent, final Ver4PtNodeInfo[] newChildren, + final int newParentStartPos, + final FormatOptions formatOptions) throws IOException { + updateTerminalPosition(newParent.mTerminalId, + newParentStartPos + 1 /* size of PtNodeCount */); + int written = writePtNodeArray(newParentStartPos, new Ver4PtNodeInfo[] { newParent }, + FormatSpec.NO_FORWARD_LINK_ADDRESS); + final int childrenStartPos = newParentStartPos + written; + writePtNodeArray(childrenStartPos, newChildren, FormatSpec.NO_FORWARD_LINK_ADDRESS); + int childrenNodePos = childrenStartPos + 1 /* size of PtNodeCount */; + for (final Ver4PtNodeInfo info : newChildren) { + updateTerminalPosition(info.mTerminalId, childrenNodePos); + childrenNodePos += computePtNodeSize(info.mCharacters, info.mStartIndexOfCharacters, + info.mEndIndexOfCharacters, + (info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0); + } + + // Mark as moved. + markPtNodeAsMoved(splittedPtNodeToMovePos, newParentStartPos + 1 /* size of PtNodeCount */, + formatOptions); + updateForwardLink(ptNodeArrayPos, newParentStartPos, formatOptions); + } + + /** + * Writes a node array to the stream. + * + * @param nodeArrayPos the position of the head of the node array. + * @param infos an array of Ver4PtNodeInfo to be written. + * @return the written length in bytes. + */ + private int writePtNodeArray(final int nodeArrayPos, final Ver4PtNodeInfo[] infos, + final int forwardLink) throws IOException { + int written = BinaryDictIOUtils.writePtNodeCount(mDictStream, infos.length); + for (int i = 0; i < infos.length; ++i) { + written += writePtNode(nodeArrayPos + written, infos[i]); + } + BinaryDictIOUtils.writeSInt24ToStream(mDictStream, forwardLink); + written += FormatSpec.FORWARD_LINK_ADDRESS_SIZE; + return written; + } + + private int computePtNodeSize(final int[] codePoints, final int startIndex, final int endIndex, + final boolean isTerminal) { + return FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE + + CharEncoding.getCharArraySize(codePoints, startIndex, endIndex) + + (endIndex - startIndex > 1 ? FormatSpec.PTNODE_TERMINATOR_SIZE : 0) + + (isTerminal ? FormatSpec.PTNODE_TERMINAL_ID_SIZE : 0) + + FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE; + } + + private void writeNewSinglePtNodeWithAttributes(final int[] codePoints, + final boolean hasShortcuts, final int terminalId, final boolean hasBigrams, + final boolean isNotAWord, final boolean isBlackListEntry, final int parentPos, + final FormatOptions formatOptions) throws IOException { + final int newNodeArrayPos = mDictBuffer.limit(); + final int newNodeFlags = BinaryDictEncoderUtils.makePtNodeFlags(codePoints.length > 1, + terminalId != PtNode.NOT_A_TERMINAL, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts, + hasBigrams, isNotAWord, isBlackListEntry, formatOptions); + final Ver4PtNodeInfo info = new Ver4PtNodeInfo(newNodeFlags, codePoints, terminalId, + FormatSpec.NO_CHILDREN_ADDRESS, parentPos, 0 /* nodeSize */); + writePtNodeArray(newNodeArrayPos, new Ver4PtNodeInfo[] { info }, + FormatSpec.NO_FORWARD_LINK_ADDRESS); + } + + private int setMultipleCharsInFlags(final int currentFlags, final boolean hasMultipleChars) { + final int flags; + if (hasMultipleChars) { + flags = currentFlags | FormatSpec.FLAG_HAS_MULTIPLE_CHARS; + } else { + flags = currentFlags & (~FormatSpec.FLAG_HAS_MULTIPLE_CHARS); + } + return flags; + } + + private int setIsNotAWordInFlags(final int currentFlags, final boolean isNotAWord) { + final int flags; + if (isNotAWord) { + flags = currentFlags | FormatSpec.FLAG_IS_NOT_A_WORD; + } else { + flags = currentFlags & (~FormatSpec.FLAG_IS_NOT_A_WORD); + } + return flags; + } + + private int setIsBlackListEntryInFlags(final int currentFlags, final boolean isBlackListEntry) { + final int flags; + if (isBlackListEntry) { + flags = currentFlags | FormatSpec.FLAG_IS_BLACKLISTED; + } else { + flags = currentFlags & (~FormatSpec.FLAG_IS_BLACKLISTED); + } + return flags; + } + + /** + * Splits a PtNode. + * + * abcd - ef + * + * -> inserting "abc" + * + * abc - d - ef + * + * @param nodeArrayToSplitPos the position of PtNodeArray which contains the PtNode to split. + * @param nodeToSplitPos the position of the PtNode to split. + * @param nodeToSplitInfo the information of the PtNode to split. + * @param indexToSplit the index where to split in the code points array. + * @param parentOfNodeToSplitPos the absolute position of a parent of the node to split. + * @param newTerminalId the terminal id of the inserted node (corresponds to "d"). + * @param hasShortcuts whether the inserted word should have shortcuts. + * @param hasBigrams whether the inserted word should have bigrams. + * @param isNotAWord whether the inserted word should be not a word. + * @param isBlackListEntry whether the inserted word should be a black list entry. + * @param formatOptions the format options. + */ + private void splitOnly(final int nodeArrayToSplitPos, final int nodeToSplitPos, + final Ver4PtNodeInfo nodeToSplitInfo, final int indexToSplit, + final int parentOfNodeToSplitPos, final int newTerminalId, final boolean hasShortcuts, + final boolean hasBigrams, final boolean isNotAWord, final boolean isBlackListEntry, + final FormatOptions formatOptions) throws IOException { + final int parentNodeArrayStartPos = mDictBuffer.limit(); + final int parentNodeStartPos = parentNodeArrayStartPos + 1 /* size of PtNodeCount */; + final int parentFlags = BinaryDictEncoderUtils.makePtNodeFlags(indexToSplit > 1, + true /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, formatOptions); + final Ver4PtNodeInfo parentInfo = new Ver4PtNodeInfo(parentFlags, + nodeToSplitInfo.mCharacters, newTerminalId, parentNodeStartPos + + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, true) + + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, + parentOfNodeToSplitPos, 0 /* nodeSize */); + parentInfo.mStartIndexOfCharacters = 0; + parentInfo.mEndIndexOfCharacters = indexToSplit; + + // Write the child. + final int childrenFlags = setMultipleCharsInFlags(nodeToSplitInfo.mFlags, + nodeToSplitInfo.mCharacters.length - indexToSplit > 1); + final Ver4PtNodeInfo childrenInfo = new Ver4PtNodeInfo(childrenFlags, + nodeToSplitInfo.mCharacters, nodeToSplitInfo.mTerminalId, + nodeToSplitInfo.mChildrenPos, parentNodeStartPos, 0 /* nodeSize */); + childrenInfo.mStartIndexOfCharacters = indexToSplit; + childrenInfo.mEndIndexOfCharacters = nodeToSplitInfo.mCharacters.length; + if (nodeToSplitInfo.mChildrenPos != FormatSpec.NO_CHILDREN_ADDRESS) { + updateParentPositions(nodeToSplitInfo.mChildrenPos, + parentInfo.mChildrenPos + 1 /* size of PtNodeCount */, formatOptions); + } + + writeSplittedPtNodes(nodeArrayToSplitPos, nodeToSplitPos, parentInfo, + new Ver4PtNodeInfo[] { childrenInfo }, parentNodeArrayStartPos, formatOptions); + } + + /** + * Split and branch a PtNode. + * + * ab - cd + * + * -> inserting "ac" + * + * a - b - cd + * | + * - c + * + * @param nodeArrayToSplitPos the position of PtNodeArray which contains the PtNode to split. + * @param nodeToSplitPos the position of the PtNode to split. + * @param nodeToSplitInfo the information of the PtNode to split. + * @param indexToSplit the index where to split in the code points array. + * @param parentOfNodeToSplitPos the absolute position of parent of the node to split. + * @param newWordSuffixCodePoints the suffix of the newly inserted word (corresponds to "c"). + * @param startIndexOfNewWordSuffixCodePoints the start index in newWordSuffixCodePoints where + * the suffix starts. + * @param newTerminalId the terminal id of the inserted node (correspond to "c"). + * @param hasShortcuts whether the inserted word should have shortcuts. + * @param hasBigrams whether the inserted word should have bigrams. + * @param isNotAWord whether the inserted word should be not a word. + * @param isBlackListEntry whether the inserted word should be a black list entry. + * @param formatOptions the format options. + */ + private void splitAndBranch(final int nodeArrayToSplitPos, final int nodeToSplitPos, + final Ver4PtNodeInfo nodeToSplitInfo, final int indexToSplit, + final int parentOfNodeToSplitPos, final int[] newWordSuffixCodePoints, + final int startIndexOfNewWordSuffixCodePoints, + final int newTerminalId, + final boolean hasShortcuts, final boolean hasBigrams, final boolean isNotAWord, + final boolean isBlackListEntry, final FormatOptions formatOptions) throws IOException { + final int parentNodeArrayStartPos = mDictBuffer.limit(); + final int parentNodeStartPos = parentNodeArrayStartPos + 1 /* size of PtNodeCount */; + final int parentFlags = BinaryDictEncoderUtils.makePtNodeFlags( + indexToSplit > 1, + false /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED, + false /* hasShortcut */, false /* hasBigrams */, + false /* isNotAWord */, false /* isBlackListEntry */, formatOptions); + final Ver4PtNodeInfo parentInfo = new Ver4PtNodeInfo(parentFlags, + nodeToSplitInfo.mCharacters, PtNode.NOT_A_TERMINAL, + parentNodeStartPos + + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, false) + + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, + parentOfNodeToSplitPos, 0 /* nodeSize */); + parentInfo.mStartIndexOfCharacters = 0; + parentInfo.mEndIndexOfCharacters = indexToSplit; + + final int childrenNodeArrayStartPos = parentNodeStartPos + + computePtNodeSize(nodeToSplitInfo.mCharacters, 0, indexToSplit, false) + + FormatSpec.FORWARD_LINK_ADDRESS_SIZE; + final int firstChildrenFlags = BinaryDictEncoderUtils.makePtNodeFlags( + newWordSuffixCodePoints.length - startIndexOfNewWordSuffixCodePoints > 1, + true /* isTerminal */, FormatSpec.FLAG_IS_NOT_MOVED, hasShortcuts, hasBigrams, + isNotAWord, isBlackListEntry, formatOptions); + final Ver4PtNodeInfo firstChildrenInfo = new Ver4PtNodeInfo(firstChildrenFlags, + newWordSuffixCodePoints, newTerminalId, + FormatSpec.NO_CHILDREN_ADDRESS, parentNodeStartPos, + 0 /* nodeSize */); + firstChildrenInfo.mStartIndexOfCharacters = startIndexOfNewWordSuffixCodePoints; + firstChildrenInfo.mEndIndexOfCharacters = newWordSuffixCodePoints.length; + + final int secondChildrenStartPos = childrenNodeArrayStartPos + 1 /* size of ptNodeCount */ + + computePtNodeSize(newWordSuffixCodePoints, startIndexOfNewWordSuffixCodePoints, + newWordSuffixCodePoints.length, true /* isTerminal */); + final int secondChildrenFlags = setMultipleCharsInFlags(nodeToSplitInfo.mFlags, + nodeToSplitInfo.mCharacters.length - indexToSplit > 1); + final Ver4PtNodeInfo secondChildrenInfo = new Ver4PtNodeInfo(secondChildrenFlags, + nodeToSplitInfo.mCharacters, nodeToSplitInfo.mTerminalId, + nodeToSplitInfo.mChildrenPos, parentNodeStartPos, 0 /* nodeSize */); + secondChildrenInfo.mStartIndexOfCharacters = indexToSplit; + secondChildrenInfo.mEndIndexOfCharacters = nodeToSplitInfo.mCharacters.length; + if (nodeToSplitInfo.mChildrenPos != FormatSpec.NO_CHILDREN_ADDRESS) { + updateParentPositions(nodeToSplitInfo.mChildrenPos, secondChildrenStartPos, + formatOptions); + } + + writeSplittedPtNodes(nodeArrayToSplitPos, nodeToSplitPos, parentInfo, + new Ver4PtNodeInfo[] { firstChildrenInfo, secondChildrenInfo }, + parentNodeArrayStartPos, formatOptions); + } + + /** + * Inserts a word into the trie file and returns the position of inserted terminal node. + * If the insertion is failed, returns FormatSpec.NOT_VALID_WORD. + */ + @UsedForTesting + private int insertWordToTrie(final String word, final int newTerminalId, + final boolean isNotAWord, final boolean isBlackListEntry, final boolean hasBigrams, + final boolean hasShortcuts) throws IOException, UnsupportedFormatException { + setPosition(0); + final FileHeader header = readHeader(); + + final int[] codePoints = FusionDictionary.getCodePoints(word); + final int wordLen = codePoints.length; + + int wordPos = 0; + for (int depth = 0; depth < FormatSpec.MAX_WORD_LENGTH; /* nop */) { + final int nodeArrayPos = getPosition(); + final int ptNodeCount = readPtNodeCount(); + boolean goToChildren = false; + int parentPos = FormatSpec.NO_PARENT_ADDRESS; + for (int i = 0; i < ptNodeCount; ++i) { + final int nodePos = getPosition(); + final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(nodePos, header.mFormatOptions); + if (BinaryDictIOUtils.isMovedPtNode(nodeInfo.mFlags, header.mFormatOptions)) { + continue; + } + if (nodeInfo.mParentPos != FormatSpec.NO_PARENT_ADDRESS) { + parentPos = nodePos + nodeInfo.mParentPos; + } + + final boolean firstCharacterMatched = + codePoints[wordPos] == nodeInfo.mCharacters[0]; + boolean allCharactersMatched = true; + int firstDifferentCharacterIndex = -1; + for (int p = 0; p < nodeInfo.mCharacters.length; ++p) { + if (wordPos + p >= codePoints.length) break; + if (codePoints[wordPos + p] != nodeInfo.mCharacters[p]) { + if (firstDifferentCharacterIndex == -1) { + firstDifferentCharacterIndex = p; + } + allCharactersMatched = false; + } + } + + if (!firstCharacterMatched) { + // Go to the next sibling node. + continue; + } + + if (!allCharactersMatched) { + final int parentNodeArrayStartPos = mDictBuffer.limit(); + splitAndBranch(nodeArrayPos, nodePos, nodeInfo, firstDifferentCharacterIndex, + parentPos, codePoints, wordPos + firstDifferentCharacterIndex, + newTerminalId, hasShortcuts, hasBigrams, isNotAWord, + isBlackListEntry, header.mFormatOptions); + + return parentNodeArrayStartPos + computePtNodeSize(codePoints, wordPos, + wordPos + firstDifferentCharacterIndex, false) + + FormatSpec.FORWARD_LINK_ADDRESS_SIZE + 1 /* size of PtNodeCount */; + } + + if (wordLen - wordPos < nodeInfo.mCharacters.length) { + final int parentNodeArrayStartPos = mDictBuffer.limit(); + splitOnly(nodeArrayPos, nodePos, nodeInfo, wordLen - wordPos, parentPos, + newTerminalId, hasShortcuts, hasBigrams, isNotAWord, isBlackListEntry, + header.mFormatOptions); + + // Return the position of the inserted word. + return parentNodeArrayStartPos + 1 /* size of PtNodeCount */; + } + + wordPos += nodeInfo.mCharacters.length; + if (wordPos == wordLen) { + // This dictionary already contains the word. + Log.e(TAG, "Something went wrong. If the word is already contained, " + + " there is no need to insert new PtNode."); + return FormatSpec.NOT_VALID_WORD; + } + if (nodeInfo.mChildrenPos == FormatSpec.NO_CHILDREN_ADDRESS) { + // There are no children. + // We need to add a new node as a child of this node. + final int newNodeArrayPos = mDictBuffer.limit(); + final int[] newNodeCodePoints = Arrays.copyOfRange(codePoints, wordPos, + codePoints.length); + writeNewSinglePtNodeWithAttributes(newNodeCodePoints, hasShortcuts, + newTerminalId, hasBigrams, isNotAWord, isBlackListEntry, nodePos, + header.mFormatOptions); + updateChildrenPos(nodePos, newNodeArrayPos, header.mFormatOptions); + return newNodeArrayPos + 1 /* size of PtNodeCount */; + } else { + // Found the matched node. + // Go to the children of this node. + setPosition(nodeInfo.mChildrenPos); + goToChildren = true; + depth++; + break; + } + } + + if (goToChildren) continue; + if (!readAndFollowForwardLink()) { + // Add a new node that contains [wordPos, word.length()-1]. + // and update the forward link. + final int newNodeArrayPos = mDictBuffer.limit(); + final int[] newCodePoints = Arrays.copyOfRange(codePoints, wordPos, + codePoints.length); + writeNewSinglePtNodeWithAttributes(newCodePoints, hasShortcuts, newTerminalId, + hasBigrams, isNotAWord, isBlackListEntry, parentPos, header.mFormatOptions); + updateForwardLink(nodeArrayPos, newNodeArrayPos, header.mFormatOptions); + return newNodeArrayPos + 1 /* size of PtNodeCount */; + } + } + return FormatSpec.NOT_VALID_WORD; + } + + private void updateFrequency(final int terminalId, final int frequency) { + mFrequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE); + BinaryDictEncoderUtils.writeUIntToDictBuffer(mFrequencyBuffer, frequency, + FormatSpec.FREQUENCY_AND_FLAGS_SIZE); + } + + private void insertFrequency(final int frequency) throws IOException { + final OutputStream frequencyStream = new FileOutputStream(mFrequencyFile, + true /* append */); + BinaryDictEncoderUtils.writeUIntToStream(frequencyStream, frequency, + FormatSpec.FREQUENCY_AND_FLAGS_SIZE); + frequencyStream.close(); + } + + private void insertTerminalPosition(final int posOfTerminal) throws IOException, + UnsupportedFormatException { + final OutputStream terminalPosStream = new FileOutputStream( + getFile(FILETYPE_TERMINAL_ADDRESS_TABLE), true /* append */); + BinaryDictEncoderUtils.writeUIntToStream(terminalPosStream, posOfTerminal, + FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); + terminalPosStream.close(); + } + + private void insertBigrams(final int terminalId, final int frequency, + final ArrayList<PendingAttribute> bigramAddresses) + throws IOException, UnsupportedFormatException { + openDictBuffer(); + final BigramContentUpdater updater = new BigramContentUpdater(mDictDirectory.getName(), + mDictDirectory, false); + + // Convert addresses to terminal ids. + final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList(); + mDictBuffer.position(0); + final FileHeader header = readHeader(); + for (PendingAttribute attr : bigramAddresses) { + mDictBuffer.position(attr.mAddress); + final Ver4PtNodeInfo info = readVer4PtNodeInfo(attr.mAddress, header.mFormatOptions); + if (info.mTerminalId == PtNode.NOT_A_TERMINAL) { + throw new RuntimeException("We can't have a bigram target that's not a terminal."); + } + bigrams.add(new PendingAttribute(frequency, info.mTerminalId)); + } + updater.insertBigramEntries(terminalId, frequency, bigrams); + close(); + } + + private void insertShortcuts(final int terminalId, final ArrayList<WeightedString> shortcuts) + throws IOException { + final ShortcutContentUpdater updater = new ShortcutContentUpdater(mDictDirectory.getName(), + mDictDirectory); + updater.insertShortcuts(terminalId, shortcuts); + } + + private void openBuffersAndStream() throws IOException, UnsupportedFormatException { + openDictBuffer(); + mDictStream = new FileOutputStream(getFile(FILETYPE_TRIE), true /* append */); + } + + private void close() throws IOException { + if (mDictStream != null) { + mDictStream.close(); + mDictStream = null; + } + mDictBuffer = null; + mFrequencyBuffer = null; + mTerminalAddressTableBuffer = null; + } + + private void updateAttributes(final int posOfWord, final int frequency, + final ArrayList<WeightedString> bigramStrings, + final ArrayList<WeightedString> shortcuts, final boolean isNotAWord, + final boolean isBlackListEntry) throws IOException, UnsupportedFormatException { + mDictBuffer.position(0); + final FileHeader header = readHeader(); + mDictBuffer.position(posOfWord); + final Ver4PtNodeInfo info = readVer4PtNodeInfo(posOfWord, header.mFormatOptions); + final int terminalId = info.mTerminalId; + + // Update the flags. + final int newFlags = setIsNotAWordInFlags( + setIsBlackListEntryInFlags(info.mFlags, isBlackListEntry), isNotAWord); + mDictBuffer.position(posOfWord); + mDictBuffer.put((byte) newFlags); + + updateFrequency(terminalId, frequency); + insertBigrams(terminalId, frequency, + DynamicBinaryDictIOUtils.resolveBigramPositions(this, bigramStrings)); + insertShortcuts(terminalId, shortcuts); + } + + @Override @UsedForTesting public void insertWord(final String word, final int frequency, final ArrayList<WeightedString> bigramStrings, final ArrayList<WeightedString> shortcuts, final boolean isNotAWord, final boolean isBlackListEntry) throws IOException, UnsupportedFormatException { - // TODO: Implement this method. + final int newTerminalId = getNewTerminalId(); + + openBuffersAndStream(); + final int posOfWord = getTerminalPosition(word); + if (posOfWord != FormatSpec.NOT_VALID_WORD) { + // The word is already contained in the dictionary. + updateAttributes(posOfWord, frequency, bigramStrings, shortcuts, isNotAWord, + isBlackListEntry); + close(); + return; + } + + // Insert new PtNode into trie. + final int posOfTerminal = insertWordToTrie(word, newTerminalId, isNotAWord, + isBlackListEntry, bigramStrings != null && !bigramStrings.isEmpty(), + shortcuts != null && !shortcuts.isEmpty()); + insertFrequency(frequency); + insertTerminalPosition(posOfTerminal); + close(); + + insertBigrams(newTerminalId, frequency, + DynamicBinaryDictIOUtils.resolveBigramPositions(this, bigramStrings)); + insertShortcuts(newTerminalId, shortcuts); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 1de15a333..296733fad 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -17,18 +17,16 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; -import android.content.SharedPreferences; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; -import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.FormatSpec; -import com.android.inputmethod.latin.settings.Settings; -import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener; @@ -36,7 +34,9 @@ import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.TimeUnit; /** * This class is a base class of a dictionary that supports decaying for the personalized language @@ -45,8 +45,7 @@ import java.util.Map; public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary { private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName(); public static final boolean DBG_SAVE_RESTORE = false; - private static final boolean DBG_STRESS_TEST = false; - private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG; + private static final boolean DBG_DUMP_ON_CLOSE = false; /** Any pair being typed or picked */ public static final int FREQUENCY_FOR_TYPED = 2; @@ -54,41 +53,43 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB public static final int FREQUENCY_FOR_WORDS_IN_DICTS = FREQUENCY_FOR_TYPED; public static final int FREQUENCY_FOR_WORDS_NOT_IN_DICTS = Dictionary.NOT_A_PROBABILITY; - /** Locale for which this user history dictionary is storing words */ - private final String mLocale; - - private final String mFileName; + public static final int REQUIRED_BINARY_DICTIONARY_VERSION = 4; - private final SharedPreferences mPrefs; + /** Locale for which this user history dictionary is storing words */ + private final Locale mLocale; - private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions = - CollectionUtils.newArrayList(); + private final String mDictName; - // Should always be false except when we use this class for test - @UsedForTesting boolean mIsTest = false; + /* package */ DecayingExpandableBinaryDictionaryBase(final Context context, + final Locale locale, final String dictionaryType, final String dictName) { + super(context, dictName, locale, dictionaryType, true); + mLocale = locale; + mDictName = dictName; + if (mLocale != null && mLocale.toString().length() > 1) { + reloadDictionaryIfRequired(); + } + } + // Creates an instance that uses a given dictionary file for testing. + @UsedForTesting /* package */ DecayingExpandableBinaryDictionaryBase(final Context context, - final String locale, final SharedPreferences sp, final String dictionaryType, - final String fileName) { - super(context, fileName, dictionaryType, true); + final Locale locale, final String dictionaryType, final String dictName, + final File dictFile) { + super(context, dictName, locale, dictionaryType, true, dictFile); mLocale = locale; - mFileName = fileName; - mPrefs = sp; - if (mLocale != null && mLocale.length() > 1) { - asyncLoadDictionaryToMemory(); + mDictName = dictName; + if (mLocale != null && mLocale.toString().length() > 1) { reloadDictionaryIfRequired(); } } @Override public void close() { - if (!ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { - closeBinaryDictionary(); + if (DBG_DUMP_ON_CLOSE) { + dumpAllWordsForDebug(); } // Flush pending writes. - // TODO: Remove after this class become to use a dynamic binary dictionary. - asyncFlashAllBinaryDictionary(); - Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale); + asyncFlushBinaryDictionary(); } @Override @@ -98,8 +99,12 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFileName); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale); + attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE, + FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString()); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE, + String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); return attributeMap; } @@ -113,6 +118,25 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB return false; } + @Override + protected boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { + // This class is using format 4 because it's used by all version 4 dictionaries. + // TODO: remove this when all dynamically generated dicts use version 4. + return formatVersion == REQUIRED_BINARY_DICTIONARY_VERSION; + } + + public void addMultipleDictionaryEntriesToDictionary( + final ArrayList<LanguageModelParam> languageModelParams, + final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) { + if (languageModelParams == null || languageModelParams.isEmpty()) { + if (callback != null) { + callback.onFinished(); + } + return; + } + addMultipleDictionaryEntriesDynamically(languageModelParams, callback); + } + /** * Pair will be added to the decaying dictionary. * @@ -121,70 +145,63 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB * context, as in beginning of a sentence for example. * The second word may not be null (a NullPointerException would be thrown). */ - public void addToDictionary(final String word0, final String word1, final boolean isValid) { + public void addToDictionary(final String word0, final String word1, final boolean isValid, + final int timestamp) { if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || (word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { return; } - final int frequency = ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? - (isValid ? FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS) : - FREQUENCY_FOR_TYPED; - addWordDynamically(word1, null /* shortcutTarget */, frequency, 0 /* shortcutFreq */, - false /* isNotAWord */); + final int frequency = isValid ? + FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS; + addWordDynamically(word1, frequency, null /* shortcutTarget */, 0 /* shortcutFreq */, + false /* isNotAWord */, false /* isBlacklisted */, timestamp); // Do not insert a word as a bigram of itself if (word1.equals(word0)) { return; } if (null != word0) { - addBigramDynamically(word0, word1, frequency, isValid); + addBigramDynamically(word0, word1, frequency, timestamp); } } - public void cancelAddingUserHistory(final String word0, final String word1) { - removeBigramDynamically(word0, word1); - } - @Override protected void loadDictionaryAsync() { - final int[] profTotalCount = { 0 }; - final String locale = getLocale(); - if (DBG_STRESS_TEST) { - try { - Log.w(TAG, "Start stress in loading: " + locale); - Thread.sleep(15000); - Log.w(TAG, "End stress in loading"); - } catch (InterruptedException e) { + // Never loaded to memory in Java side. + } + + @UsedForTesting + public void dumpAllWordsForDebug() { + runAfterGcForDebug(new Runnable() { + @Override + public void run() { + dumpAllWordsForDebugLocked(); } - } - final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale); - final long now = System.currentTimeMillis(); - final ExpandableBinaryDictionary dictionary = this; + }); + } + + private void dumpAllWordsForDebugLocked() { + Log.d(TAG, "dumpAllWordsForDebug started."); final OnAddWordListener listener = new OnAddWordListener() { @Override public void setUnigram(final String word, final String shortcutTarget, final int frequency, final int shortcutFreq) { - if (DBG_SAVE_RESTORE) { - Log.d(TAG, "load unigram: " + word + "," + frequency); - } - addWord(word, shortcutTarget, frequency, shortcutFreq, false /* isNotAWord */); - ++profTotalCount[0]; + Log.d(TAG, "load unigram: " + word + "," + frequency); } @Override public void setBigram(final String word0, final String word1, final int frequency) { if (word0.length() < Constants.DICTIONARY_MAX_WORD_LENGTH && word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) { - if (DBG_SAVE_RESTORE) { - Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency); - } - ++profTotalCount[0]; - addBigram(word0, word1, frequency, last); + Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency); + } else { + Log.d(TAG, "Skip inserting a too long bigram: " + word0 + "," + word1 + "," + + frequency); } } }; // Load the dictionary from binary file - final File dictFile = new File(mContext.getFilesDir(), mFileName); + final File dictFile = new File(mContext.getFilesDir(), mDictName); final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile, DictDecoder.USE_BYTEARRAY); if (dictDecoder == null) { @@ -198,35 +215,17 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener); } catch (IOException e) { Log.d(TAG, "IOException on opening a bytebuffer", e); - } finally { - if (PROFILE_SAVE_RESTORE) { - final long diff = System.currentTimeMillis() - now; - Log.d(TAG, "PROF: Load UserHistoryDictionary: " - + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries."); - } + } catch (UnsupportedFormatException e) { + Log.d(TAG, "Unsupported format, can't read the dictionary", e); } } - protected String getLocale() { - return mLocale; - } - - public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) { - session.setPredictionDictionary(this); - mSessions.add(session); - session.onDictionaryReady(); - } - - public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) { - mSessions.remove(session); - } - @UsedForTesting public void clearAndFlushDictionary() { // Clear the node structure on memory clear(); // Then flush the cleared state of the dictionary on disk. - asyncFlashAllBinaryDictionary(); + asyncFlushBinaryDictionary(); } /* package */ void decayIfNeeded() { diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java deleted file mode 100644 index 6f152bb91..000000000 --- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.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; -import com.android.inputmethod.latin.WordComposer; -import com.android.inputmethod.latin.ExpandableDictionary.NextWord; -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.UnsupportedFormatException; -import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils; -import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface; -import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils; -import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; - -// Currently this class is used to implement dynamic prodiction dictionary. -// TODO: Move to native code. -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 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; - - private static final int BINARY_DICT_VERSION = 3; - private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */); - - 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 - public void clear() { - mBigramList.evictAll(); - mExpandableDictionary.clearDictionary(); - } - - /** - * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes - * are done to update the binary dictionary. - * @param word The word to add. - * @param shortcutTarget A shortcut target for this word, or null if none. - * @param frequency The frequency for this unigram. - * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored - * if shortcutTarget is null. - * @param isNotAWord true if this is not a word, i.e. shortcut only. - */ - @Override - public void addUnigramWord(final String word, final String shortcutTarget, final int frequency, - final int shortcutFreq, final boolean isNotAWord) { - if (mBigramList.size() > mMaxHistoryBigrams * 2) { - // Too many entries: just stop adding new vocabulary and wait next refresh. - return; - } - mExpandableDictionary.addWord(word, shortcutTarget, frequency, shortcutFreq); - mBigramList.addBigram(null, word, (byte)frequency); - } - - @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 vocabulary and wait next refresh. - return; - } - if (lastModifiedTime > 0) { - mExpandableDictionary.setBigramAndGetFrequency(word0, word1, - new ForgettingCurveParams(frequency, System.currentTimeMillis(), - lastModifiedTime)); - mBigramList.addBigram(word0, word1, (byte)frequency); - } else { - mExpandableDictionary.setBigramAndGetFrequency(word0, word1, - new ForgettingCurveParams(isValid)); - mBigramList.addBigram(word0, word1, (byte)frequency); - } - } - - @Override - public void removeBigramWords(final String word0, final String word1) { - if (mBigramList.removeBigram(word0, word1)) { - mExpandableDictionary.removeBigram(word0, word1); - } - } - - @Override - protected void writeDictionary(final DictEncoder dictEncoder, - final Map<String, String> attributeMap) throws IOException, UnsupportedFormatException { - UserHistoryDictIOUtils.writeDictionary(dictEncoder, - new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams), - mBigramList, FORMAT_OPTIONS); - } - - private static class FrequencyProvider implements BigramDictionaryInterface { - private final UserHistoryDictionaryBigramList mBigramList; - private final ExpandableDictionary mExpandableDictionary; - private final int mMaxHistoryBigrams; - - public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList, - 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; - if (word0 == null) { // unigram - freq = FREQUENCY_FOR_TYPED; - } else { // bigram - final NextWord nw = mExpandableDictionary.getBigramWord(word0, word1); - if (nw != null) { - final ForgettingCurveParams forgettingCurveParams = nw.getFcParams(); - final byte prevFc = mBigramList.getBigrams(word0).get(word1); - final byte fc = forgettingCurveParams.getFc(); - final boolean isValid = forgettingCurveParams.isValid(); - if (prevFc > 0 && prevFc == fc) { - freq = fc & 0xFF; - } else if (UserHistoryForgettingCurveUtils. - needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) { - freq = fc & 0xFF; - } else { - // Delete this entry - freq = -1; - } - } else { - // Delete this entry - freq = -1; - } - } - return freq; - } - } - - @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final String prevWord, final ProximityInfo proximityInfo, - boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { - return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions); - } - - @Override - public boolean isValidWord(final String word) { - return mExpandableDictionary.isValidWord(word); - } - - @UsedForTesting - public boolean isInBigramListForTests(final String word) { - // TODO: Use native method to determine whether the word is in dictionary or not - return mBigramList.containsKey(word) || mBigramList.getBigrams(null).containsKey(word); - } -} diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index f257165cb..596562f1d 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -16,53 +16,37 @@ package com.android.inputmethod.latin.personalization; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.utils.CollectionUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.Locale; + import android.content.Context; -import android.content.SharedPreferences; -import java.util.ArrayList; +public class PersonalizationDictionary extends DecayingExpandableBinaryDictionaryBase { + private static final String NAME = PersonalizationDictionary.class.getSimpleName(); -/** - * This class is a dictionary for the personalized language model that uses binary dictionary. - */ -public class PersonalizationDictionary extends ExpandableBinaryDictionary { - private static final String NAME = "personalization"; private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions = CollectionUtils.newArrayList(); - /** Locale for which this user history dictionary is storing words */ - private final String mLocale; - - public PersonalizationDictionary(final Context context, final String locale, - final SharedPreferences prefs) { - // TODO: Make isUpdatable true. - super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION, - false /* isUpdatable */); - mLocale = locale; - // TODO: Restore last updated time - loadDictionary(); - } - - @Override - protected void loadDictionaryAsync() { - // TODO: Implement - } - - @Override - protected boolean hasContentChanged() { - return false; + /* package */ PersonalizationDictionary(final Context context, final Locale locale) { + super(context, locale, Dictionary.TYPE_PERSONALIZATION, + getDictNameWithLocale(NAME, locale)); } - @Override - protected boolean needsToReloadBeforeWriting() { - return false; + // Creates an instance that uses a given dictionary file for testing. + @UsedForTesting + public PersonalizationDictionary(final Context context, final Locale locale, + final File dictFile) { + super(context, locale, Dictionary.TYPE_PERSONALIZATION, getDictNameWithLocale(NAME, locale), + dictFile); } public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) { - session.setDictionary(this); + session.setPredictionDictionary(this); mSessions.add(session); session.onDictionaryReady(); } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java index c1833ff14..542bda621 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java @@ -20,18 +20,18 @@ import android.content.Context; import android.content.res.Configuration; public class PersonalizationDictionarySessionRegister { - public static void init(Context context) { + public static void init(final Context context) { } public static void onConfigurationChanged(final Context context, final Configuration conf) { } - public static void onUpdateData(Context context, String type) { + public static void onUpdateData(final Context context, final String type) { } - public static void onRemoveData(Context context, String type) { + public static void onRemoveData(final Context context, final String type) { } - public static void onDestroy(Context context) { + public static void onDestroy(final Context context) { } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java index a86f6e584..61354762b 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java @@ -18,61 +18,37 @@ package com.android.inputmethod.latin.personalization; import android.content.Context; +import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; +import com.android.inputmethod.latin.ExpandableBinaryDictionary; + import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Locale; /** * This class is a session where a data provider can communicate with a personalization * dictionary. */ public abstract class PersonalizationDictionaryUpdateSession { - /** - * This class is a parameter for a new unigram or bigram word which will be added - * to the personalization dictionary. - */ - public static class PersonalizationLanguageModelParam { - public final String mWord0; - public final String mWord1; - public final boolean mIsValid; - public final int mFrequency; - public PersonalizationLanguageModelParam(String word0, String word1, boolean isValid, - int frequency) { - mWord0 = word0; - mWord1 = word1; - mIsValid = isValid; - mFrequency = frequency; - } - } - - // TODO: Use a dynamic binary dictionary instead public WeakReference<PersonalizationDictionary> mDictionary; - public WeakReference<DecayingExpandableBinaryDictionaryBase> mPredictionDictionary; - public final String mSystemLocale; - public PersonalizationDictionaryUpdateSession(String locale) { + public final Locale mSystemLocale; + + public PersonalizationDictionaryUpdateSession(final Locale locale) { mSystemLocale = locale; } public abstract void onDictionaryReady(); - public abstract void onDictionaryClosed(Context context); + public abstract void onDictionaryClosed(final Context context); - public void setDictionary(PersonalizationDictionary dictionary) { + public void setPredictionDictionary(final PersonalizationDictionary dictionary) { mDictionary = new WeakReference<PersonalizationDictionary>(dictionary); } - public void setPredictionDictionary(DecayingExpandableBinaryDictionaryBase dictionary) { - mPredictionDictionary = - new WeakReference<DecayingExpandableBinaryDictionaryBase>(dictionary); - } - protected PersonalizationDictionary getDictionary() { return mDictionary == null ? null : mDictionary.get(); } - protected DecayingExpandableBinaryDictionaryBase getPredictionDictionary() { - return mPredictionDictionary == null ? null : mPredictionDictionary.get(); - } - private void unsetDictionary() { final PersonalizationDictionary dictionary = getDictionary(); if (dictionary == null) { @@ -81,48 +57,30 @@ public abstract class PersonalizationDictionaryUpdateSession { dictionary.unRegisterUpdateSession(this); } - private void unsetPredictionDictionary() { - final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary(); - if (dictionary == null) { - return; - } - dictionary.unRegisterUpdateSession(this); - } - - public void clearAndFlushPredictionDictionary(Context context) { - final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary(); + public void clearAndFlushDictionary(final Context context) { + final PersonalizationDictionary dictionary = getDictionary(); if (dictionary == null) { return; } dictionary.clearAndFlushDictionary(); } - public void closeSession(Context context) { + public void closeSession(final Context context) { unsetDictionary(); - unsetPredictionDictionary(); onDictionaryClosed(context); } - // TODO: Support multi locale to add bigram - public void addBigramToPersonalizationDictionary(String word0, String word1, boolean isValid, - int frequency) { - final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary(); - if (dictionary == null) { - return; - } - dictionary.addToDictionary(word0, word1, isValid); - } - - // Bulk import - // TODO: Support multi locale to add bigram - public void addBigramsToPersonalizationDictionary( - final ArrayList<PersonalizationLanguageModelParam> lmParams) { - final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary(); + // TODO: Support multi locale. + public void addMultipleDictionaryEntriesToDictionary( + final ArrayList<LanguageModelParam> languageModelParams, + final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) { + final PersonalizationDictionary dictionary = getDictionary(); if (dictionary == null) { + if (callback != null) { + callback.onFinished(); + } return; } - for (final PersonalizationLanguageModelParam lmParam : lmParams) { - dictionary.addToDictionary(lmParam.mWord0, lmParam.mWord1, lmParam.mIsValid); - } + dictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams, callback); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index 221ddeeba..d55cae132 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -19,11 +19,10 @@ package com.android.inputmethod.latin.personalization; import com.android.inputmethod.latin.utils.CollectionUtils; import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.util.Log; import java.lang.ref.SoftReference; +import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; public class PersonalizationHelper { @@ -31,21 +30,16 @@ public class PersonalizationHelper { private static final boolean DEBUG = false; private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap(); - private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>> sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap(); - private static final ConcurrentHashMap<String, - SoftReference<PersonalizationPredictionDictionary>> - sLangPersonalizationPredictionDictCache = - CollectionUtils.newConcurrentHashMap(); - public static UserHistoryDictionary getUserHistoryDictionary( - final Context context, final String locale, final SharedPreferences sp) { + final Context context, final Locale locale) { + final String localeStr = locale.toString(); synchronized (sLangUserHistoryDictCache) { - if (sLangUserHistoryDictCache.containsKey(locale)) { + if (sLangUserHistoryDictCache.containsKey(localeStr)) { final SoftReference<UserHistoryDictionary> ref = - sLangUserHistoryDictCache.get(locale); + sLangUserHistoryDictCache.get(localeStr); final UserHistoryDictionary dict = ref == null ? null : ref.get(); if (dict != null) { if (DEBUG) { @@ -55,8 +49,9 @@ public class PersonalizationHelper { return dict; } } - final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale, sp); - sLangUserHistoryDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict)); + final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale); + sLangUserHistoryDictCache.put(localeStr, + new SoftReference<UserHistoryDictionary>(dict)); return dict; } } @@ -74,57 +69,30 @@ public class PersonalizationHelper { } public static void registerPersonalizationDictionaryUpdateSession(final Context context, - final PersonalizationDictionaryUpdateSession session, String locale) { - final PersonalizationPredictionDictionary predictionDictionary = - getPersonalizationPredictionDictionary(context, locale, - PreferenceManager.getDefaultSharedPreferences(context)); - predictionDictionary.registerUpdateSession(session); - final PersonalizationDictionary dictionary = - getPersonalizationDictionary(context, locale, - PreferenceManager.getDefaultSharedPreferences(context)); - dictionary.registerUpdateSession(session); + final PersonalizationDictionaryUpdateSession session, final Locale locale) { + final PersonalizationDictionary personalizationDictionary = + getPersonalizationDictionary(context, locale); + personalizationDictionary.registerUpdateSession(session); } public static PersonalizationDictionary getPersonalizationDictionary( - final Context context, final String locale, final SharedPreferences sp) { + final Context context, final Locale locale) { + final String localeStr = locale.toString(); synchronized (sLangPersonalizationDictCache) { - if (sLangPersonalizationDictCache.containsKey(locale)) { + if (sLangPersonalizationDictCache.containsKey(localeStr)) { final SoftReference<PersonalizationDictionary> ref = - sLangPersonalizationDictCache.get(locale); + sLangPersonalizationDictCache.get(localeStr); final PersonalizationDictionary dict = ref == null ? null : ref.get(); if (dict != null) { if (DEBUG) { - Log.w(TAG, "Use cached PersonalizationDictCache for " + locale); + Log.w(TAG, "Use cached PersonalizationDictionary for " + locale); } return dict; } } - final PersonalizationDictionary dict = - new PersonalizationDictionary(context, locale, sp); + final PersonalizationDictionary dict = new PersonalizationDictionary(context, locale); sLangPersonalizationDictCache.put( - locale, new SoftReference<PersonalizationDictionary>(dict)); - return dict; - } - } - - public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary( - final Context context, final String locale, final SharedPreferences sp) { - synchronized (sLangPersonalizationPredictionDictCache) { - if (sLangPersonalizationPredictionDictCache.containsKey(locale)) { - final SoftReference<PersonalizationPredictionDictionary> ref = - sLangPersonalizationPredictionDictCache.get(locale); - final PersonalizationPredictionDictionary dict = ref == null ? null : ref.get(); - if (dict != null) { - if (DEBUG) { - Log.w(TAG, "Use cached PersonalizationPredictionDictionary for " + locale); - } - return dict; - } - } - final PersonalizationPredictionDictionary dict = - new PersonalizationPredictionDictionary(context, locale, sp); - sLangPersonalizationPredictionDictCache.put( - locale, new SoftReference<PersonalizationPredictionDictionary>(dict)); + localeStr, new SoftReference<PersonalizationDictionary>(dict)); return dict; } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java deleted file mode 100644 index 432954453..000000000 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.personalization; - -import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; - -import android.content.Context; -import android.content.SharedPreferences; - -public class PersonalizationPredictionDictionary extends DecayingExpandableBinaryDictionaryBase { - private static final String NAME = PersonalizationPredictionDictionary.class.getSimpleName(); - - /* package */ PersonalizationPredictionDictionary(final Context context, final String locale, - final SharedPreferences sp) { - super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA, - getDictionaryFileName(locale)); - } - - private static String getDictionaryFileName(final String locale) { - return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; - } -} diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index a60226d7e..868f21cbc 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -16,11 +16,13 @@ package com.android.inputmethod.latin.personalization; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; + +import java.io.File; +import java.util.Locale; import android.content.Context; -import android.content.SharedPreferences; /** * Locally gathers stats about the words user types and various other signals like auto-correction @@ -29,12 +31,19 @@ import android.content.SharedPreferences; public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase { /* package for tests */ static final String NAME = UserHistoryDictionary.class.getSimpleName(); - /* package */ UserHistoryDictionary(final Context context, final String locale, - final SharedPreferences sp) { - super(context, locale, sp, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale)); + /* package */ UserHistoryDictionary(final Context context, final Locale locale) { + super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictNameWithLocale(NAME, locale)); + } + + // Creates an instance that uses a given dictionary file for testing. + @UsedForTesting + public UserHistoryDictionary(final Context context, final Locale locale, + final File dictFile) { + super(context, locale, Dictionary.TYPE_USER_HISTORY, getDictNameWithLocale(NAME, locale), + dictFile); } - private static String getDictionaryFileName(final String locale) { - return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + public void cancelAddingUserHistory(final String word0, final String word1) { + removeBigramDynamically(word0, word1); } } diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index da1fb73fe..d060485bd 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -112,8 +112,7 @@ public final class DebugSettings extends PreferenceFragment updateDebugMode(); mServiceNeedsRestart = true; } - } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH) - || key.equals(PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG)) { + } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { mServiceNeedsRestart = true; } } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index df2c6907f..714c3a97a 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -27,12 +27,10 @@ import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.StringUtils; -import java.util.HashMap; import java.util.Locale; import java.util.concurrent.locks.ReentrantLock; @@ -53,8 +51,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; public static final String PREF_MISC_SETTINGS = "misc_settings"; - public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME = - "last_user_dictionary_write_time"; public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD = @@ -229,16 +225,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang res.getBoolean(R.bool.config_default_phrase_gesture_enabled)); } - public static boolean readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption( - final Resources res) { - return res.getBoolean(R.bool.config_enable_show_option_of_key_preview_popup); + public static boolean readFromBuildConfigIfToShowKeyPreviewPopupOption(final Resources res) { + return res.getBoolean(R.bool.config_enable_show_key_preview_popup_option); } public static boolean readKeyPreviewPopupEnabled(final SharedPreferences prefs, final Resources res) { final boolean defaultKeyPreviewPopup = res.getBoolean( R.bool.config_default_key_preview_popup); - if (!readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) { + if (!readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { return defaultKeyPreviewPopup; } return prefs.getBoolean(PREF_POPUP_ON, defaultKeyPreviewPopup); @@ -333,25 +328,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); } - public static long readLastUserHistoryWriteTime(final SharedPreferences prefs, - final String locale) { - final String str = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, ""); - final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str); - if (map.containsKey(locale)) { - return map.get(locale); - } - return 0; - } - - public static void writeLastUserHistoryWriteTime(final SharedPreferences prefs, - final String locale) { - final String oldStr = prefs.getString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, ""); - final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr); - map.put(locale, System.currentTimeMillis()); - final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map); - prefs.edit().putString(PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply(); - } - public static boolean readUseFullscreenMode(final Resources res) { return res.getBoolean(R.bool.config_use_fullscreen_mode); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 5c60a7350..d7a3e95b3 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -48,7 +48,6 @@ import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.FeedbackUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; -import com.android.inputmethod.research.ResearchLogger; import com.android.inputmethodcommon.InputMethodSettingsFragment; import java.util.TreeSet; @@ -61,13 +60,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */; - private CheckBoxPreference mVoiceInputKeyPreference; - private ListPreference mShowCorrectionSuggestionsPreference; - private ListPreference mAutoCorrectionThresholdPreference; - private ListPreference mKeyPreviewPopupDismissDelay; - // Use bigrams to predict the next word when there is no input for it yet - private CheckBoxPreference mBigramPrediction; - private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { final Preference preference = findPreference(preferenceKey); if (preference != null) { @@ -75,6 +67,18 @@ public final class SettingsFragment extends InputMethodSettingsFragment } } + private void updateListPreferenceSummaryToCurrentValue(final String prefKey) { + // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before + // KitKat, we need to update the summary programmatically. + final ListPreference listPreference = (ListPreference)findPreference(prefKey); + if (listPreference == null) { + return; + } + final CharSequence entries[] = listPreference.getEntries(); + final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue()); + listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]); + } + private static void removePreference(final String preferenceKey, final PreferenceGroup parent) { if (parent == null) { return; @@ -107,16 +111,9 @@ public final class SettingsFragment extends InputMethodSettingsFragment SubtypeLocaleUtils.init(context); AudioAndHapticFeedbackManager.init(context); - mVoiceInputKeyPreference = - (CheckBoxPreference) findPreference(Settings.PREF_VOICE_INPUT_KEY); - mShowCorrectionSuggestionsPreference = - (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING); final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); prefs.registerOnSharedPreferenceChangeListener(this); - mAutoCorrectionThresholdPreference = - (ListPreference) findPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD); - mBigramPrediction = (CheckBoxPreference) findPreference(Settings.PREF_BIGRAM_PREDICTIONS); ensureConsistencyOfAutoCorrectionSettings(); final PreferenceGroup generalSettings = @@ -143,12 +140,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(final Preference pref) { - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - // Use development-only feedback mechanism - ResearchLogger.getInstance().presentFeedbackDialogFromSettings(); - } else { - FeedbackUtils.showFeedbackForm(getActivity()); - } + FeedbackUtils.showFeedbackForm(getActivity()); return true; } }); @@ -167,7 +159,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { - generalSettings.removePreference(mVoiceInputKeyPreference); + removePreference(Settings.PREF_VOICE_INPUT_KEY, generalSettings); } final PreferenceGroup advancedSettings = @@ -177,26 +169,27 @@ public final class SettingsFragment extends InputMethodSettingsFragment removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings); } - mKeyPreviewPopupDismissDelay = - (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(res)) { + if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { removePreference(Settings.PREF_POPUP_ON, generalSettings); removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings); } else { + // TODO: Cleanup this setup. + final ListPreference keyPreviewPopupDismissDelay = + (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( R.integer.config_key_preview_linger_timeout)); - mKeyPreviewPopupDismissDelay.setEntries(new String[] { + keyPreviewPopupDismissDelay.setEntries(new String[] { res.getString(R.string.key_preview_popup_dismiss_no_delay), res.getString(R.string.key_preview_popup_dismiss_default_delay), }); - mKeyPreviewPopupDismissDelay.setEntryValues(new String[] { + keyPreviewPopupDismissDelay.setEntryValues(new String[] { "0", popupDismissDelayDefaultValue }); - if (null == mKeyPreviewPopupDismissDelay.getValue()) { - mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); + if (null == keyPreviewPopupDismissDelay.getValue()) { + keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); } - mKeyPreviewPopupDismissDelay.setEnabled( + keyPreviewPopupDismissDelay.setEnabled( Settings.readKeyPreviewPopupEnabled(prefs, res)); } @@ -243,20 +236,19 @@ public final class SettingsFragment extends InputMethodSettingsFragment @Override public void onResume() { super.onResume(); - final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - if (!isShortcutImeEnabled) { - getPreferenceScreen().removePreference(mVoiceInputKeyPreference); - } final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final Resources res = getResources(); + final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); + setPreferenceEnabled(Settings.PREF_VOICE_INPUT_KEY, isShortcutImeEnabled); final CheckBoxPreference showSetupWizardIcon = (CheckBoxPreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); if (showSetupWizardIcon != null) { showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity())); } - updateShowCorrectionSuggestionsSummary(); - updateKeyPreviewPopupDelaySummary(); - updateColorSchemeSummary(prefs, getResources()); - updateCustomInputStylesSummary(); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT); + updateCustomInputStylesSummary(prefs, res); } @Override @@ -287,50 +279,26 @@ public final class SettingsFragment extends InputMethodSettingsFragment LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); } ensureConsistencyOfAutoCorrectionSettings(); - updateShowCorrectionSuggestionsSummary(); - updateKeyPreviewPopupDelaySummary(); - updateColorSchemeSummary(prefs, res); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_SHOW_SUGGESTIONS_SETTING); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_LAYOUT); refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); } private void ensureConsistencyOfAutoCorrectionSettings() { final String autoCorrectionOff = getResources().getString( R.string.auto_correction_threshold_mode_index_off); - final String currentSetting = mAutoCorrectionThresholdPreference.getValue(); - mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff)); - } - - private void updateShowCorrectionSuggestionsSummary() { - mShowCorrectionSuggestionsPreference.setSummary( - getResources().getStringArray(R.array.prefs_suggestion_visibilities) - [mShowCorrectionSuggestionsPreference.findIndexOfValue( - mShowCorrectionSuggestionsPreference.getValue())]); + final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference( + Settings.PREF_AUTO_CORRECTION_THRESHOLD); + final String currentSetting = autoCorrectionThresholdPref.getValue(); + setPreferenceEnabled( + Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff)); } - private void updateColorSchemeSummary(final SharedPreferences prefs, final Resources res) { - // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before - // KitKat, we need to update the summary by code. - final Preference preference = findPreference(Settings.PREF_KEYBOARD_LAYOUT); - if (!(preference instanceof ListPreference)) { - Log.w(TAG, "Can't find Keyboard Color Scheme preference"); - return; - } - final ListPreference colorSchemePreference = (ListPreference)preference; - final int themeIndex = Settings.readKeyboardThemeIndex(prefs, res); - int entryIndex = colorSchemePreference.findIndexOfValue(Integer.toString(themeIndex)); - if (entryIndex < 0) { - final int defaultThemeIndex = Settings.resetAndGetDefaultKeyboardThemeIndex(prefs, res); - entryIndex = colorSchemePreference.findIndexOfValue( - Integer.toString(defaultThemeIndex)); - } - colorSchemePreference.setSummary(colorSchemePreference.getEntries()[entryIndex]); - } - - private void updateCustomInputStylesSummary() { + private void updateCustomInputStylesSummary(final SharedPreferences prefs, + final Resources res) { final PreferenceScreen customInputStyles = (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - final Resources res = getResources(); final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); final InputMethodSubtype[] subtypes = AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); @@ -342,13 +310,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment customInputStyles.setSummary(styles); } - private void updateKeyPreviewPopupDelaySummary() { - final ListPreference lp = mKeyPreviewPopupDismissDelay; - final CharSequence[] entries = lp.getEntries(); - if (entries == null || entries.length <= 0) return; - lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]); - } - private void refreshEnablingsOfKeypressSoundAndVibrationSettings( final SharedPreferences sp, final Resources res) { setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index f331c78e5..06406c19b 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -67,7 +67,7 @@ public final class SettingsValues { public final boolean mVibrateOn; public final boolean mSoundOn; public final boolean mKeyPreviewPopupOn; - private final boolean mShowsVoiceInputKey; + public final boolean mShowsVoiceInputKey; public final boolean mIncludesOtherImesInLanguageSwitchList; public final boolean mShowsLanguageSwitchKey; public final boolean mUseContactsDict; @@ -271,13 +271,6 @@ public final class SettingsValues { return mInputAttributes.mShouldInsertSpacesAutomatically; } - public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) { - final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled(); - final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; - return shortcutImeEnabled && mShowsVoiceInputKey - && !InputTypeUtils.isPasswordInputType(inputType); - } - public boolean isLanguageSwitchKeyEnabled() { if (!mShowsLanguageSwitchKey) { return false; @@ -374,16 +367,20 @@ public final class SettingsValues { return autoCorrectionThreshold; } - private static boolean needsToShowVoiceInputKey(SharedPreferences prefs, Resources res) { - final String voiceModeMain = res.getString(R.string.voice_mode_main); - final String voiceMode = prefs.getString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); - final boolean showsVoiceInputKey = voiceMode == null || voiceMode.equals(voiceModeMain); - if (!showsVoiceInputKey) { - // Migrate settings from PREF_VOICE_MODE_OBSOLETE to PREF_VOICE_INPUT_KEY - // Set voiceModeMain as a value of obsolete voice mode settings. - prefs.edit().putString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain).apply(); - // Disable voice input key. - prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, false).apply(); + private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs, + final Resources res) { + if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) { + // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to + // {@link Settings#PREF_VOICE_INPUT_KEY}. + final String voiceModeMain = res.getString(R.string.voice_mode_main); + final String voiceMode = prefs.getString( + Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); + final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode); + prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply(); + } + // Remove the obsolete preference if exists. + if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { + prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply(); } return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true); } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java index c4a813c24..5072fabd6 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -38,7 +38,7 @@ import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.utils.CollectionUtils; -import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import java.util.ArrayList; @@ -74,21 +74,21 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL private SettingsPoolingHandler mHandler; private static final class SettingsPoolingHandler - extends StaticInnerHandlerWrapper<SetupWizardActivity> { + extends LeakGuardHandlerWrapper<SetupWizardActivity> { private static final int MSG_POLLING_IME_SETTINGS = 0; private static final long IME_SETTINGS_POLLING_INTERVAL = 200; private final InputMethodManager mImmInHandler; - public SettingsPoolingHandler(final SetupWizardActivity outerInstance, + public SettingsPoolingHandler(final SetupWizardActivity ownerInstance, final InputMethodManager imm) { - super(outerInstance); + super(ownerInstance); mImmInHandler = imm; } @Override public void handleMessage(final Message msg) { - final SetupWizardActivity setupWizardActivity = getOuterInstance(); + final SetupWizardActivity setupWizardActivity = getOwnerInstance(); if (setupWizardActivity == null) { return; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 503b18b1b..c108b20ed 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -428,7 +428,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService final String localeStr = locale.toString(); UserBinaryDictionary userDictionary = mUserDictionaries.get(localeStr); if (null == userDictionary) { - userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true); + userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, locale, true); mUserDictionaries.put(localeStr, userDictionary); } dictionaryCollection.addDictionary(userDictionary); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index a0aed2829..b7a5a4026 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -49,6 +49,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> { final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList(); private final static DictAndKeyboard dummyDict = new DictAndKeyboard( new Dictionary(Dictionary.TYPE_MAIN) { + // TODO: this dummy dictionary should be a singleton in the Dictionary class. @Override public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index acd47450b..52012c846 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -66,7 +66,8 @@ public final class MoreSuggestions extends Keyboard { clearKeys(); mDivider = res.getDrawable(R.drawable.more_suggestions_divider); mDividerWidth = mDivider.getIntrinsicWidth(); - final float padding = res.getDimension(R.dimen.more_suggestions_key_horizontal_padding); + final float padding = res.getDimension( + R.dimen.config_more_suggestions_key_horizontal_padding); int row = 0; int index = fromIndex; @@ -75,7 +76,7 @@ public final class MoreSuggestions extends Keyboard { while (index < size) { final String word = suggestedWords.getWord(index); // TODO: Should take care of text x-scaling. - mWidths[index] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding); + mWidths[index] = (int)(TypefaceUtils.getStringWidth(word, paint) + padding); final int numColumn = index - rowStartIndex + 1; final int columnWidth = (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn; diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java index 0ebe37782..549ff0d9d 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java @@ -54,7 +54,7 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView { public void adjustVerticalCorrectionForModalMode() { // Set vertical correction to zero (Reset more keys keyboard sliding allowance - // {@link R#dimen.more_keys_keyboard_slide_allowance}). + // {@link R#dimen.config_more_keys_keyboard_slide_allowance}). mKeyDetector.setKeyboard(getKeyboard(), -getPaddingLeft(), -getPaddingTop()); } diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index faa5560e4..72281e62c 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -119,7 +119,8 @@ final class SuggestionStripLayoutHelper { mDividerWidth = dividerView.getMeasuredWidth(); final Resources res = wordView.getResources(); - mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); + mSuggestionsStripHeight = res.getDimensionPixelSize( + R.dimen.config_suggestions_strip_height); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripView); @@ -145,15 +146,17 @@ final class SuggestionStripLayoutHelper { a.recycle(); mMoreSuggestionsHint = getMoreSuggestionsHint(res, - res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect); + res.getDimension(R.dimen.config_more_suggestions_hint_text_size), + mColorAutoCorrect); mCenterPositionInStrip = mSuggestionsCountInStrip / 2; // Assuming there are at least three suggestions. Also, note that the suggestions are // laid out according to script direction, so this is left of the center for LTR scripts // and right of the center for RTL scripts. mTypedWordPositionWhenAutocorrect = mCenterPositionInStrip - 1; mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( - R.dimen.more_suggestions_bottom_gap); - mMoreSuggestionsRowHeight = res.getDimensionPixelSize(R.dimen.more_suggestions_row_height); + R.dimen.config_more_suggestions_bottom_gap); + mMoreSuggestionsRowHeight = res.getDimensionPixelSize( + R.dimen.config_more_suggestions_row_height); final LayoutInflater inflater = LayoutInflater.from(context); mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 75f17c559..aa87affa2 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -112,7 +112,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick final Resources res = context.getResources(); mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset( - R.dimen.more_suggestions_modal_tolerance); + R.dimen.config_more_suggestions_modal_tolerance); mMoreSuggestionsSlidingDetector = new GestureDetector( context, mMoreSuggestionsSlidingListener); } diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index d87f6f3c4..ef1d0f42c 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -17,21 +17,25 @@ package com.android.inputmethod.latin.utils; import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; +import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.EMOJI_CAPABLE; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.IS_ADDITIONAL_SUBTYPE; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET; import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME; import android.os.Build; import android.text.TextUtils; +import android.util.Log; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; -import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import java.util.ArrayList; +import java.util.Arrays; public final class AdditionalSubtypeUtils { + private static final String TAG = AdditionalSubtypeUtils.class.getSimpleName(); + private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0]; private AdditionalSubtypeUtils() { @@ -43,6 +47,11 @@ public final class AdditionalSubtypeUtils { } private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":"; + private static final int INDEX_OF_LOCALE = 0; + private static final int INDEX_OF_KEYBOARD_LAYOUT = 1; + private static final int INDEX_OF_EXTRA_VALUE = 2; + private static final int LENGTH_WITHOUT_EXTRA_VALUE = (INDEX_OF_KEYBOARD_LAYOUT + 1); + private static final int LENGTH_WITH_EXTRA_VALUE = (INDEX_OF_EXTRA_VALUE + 1); private static final String PREF_SUBTYPE_SEPARATOR = ";"; public static InputMethodSubtype createAdditionalSubtype(final String localeString, @@ -79,17 +88,6 @@ public final class AdditionalSubtypeUtils { : basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue; } - public static InputMethodSubtype createAdditionalSubtype(final String prefSubtype) { - final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); - if (elems.length < 2 || elems.length > 3) { - throw new RuntimeException("Unknown additional subtype specified: " + prefSubtype); - } - final String localeString = elems[0]; - final String keyboardLayoutSetName = elems[1]; - final String extraValue = (elems.length == 3) ? elems[2] : null; - return createAdditionalSubtype(localeString, keyboardLayoutSetName, extraValue); - } - public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) { if (TextUtils.isEmpty(prefSubtypes)) { return EMPTY_SUBTYPE_ARRAY; @@ -98,7 +96,19 @@ public final class AdditionalSubtypeUtils { final ArrayList<InputMethodSubtype> subtypesList = CollectionUtils.newArrayList(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { - final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype); + final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); + if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE + && elems.length != LENGTH_WITH_EXTRA_VALUE) { + Log.w(TAG, "Unknown additional subtype specified: " + prefSubtype + " in " + + prefSubtypes); + continue; + } + final String localeString = elems[INDEX_OF_LOCALE]; + final String keyboardLayoutSetName = elems[INDEX_OF_KEYBOARD_LAYOUT]; + final String extraValue = (elems.length == LENGTH_WITH_EXTRA_VALUE) + ? elems[INDEX_OF_EXTRA_VALUE] : null; + final InputMethodSubtype subtype = createAdditionalSubtype( + localeString, keyboardLayoutSetName, extraValue); if (subtype.getNameResId() == SubtypeLocaleUtils.UNKNOWN_KEYBOARD_LAYOUT) { // Skip unknown keyboard layout subtype. This may happen when predefined keyboard // layout has been removed. @@ -137,31 +147,36 @@ public final class AdditionalSubtypeUtils { return sb.toString(); } - private static InputMethodSubtype buildInputMethodSubtype(int nameId, String localeString, - String layoutExtraValue, String additionalSubtypeExtraValue) { - // CAVEAT! If you want to change subtypeId after changing the extra values, - // you must change "getInputMethodSubtypeId". But it will remove the additional keyboard - // from the current users. So, you should be really careful to change it. - final int subtypeId = getInputMethodSubtypeId(nameId, localeString, layoutExtraValue, - additionalSubtypeExtraValue); + private static InputMethodSubtype buildInputMethodSubtype(final int nameId, + final String localeString, final String layoutExtraValue, + final String additionalSubtypeExtraValue) { + // To preserve additional subtype settings and user's selection across OS updates, subtype + // id shouldn't be changed. New attributes, such as emojiCapable, are carefully excluded + // from the calculation of subtype id. + final String compatibleExtraValue = StringUtils.joinCommaSplittableText( + layoutExtraValue, additionalSubtypeExtraValue); + final int compatibleSubtypeId = getInputMethodSubtypeId(localeString, compatibleExtraValue); final String extraValue; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue - + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE - + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE; + // Color Emoji is supported from KitKat. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + extraValue = StringUtils.appendToCommaSplittableTextIfNotExists( + EMOJI_CAPABLE, compatibleExtraValue); } else { - extraValue = layoutExtraValue + "," + additionalSubtypeExtraValue; + extraValue = compatibleExtraValue; } return InputMethodSubtypeCompatUtils.newInputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, localeString, KEYBOARD_MODE, extraValue, - false, false, subtypeId); + false, false, compatibleSubtypeId); } - private static int getInputMethodSubtypeId(int nameId, String localeString, - String layoutExtraValue, String additionalSubtypeExtraValue) { - // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19. - return (new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark, - localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue, - false, false)).hashCode(); + private static int getInputMethodSubtypeId(final String localeString, final String extraValue) { + // From the compatibility point of view, the calculation of subtype id has been copied from + // {@link InputMethodSubtype} of JellyBean MR2. + return Arrays.hashCode(new Object[] { + localeString, + KEYBOARD_MODE, + extraValue, + false /* isAuxiliary */, + false /* overrideImplicitlyEnabledSubtype */ }); } } diff --git a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java index 08a2a8c5a..e521ec807 100644 --- a/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ApplicationUtils.java @@ -62,4 +62,22 @@ public final class ApplicationUtils { } return ""; } + + /** + * A utility method to get the application's PackageInfo.versionCode + * @return the application's PackageInfo.versionCode + */ + public static int getVersionCode(final Context context) { + try { + if (context == null) { + return 0; + } + final String packageName = context.getPackageName(); + final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + return info.versionCode; + } catch (final NameNotFoundException e) { + Log.e(TAG, "Could not find version info.", e); + } + return 0; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java index c2e97a36f..d12aad639 100644 --- a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java +++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java @@ -20,7 +20,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** - * This class is a holder of a result of asynchronous computation. + * This class is a holder of the result of an asynchronous computation. * * @param <E> the type of the result. */ @@ -36,9 +36,9 @@ public class AsyncResultHolder<E> { } /** - * Sets the result value to this holder. + * Sets the result value of this holder. * - * @param result the value which is set. + * @param result the value to set. */ public void set(final E result) { synchronized(mLock) { @@ -54,12 +54,12 @@ public class AsyncResultHolder<E> { * Causes the current thread to wait unless the value is set or the specified time is elapsed. * * @param defaultValue the default value. - * @param timeOut the time to wait. - * @return if the result is set until the time limit then the result, otherwise defaultValue. + * @param timeOut the maximum time to wait. + * @return if the result is set before the time limit then the result, otherwise defaultValue. */ public E get(final E defaultValue, final long timeOut) { try { - if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) { + if (mLatch.await(timeOut, TimeUnit.MILLISECONDS)) { return mResult; } else { return defaultValue; diff --git a/java/src/com/android/inputmethod/latin/utils/FileUtils.java b/java/src/com/android/inputmethod/latin/utils/FileUtils.java new file mode 100644 index 000000000..83c1e7c4d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/FileUtils.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.utils; + +import java.io.File; + +/** + * A simple class to help with removing directories recursively. + */ +public class FileUtils { + public static boolean deleteRecursively(final File path) { + if (path.isDirectory()) { + for (final File child : path.listFiles()) { + deleteRecursively(child); + } + } + return path.delete(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java new file mode 100644 index 000000000..764ef72ce --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.util.JsonReader; +import android.util.JsonWriter; +import android.util.Log; + +import java.io.Closeable; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public final class JsonUtils { + private static final String TAG = JsonUtils.class.getSimpleName(); + + private static final String INTEGER_CLASS_NAME = Integer.class.getSimpleName(); + private static final String STRING_CLASS_NAME = String.class.getSimpleName(); + + private static final String EMPTY_STRING = ""; + + public static List<Object> jsonStrToList(final String s) { + final ArrayList<Object> list = 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_NAME)) { + list.add(reader.nextInt()); + } else if (name.equals(STRING_CLASS_NAME)) { + list.add(reader.nextString()); + } else { + Log.w(TAG, "Invalid name: " + name); + reader.skipValue(); + } + } + reader.endObject(); + } + reader.endArray(); + return list; + } catch (final IOException e) { + } finally { + close(reader); + } + return Collections.<Object>emptyList(); + } + + public static String listToJsonStr(final List<Object> list) { + if (list == null || list.isEmpty()) { + return EMPTY_STRING; + } + 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_NAME).value((Integer)o); + } else if (o instanceof String) { + writer.name(STRING_CLASS_NAME).value((String)o); + } + writer.endObject(); + } + writer.endArray(); + return sw.toString(); + } catch (final IOException e) { + } finally { + close(writer); + } + return EMPTY_STRING; + } + + private static void close(final Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (final IOException e) { + // Ignore + } + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java index e958a7e71..d14ba508b 100644 --- a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java @@ -35,7 +35,7 @@ public final class LatinImeLoggerUtils { public static void onSeparator(final int code, final int x, final int y) { // Helper method to log a single code point separator // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils - onSeparator(new String(new int[]{code}, 0, 1), x, y); + onSeparator(StringUtils.newSingleCodePointString(code), x, y); } public static void onSeparator(final String separator, final int x, final int y) { diff --git a/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java index 44e5d17b4..8469c87b0 100644 --- a/java/src/com/android/inputmethod/latin/utils/StaticInnerHandlerWrapper.java +++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java @@ -21,22 +21,22 @@ import android.os.Looper; import java.lang.ref.WeakReference; -public class StaticInnerHandlerWrapper<T> extends Handler { - private final WeakReference<T> mOuterInstanceRef; +public class LeakGuardHandlerWrapper<T> extends Handler { + private final WeakReference<T> mOwnerInstanceRef; - public StaticInnerHandlerWrapper(final T outerInstance) { - this(outerInstance, Looper.myLooper()); + public LeakGuardHandlerWrapper(final T ownerInstance) { + this(ownerInstance, Looper.myLooper()); } - public StaticInnerHandlerWrapper(final T outerInstance, final Looper looper) { + public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) { super(looper); - if (outerInstance == null) { - throw new NullPointerException("outerInstance is null"); + if (ownerInstance == null) { + throw new NullPointerException("ownerInstance is null"); } - mOuterInstanceRef = new WeakReference<T>(outerInstance); + mOwnerInstanceRef = new WeakReference<T>(ownerInstance); } - public T getOuterInstance() { - return mOuterInstanceRef.get(); + public T getOwnerInstance() { + return mOwnerInstanceRef.get(); } } diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java index 22045aa38..0c55484b4 100644 --- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java @@ -30,9 +30,6 @@ import java.util.Locale; * dictionary pack. */ public final class LocaleUtils { - private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap(); - private static final String LOCALE_AND_TIME_STR_SEPARATER = ","; - private LocaleUtils() { // Intentional empty constructor for utility class. } @@ -168,12 +165,14 @@ public final class LocaleUtils { * Creates a locale from a string specification. */ public static Locale constructLocaleFromString(final String localeStr) { - if (localeStr == null) + if (localeStr == null) { return null; + } synchronized (sLocaleCache) { - if (sLocaleCache.containsKey(localeStr)) - return sLocaleCache.get(localeStr); - Locale retval = null; + Locale retval = sLocaleCache.get(localeStr); + if (retval != null) { + return retval; + } String[] localeParams = localeStr.split("_", 3); if (localeParams.length == 1) { retval = new Locale(localeParams[0]); @@ -188,38 +187,4 @@ public final class LocaleUtils { return retval; } } - - public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) { - if (TextUtils.isEmpty(str)) { - return EMPTY_LT_HASH_MAP; - } - final String[] ss = str.split(LOCALE_AND_TIME_STR_SEPARATER); - final int N = ss.length; - if (N < 2 || N % 2 != 0) { - return EMPTY_LT_HASH_MAP; - } - final HashMap<String, Long> retval = CollectionUtils.newHashMap(); - for (int i = 0; i < N / 2; ++i) { - final String localeStr = ss[i * 2]; - final long time = Long.valueOf(ss[i * 2 + 1]); - retval.put(localeStr, time); - } - return retval; - } - - public static String localeAndTimeHashMapToStr(HashMap<String, Long> map) { - if (map == null || map.isEmpty()) { - return ""; - } - final StringBuilder builder = new StringBuilder(); - for (String localeStr : map.keySet()) { - if (builder.length() > 0) { - builder.append(LOCALE_AND_TIME_STR_SEPARATER); - } - final Long time = map.get(localeStr); - builder.append(localeStr).append(LOCALE_AND_TIME_STR_SEPARATER); - builder.append(String.valueOf(time)); - } - return builder.toString(); - } } diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index 22c92446a..deb28a08d 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -227,19 +227,19 @@ public final class ResourceUtils { final String keyboardHeightString = getDeviceOverrideValue(res, R.array.keyboard_heights); final float keyboardHeight; if (TextUtils.isEmpty(keyboardHeightString)) { - keyboardHeight = res.getDimension(R.dimen.keyboardHeight); + keyboardHeight = res.getDimension(R.dimen.config_default_keyboard_height); } else { keyboardHeight = Float.parseFloat(keyboardHeightString) * dm.density; } final float maxKeyboardHeight = res.getFraction( - R.fraction.maxKeyboardHeight, dm.heightPixels, dm.heightPixels); + R.fraction.config_max_keyboard_height, dm.heightPixels, dm.heightPixels); float minKeyboardHeight = res.getFraction( - R.fraction.minKeyboardHeight, dm.heightPixels, dm.heightPixels); + R.fraction.config_min_keyboard_height, dm.heightPixels, dm.heightPixels); if (minKeyboardHeight < 0.0f) { // Specified fraction was negative, so it should be calculated against display // width. minKeyboardHeight = -res.getFraction( - R.fraction.minKeyboardHeight, dm.widthPixels, dm.widthPixels); + R.fraction.config_min_keyboard_height, dm.widthPixels, dm.widthPixels); } // Keyboard height will not exceed maxKeyboardHeight and will not be less than // minKeyboardHeight. diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index a36548392..df420417d 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -16,20 +16,15 @@ package com.android.inputmethod.latin.utils; +import android.text.TextUtils; +import android.util.Log; + import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.settings.SettingsValues; -import android.text.TextUtils; -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; @@ -39,6 +34,8 @@ public final class StringUtils { public static final int CAPITALIZE_FIRST = 1; // First only public static final int CAPITALIZE_ALL = 2; // All caps + private static final String EMPTY_STRING = ""; + private StringUtils() { // This utility class is not publicly instantiable. } @@ -80,6 +77,20 @@ public final class StringUtils { return containsInArray(text, extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT)); } + public static String joinCommaSplittableText(final String head, final String tail) { + if (TextUtils.isEmpty(head) && TextUtils.isEmpty(tail)) { + return EMPTY_STRING; + } + // Here either head or tail is not null. + if (TextUtils.isEmpty(head)) { + return tail; + } + if (TextUtils.isEmpty(tail)) { + return head; + } + return head + SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT + tail; + } + public static String appendToCommaSplittableTextIfNotExists(final String text, final String extraValues) { if (TextUtils.isEmpty(extraValues)) { @@ -94,7 +105,7 @@ public final class StringUtils { public static String removeFromCommaSplittableTextIfExists(final String text, final String extraValues) { if (TextUtils.isEmpty(extraValues)) { - return ""; + return EMPTY_STRING; } final String[] elements = extraValues.split(SEPARATOR_FOR_COMMA_SPLITTABLE_TEXT); if (!containsInArray(text, elements)) { @@ -367,7 +378,7 @@ public final class StringUtils { return false; } - public static boolean isEmptyStringOrWhiteSpaces(String s) { + public static boolean isEmptyStringOrWhiteSpaces(final String s) { final int N = codePointCount(s); for (int i = 0; i < N; ++i) { if (!Character.isWhitespace(s.codePointAt(i))) { @@ -378,9 +389,9 @@ public final class StringUtils { } @UsedForTesting - public static String byteArrayToHexString(byte[] bytes) { + public static String byteArrayToHexString(final byte[] bytes) { if (bytes == null || bytes.length == 0) { - return ""; + return EMPTY_STRING; } final StringBuilder sb = new StringBuilder(); for (byte b : bytes) { @@ -393,7 +404,7 @@ public final class StringUtils { * Convert hex string to byte array. The string length must be an even number. */ @UsedForTesting - public static byte[] hexStringToByteArray(String hexString) { + public static byte[] hexStringToByteArray(final String hexString) { if (TextUtils.isEmpty(hexString)) { return null; } @@ -409,67 +420,4 @@ 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 ""; - } } diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 102a41b4e..fdbe81ab6 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -197,7 +197,9 @@ public final class SubtypeLocaleUtils { // es_US spanish F Español (EE.UU.) exception // fr azerty F Français // fr_CA qwerty F Français (Canada) + // fr_CH swiss F Français (Suisse) // de qwertz F Deutsch + // de_CH swiss T Deutsch (Schweiz) // zz qwerty F No language (QWERTY) in system locale // fr qwertz T Français (QWERTZ) // de qwerty T Deutsch (QWERTY) @@ -298,7 +300,9 @@ public final class SubtypeLocaleUtils { // es_US spanish F Es Español Español (EE.UU.) exception // fr azerty F Fr Français Français // fr_CA qwerty F Fr Français Français (Canada) + // fr_CH swiss F Fr Français Français (Suisse) // de qwertz F De Deutsch Deutsch + // de_CH swiss T De Deutsch Deutsch (Schweiz) // zz qwerty F QWERTY QWERTY // fr qwertz T Fr Français Français // de qwerty T De Deutsch Deutsch diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index 47ea1ea75..087a7f255 100644 --- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -22,6 +22,9 @@ import android.graphics.Typeface; import android.util.SparseArray; public final class TypefaceUtils { + private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' }; + private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' }; + private TypefaceUtils() { // This utility class is not publicly instantiable. } @@ -31,7 +34,7 @@ public final class TypefaceUtils { // Working variable for the following method. private static final Rect sTextHeightBounds = new Rect(); - public static float getCharHeight(final char[] referenceChar, final Paint paint) { + private static float getCharHeight(final char[] referenceChar, final Paint paint) { final int key = getCharGeometryCacheKey(referenceChar[0], paint); synchronized (sTextHeightCache) { final Float cachedValue = sTextHeightCache.get(key); @@ -51,7 +54,7 @@ public final class TypefaceUtils { // Working variable for the following method. private static final Rect sTextWidthBounds = new Rect(); - public static float getCharWidth(final char[] referenceChar, final Paint paint) { + private static float getCharWidth(final char[] referenceChar, final Paint paint) { final int key = getCharGeometryCacheKey(referenceChar[0], paint); synchronized (sTextWidthCache) { final Float cachedValue = sTextWidthCache.get(key); @@ -66,11 +69,6 @@ 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(); @@ -86,9 +84,25 @@ public final class TypefaceUtils { } } - public static float getLabelWidth(final String label, final Paint paint) { - final Rect textBounds = new Rect(); - paint.getTextBounds(label, 0, label.length(), textBounds); - return textBounds.width(); + public static float getReferenceCharHeight(final Paint paint) { + return getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint); + } + + public static float getReferenceCharWidth(final Paint paint) { + return getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint); + } + + public static float getReferenceDigitWidth(final Paint paint) { + return getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint); + } + + // Working variable for the following method. + private static final Rect sStringWidthBounds = new Rect(); + + public static float getStringWidth(final String string, final Paint paint) { + synchronized (sStringWidthBounds) { + paint.getTextBounds(string, 0, string.length(), sStringWidthBounds); + return sStringWidthBounds.width(); + } } } diff --git a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java b/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java new file mode 100644 index 000000000..4feee4393 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.inputmethod.latin.utils; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + +import java.util.ArrayList; + +// This has information that belong to a unigram. This class has some detailed attributes such as +// historical information but they have to be checked only for testing purpose. +@UsedForTesting +public class UnigramProperty { + public final String mCodePoints; + public final boolean mIsNotAWord; + public final boolean mIsBlacklisted; + public final boolean mHasBigrams; + public final boolean mHasShortcuts; + public final int mProbability; + // mTimestamp, mLevel and mCount are historical info. These values are depend on the + // implementation in native code; thus, we must not use them and have any assumptions about + // them except for tests. + public final int mTimestamp; + public final int mLevel; + public final int mCount; + public final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList(); + + private static int getCodePointCount(final int[] codePoints) { + for (int i = 0; i < codePoints.length; i++) { + if (codePoints[i] == 0) { + return i; + } + } + return codePoints.length; + } + + // This represents invalid unigram when the probability is BinaryDictionary.NOT_A_PROBABILITY. + public UnigramProperty(final int[] codePoints, final boolean isNotAWord, + final boolean isBlacklisted, final boolean hasBigram, + final boolean hasShortcuts, final int probability, final int timestamp, + final int level, final int count, final ArrayList<int[]> shortcutTargets, + final ArrayList<Integer> shortcutProbabilities) { + mCodePoints = new String(codePoints, 0 /* offset */, getCodePointCount(codePoints)); + mIsNotAWord = isNotAWord; + mIsBlacklisted = isBlacklisted; + mHasBigrams = hasBigram; + mHasShortcuts = hasShortcuts; + mProbability = probability; + mTimestamp = timestamp; + mLevel = level; + mCount = count; + final int shortcutTargetCount = shortcutTargets.size(); + for (int i = 0; i < shortcutTargetCount; i++) { + final int[] shortcutTargetCodePointArray = shortcutTargets.get(i); + final String shortcutTargetString = new String(shortcutTargetCodePointArray, + 0 /* offset */, getCodePointCount(shortcutTargetCodePointArray)); + mShortcutTargets.add( + new WeightedString(shortcutTargetString, shortcutProbabilities.get(i))); + } + } + + @UsedForTesting + public boolean isValid() { + return mProbability != BinaryDictionary.NOT_A_PROBABILITY; + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java index 635afe7cc..a2c3ed44d 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java @@ -70,10 +70,11 @@ public final class UserHistoryDictIOUtils { /** * Writes dictionary to file. */ + @UsedForTesting public static void writeDictionary(final DictEncoder dictEncoder, final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams, - final FormatOptions formatOptions) { - final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams); + final FormatOptions formatOptions, final HashMap<String, String> options) { + final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams, options); fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE); fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); @@ -91,10 +92,10 @@ public final class UserHistoryDictIOUtils { * Constructs a new FusionDictionary from BigramDictionaryInterface. */ @UsedForTesting - static FusionDictionary constructFusionDictionary( - final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) { + static FusionDictionary constructFusionDictionary(final BigramDictionaryInterface dict, + final UserHistoryDictionaryBigramList bigrams, final HashMap<String, String> options) { final FusionDictionary fusionDict = new FusionDictionary(new PtNodeArray(), - new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false, + new FusionDictionary.DictionaryOptions(options, false, false)); int profTotal = 0; for (final String word1 : bigrams.keySet()) { diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java index 1992b2f5d..677035ed6 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java @@ -20,6 +20,9 @@ import android.util.Log; import java.util.concurrent.TimeUnit; +import com.android.inputmethod.annotations.UsedForTesting; + +@UsedForTesting public final class UserHistoryForgettingCurveUtils { private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName(); private static final boolean DEBUG = false; @@ -118,18 +121,22 @@ public final class UserHistoryForgettingCurveUtils { } } + @UsedForTesting /* package */ static int fcToElapsedTime(byte fc) { return fc & 0x0F; } + @UsedForTesting /* package */ static int fcToCount(byte fc) { return (fc >> 4) & 0x03; } + @UsedForTesting /* package */ static int fcToLevel(byte fc) { return (fc >> 6) & 0x03; } + @UsedForTesting private static int calcFreq(int elapsedTime, int count, int level) { if (level <= 0) { // Reserved words, just return -1 @@ -158,6 +165,7 @@ public final class UserHistoryForgettingCurveUtils { return calcFreq(elapsedTime, count, level); } + @UsedForTesting public static byte pushElapsedTime(byte fc) { int elapsedTime = fcToElapsedTime(fc); int count = fcToCount(fc); @@ -173,6 +181,7 @@ public final class UserHistoryForgettingCurveUtils { return calcFc(elapsedTime, count, level); } + @UsedForTesting public static byte pushCount(byte fc, boolean isValid) { final int elapsedTime = fcToElapsedTime(fc); int count = fcToCount(fc); diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index da9c61103..28a647b02 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -102,10 +102,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; private static final boolean DEBUG_REPLAY_AFTER_FEEDBACK = false && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; - // Whether the TextView contents are logged at the end of the session. true will disclose - // private info. - private static final boolean LOG_FULL_TEXTVIEW_CONTENTS = false - && ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG; // Whether the feedback dialog preserves the editable text across invocations. Should be false // for normal research builds so users do not have to delete the same feedback string they // entered earlier. Should be true for builds internal to a development team so when the text @@ -168,9 +164,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // U+E001 is in the "private-use area" /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001"; protected static final int SUSPEND_DURATION_IN_MINUTES = 1; - // set when LatinIME should ignore an onUpdateSelection() callback that - // arises from operations in this class - private static boolean sLatinIMEExpectingUpdateSelection = false; // used to check whether words are not unique private Suggest mSuggest; @@ -1126,12 +1119,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang new Object[] { applicationSpecifiedCompletions }); } - public static boolean getAndClearLatinIMEExpectingUpdateSelection() { - boolean returnValue = sLatinIMEExpectingUpdateSelection; - sLatinIMEExpectingUpdateSelection = false; - return returnValue; - } - /** * The IME is finishing; it is either being destroyed, or is about to be hidden. * @@ -1149,51 +1136,12 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // if called from finishViews(), which is called from hideWindow() and onDestroy(). These // are the situations in which we want to finish up the researchLog. if (ic != null && !finishingInput) { - final boolean isTextTruncated; - final String text; - if (LOG_FULL_TEXTVIEW_CONTENTS) { - // Capture the TextView contents. This will trigger onUpdateSelection(), so we - // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called, - // it can tell that it was generated by the logging code, and not by the user, and - // therefore keep user-visible state as is. - ic.beginBatchEdit(); - ic.performContextMenuAction(android.R.id.selectAll); - CharSequence charSequence = ic.getSelectedText(0); - if (savedSelectionStart != -1 && savedSelectionEnd != -1) { - ic.setSelection(savedSelectionStart, savedSelectionEnd); - } - ic.endBatchEdit(); - sLatinIMEExpectingUpdateSelection = true; - if (TextUtils.isEmpty(charSequence)) { - isTextTruncated = false; - text = ""; - } else { - if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) { - int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE; - // do not cut in the middle of a supplementary character - final char c = charSequence.charAt(length - 1); - if (Character.isHighSurrogate(c)) { - length--; - } - final CharSequence truncatedCharSequence = charSequence.subSequence(0, - length); - isTextTruncated = true; - text = truncatedCharSequence.toString(); - } else { - isTextTruncated = false; - text = charSequence.toString(); - } - } - } else { - isTextTruncated = true; - text = ""; - } final ResearchLogger researchLogger = getInstance(); // Assume that OUTPUT_ENTIRE_BUFFER is only true when we don't care about privacy (e.g. // during a live user test), so the normal isPotentiallyPrivate and // isPotentiallyRevealing flags do not apply researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONFINISHINPUTVIEWINTERNAL, - isTextTruncated, text); + true /* isTextTruncated */, "" /* text */); researchLogger.commitCurrentLogUnit(); getInstance().stop(); } @@ -1213,9 +1161,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onUpdateSelection(final int lastSelectionStart, final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd, final int newSelStart, final int newSelEnd, final int composingSpanStart, - final int composingSpanEnd, final boolean expectingUpdateSelection, - final boolean expectingUpdateSelectionFromLogger, - final RichInputConnection connection) { + final int composingSpanEnd, final RichInputConnection connection) { String word = ""; if (connection != null) { TextRange range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1); @@ -1227,8 +1173,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang final String scrubbedWord = researchLogger.scrubWord(word); researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONUPDATESELECTION, lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, - composingSpanStart, composingSpanEnd, expectingUpdateSelection, - expectingUpdateSelectionFromLogger, scrubbedWord); + composingSpanStart, composingSpanEnd, false /* expectingUpdateSelection */, + false /* expectingUpdateSelectionFromLogger */, scrubbedWord); } /** @@ -1411,8 +1357,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private static final LogStatement LOGSTATEMENT_MAINKEYBOARDVIEW_SETKEYBOARD = new LogStatement("MainKeyboardViewSetKeyboard", false, false, "elementId", "locale", "orientation", "width", "modeName", "action", "navigateNext", - "navigatePrevious", "clobberSettingsKey", "passwordInput", "shortcutKeyEnabled", - "hasShortcutKey", "languageSwitchKeyEnabled", "isMultiLine", "tw", "th", + "navigatePrevious", "clobberSettingsKey", "passwordInput", + "supportsSwitchingToShortcutIme", "hasShortcutKey", "languageSwitchKeyEnabled", + "isMultiLine", "tw", "th", "keys"); public static void mainKeyboardView_setKeyboard(final Keyboard keyboard, final int orientation) { @@ -1425,7 +1372,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), orientation, kid.mWidth, KeyboardId.modeName(kid.mMode), kid.imeAction(), kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey, - isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey, + isPasswordView, kid.mSupportsSwitchingToShortcutIme, kid.mHasShortcutKey, kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth, keyboard.mOccupiedHeight, keyboard.getKeys()); } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index ca6a77997..e11e706f3 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -57,7 +57,9 @@ LATIN_IME_CORE_SRC_FILES := \ bloom_filter.cpp \ dictionary.cpp \ digraph_utils.cpp \ - multi_bigram_map.cpp) \ + error_type_utils.cpp \ + multi_bigram_map.cpp \ + unigram_property.cpp) \ $(addprefix suggest/core/layout/, \ additional_proximity_chars.cpp \ proximity_info.cpp \ @@ -67,27 +69,45 @@ LATIN_IME_CORE_SRC_FILES := \ suggest/core/policy/weighting.cpp \ suggest/core/session/dic_traverse_session.cpp \ $(addprefix suggest/policyimpl/dictionary/, \ - bigram/bigram_list_read_write_utils.cpp \ - bigram/dynamic_bigram_list_policy.cpp \ header/header_policy.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 \ - dynamic_patricia_trie_reading_utils.cpp \ - dynamic_patricia_trie_writing_helper.cpp \ - dynamic_patricia_trie_writing_utils.cpp \ + structure/dictionary_structure_with_buffer_policy_factory.cpp) \ + $(addprefix suggest/policyimpl/dictionary/bigram/, \ + bigram_list_read_write_utils.cpp \ + ver4_bigram_list_policy.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/pt_common/, \ + dynamic_pt_gc_event_listeners.cpp \ + dynamic_pt_reading_helper.cpp \ + dynamic_pt_reading_utils.cpp \ + dynamic_pt_updating_helper.cpp \ + dynamic_pt_writing_utils.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/v2/, \ patricia_trie_policy.cpp \ patricia_trie_reading_utils.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/v4/, \ + ver4_dict_buffers.cpp \ + ver4_dict_constants.cpp \ + ver4_patricia_trie_node_reader.cpp \ + ver4_patricia_trie_node_writer.cpp \ + ver4_patricia_trie_policy.cpp \ + ver4_patricia_trie_reading_utils.cpp \ + ver4_patricia_trie_writing_helper.cpp) \ + $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \ + bigram_dict_content.cpp \ + probability_dict_content.cpp \ + shortcut_dict_content.cpp \ + sparse_table_dict_content.cpp \ + terminal_position_lookup_table.cpp) \ $(addprefix suggest/policyimpl/dictionary/utils/, \ buffer_with_extendable_buffer.cpp \ byte_array_utils.cpp \ dict_file_writing_utils.cpp \ + file_utils.cpp \ forgetting_curve_utils.cpp \ - format_utils.cpp) \ + format_utils.cpp \ + mmapped_buffer.cpp \ + sparse_table.cpp) \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ $(addprefix suggest/policyimpl/typing/, \ scoring_params.cpp \ @@ -98,7 +118,8 @@ LATIN_IME_CORE_SRC_FILES := \ $(addprefix utils/, \ autocorrection_threshold_utils.cpp \ char_utils.cpp \ - log_utils.cpp) + log_utils.cpp \ + time_keeper.cpp) LOCAL_SRC_FILES := \ $(LATIN_IME_JNI_SRC_FILES) \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 8f21c50ec..716bda5a7 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -24,8 +24,9 @@ #include "jni.h" #include "jni_common.h" #include "suggest/core/dictionary/dictionary.h" +#include "suggest/core/dictionary/unigram_property.h" #include "suggest/core/suggest_options.h" -#include "suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h" +#include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h" #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include "utils/autocorrection_threshold_utils.h" @@ -86,11 +87,11 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s char sourceDirChars[sourceDirUtf8Length + 1]; env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars); sourceDirChars[sourceDirUtf8Length] = '\0'; - DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy = + DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy = DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy( sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize), isUpdatable == JNI_TRUE); - if (!dictionaryStructureWithBufferPolicy) { + if (!dictionaryStructureWithBufferPolicy.get()) { return 0; } @@ -135,6 +136,12 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dic delete dictionary; } +static int latinime_BinaryDictionary_getFormatVersion(JNIEnv *env, jclass clazz, jlong dict) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) return 0; + return dictionary->getFormatVersionNumber(); +} + static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict, jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, @@ -252,6 +259,21 @@ static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass c word1Length); } +static void latinime_BinaryDictionary_getUnigramProperty(JNIEnv *env, jclass clazz, + jlong dict, jintArray word, jintArray outCodePoints, jbooleanArray outFlags, + jintArray outProbability, jintArray outHistoricalInfo, jobject outShortcutTargets, + jobject outShortcutProbabilities) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) return; + const jsize wordLength = env->GetArrayLength(word); + int wordCodePoints[wordLength]; + env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints); + const UnigramProperty unigramProperty = dictionary->getUnigramProperty( + wordCodePoints, wordLength); + unigramProperty.outputProperties(env, outCodePoints, outFlags, outProbability, + outHistoricalInfo, outShortcutTargets, outShortcutProbabilities); +} + static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz, jintArray before, jintArray after, jint score) { jsize beforeLength = env->GetArrayLength(before); @@ -277,7 +299,8 @@ static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jclass clazz, ji } static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict, - jintArray word, jint probability) { + jintArray word, jint probability, jintArray shortcutTarget, jint shortuctProbability, + jboolean isNotAWord, jboolean isBlacklisted, jint timestamp) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { return; @@ -285,11 +308,17 @@ static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jsize wordLength = env->GetArrayLength(word); int codePoints[wordLength]; env->GetIntArrayRegion(word, 0, wordLength, codePoints); - dictionary->addUnigramWord(codePoints, wordLength, probability); + jsize shortcutLength = shortcutTarget ? env->GetArrayLength(shortcutTarget) : 0; + int shortcutTargetCodePoints[shortcutLength]; + if (shortcutTarget) { + env->GetIntArrayRegion(shortcutTarget, 0, shortcutLength, shortcutTargetCodePoints); + } + dictionary->addUnigramWord(codePoints, wordLength, probability, shortcutTargetCodePoints, + shortcutLength, shortuctProbability, isNotAWord, isBlacklisted, timestamp); } static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict, - jintArray word0, jintArray word1, jint probability) { + jintArray word0, jintArray word1, jint probability, jint timestamp) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); if (!dictionary) { return; @@ -301,7 +330,7 @@ static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, int word1CodePoints[word1Length]; env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, - word1Length, probability); + word1Length, probability, timestamp); } static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict, @@ -320,6 +349,87 @@ static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass claz word1Length); } +// Returns how many language model params are processed. +static int latinime_BinaryDictionary_addMultipleDictionaryEntries(JNIEnv *env, jclass clazz, + jlong dict, jobjectArray languageModelParams, jint startIndex) { + Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); + if (!dictionary) { + return 0; + } + jsize languageModelParamCount = env->GetArrayLength(languageModelParams); + if (languageModelParamCount == 0 || startIndex >= languageModelParamCount) { + return 0; + } + jobject languageModelParam = env->GetObjectArrayElement(languageModelParams, 0); + jclass languageModelParamClass = env->GetObjectClass(languageModelParam); + env->DeleteLocalRef(languageModelParam); + + jfieldID word0FieldId = env->GetFieldID(languageModelParamClass, "mWord0", "[I"); + jfieldID word1FieldId = env->GetFieldID(languageModelParamClass, "mWord1", "[I"); + jfieldID unigramProbabilityFieldId = + env->GetFieldID(languageModelParamClass, "mUnigramProbability", "I"); + jfieldID bigramProbabilityFieldId = + env->GetFieldID(languageModelParamClass, "mBigramProbability", "I"); + jfieldID timestampFieldId = + env->GetFieldID(languageModelParamClass, "mTimestamp", "I"); + jfieldID shortcutTargetFieldId = + env->GetFieldID(languageModelParamClass, "mShortcutTarget", "[I"); + jfieldID shortcutProbabilityFieldId = + env->GetFieldID(languageModelParamClass, "mShortcutProbability", "I"); + jfieldID isNotAWordFieldId = + env->GetFieldID(languageModelParamClass, "mIsNotAWord", "Z"); + jfieldID isBlacklistedFieldId = + env->GetFieldID(languageModelParamClass, "mIsBlacklisted", "Z"); + env->DeleteLocalRef(languageModelParamClass); + + for (int i = startIndex; i < languageModelParamCount; ++i) { + jobject languageModelParam = env->GetObjectArrayElement(languageModelParams, i); + // languageModelParam is a set of params for word1; thus, word1 cannot be null. On the + // other hand, word0 can be null and then it means the set of params doesn't contain bigram + // information. + jintArray word0 = static_cast<jintArray>( + env->GetObjectField(languageModelParam, word0FieldId)); + jsize word0Length = word0 ? env->GetArrayLength(word0) : 0; + int word0CodePoints[word0Length]; + if (word0) { + env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); + } + jintArray word1 = static_cast<jintArray>( + env->GetObjectField(languageModelParam, word1FieldId)); + jsize word1Length = env->GetArrayLength(word1); + int word1CodePoints[word1Length]; + env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); + jint unigramProbability = env->GetIntField(languageModelParam, unigramProbabilityFieldId); + jint timestamp = env->GetIntField(languageModelParam, timestampFieldId); + jboolean isNotAWord = env->GetBooleanField(languageModelParam, isNotAWordFieldId); + jboolean isBlacklisted = env->GetBooleanField(languageModelParam, isBlacklistedFieldId); + jintArray shortcutTarget = static_cast<jintArray>( + env->GetObjectField(languageModelParam, shortcutTargetFieldId)); + jsize shortcutLength = shortcutTarget ? env->GetArrayLength(shortcutTarget) : 0; + int shortcutTargetCodePoints[shortcutLength]; + if (shortcutTarget) { + env->GetIntArrayRegion(shortcutTarget, 0, shortcutLength, shortcutTargetCodePoints); + } + jint shortcutProbability = env->GetIntField(languageModelParam, shortcutProbabilityFieldId); + dictionary->addUnigramWord(word1CodePoints, word1Length, unigramProbability, + shortcutTargetCodePoints, shortcutLength, shortcutProbability, + isNotAWord, isBlacklisted, timestamp); + if (word0) { + jint bigramProbability = env->GetIntField(languageModelParam, bigramProbabilityFieldId); + dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, word1Length, + bigramProbability, timestamp); + } + if (dictionary->needsToRunGC(true /* mindsBlockByGC */)) { + return i + 1; + } + env->DeleteLocalRef(word0); + env->DeleteLocalRef(word1); + env->DeleteLocalRef(shortcutTarget); + env->DeleteLocalRef(languageModelParam); + } + return languageModelParamCount; +} + static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz, jlong dict, jint unigramProbability, jint bigramProbability) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); @@ -343,7 +453,7 @@ static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, static const int GET_PROPERTY_RESULT_LENGTH = 100; char resultChars[GET_PROPERTY_RESULT_LENGTH]; resultChars[0] = '\0'; - dictionary->getProperty(queryChars, resultChars, GET_PROPERTY_RESULT_LENGTH); + dictionary->getProperty(queryChars, queryUtf8Length, resultChars, GET_PROPERTY_RESULT_LENGTH); return env->NewStringUTF(resultChars); } @@ -364,6 +474,11 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_close) }, { + const_cast<char *>("getFormatVersionNative"), + const_cast<char *>("(J)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_getFormatVersion) + }, + { const_cast<char *>("flushNative"), const_cast<char *>("(JLjava/lang/String;)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_flush) @@ -394,6 +509,11 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability) }, { + const_cast<char *>("getUnigramPropertyNative"), + const_cast<char *>("(J[I[I[Z[I[ILjava/util/ArrayList;Ljava/util/ArrayList;)V"), + reinterpret_cast<void *>(latinime_BinaryDictionary_getUnigramProperty) + }, + { const_cast<char *>("calcNormalizedScoreNative"), const_cast<char *>("([I[II)F"), reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore) @@ -405,12 +525,12 @@ static const JNINativeMethod sMethods[] = { }, { const_cast<char *>("addUnigramWordNative"), - const_cast<char *>("(J[II)V"), + const_cast<char *>("(J[II[IIZZI)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord) }, { const_cast<char *>("addBigramWordsNative"), - const_cast<char *>("(J[I[II)V"), + const_cast<char *>("(J[I[III)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords) }, { @@ -419,6 +539,12 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords) }, { + const_cast<char *>("addMultipleDictionaryEntriesNative"), + const_cast<char *>( + "(J[Lcom/android/inputmethod/latin/BinaryDictionary$LanguageModelParam;I)I"), + reinterpret_cast<void *>(latinime_BinaryDictionary_addMultipleDictionaryEntries) + }, + { const_cast<char *>("calculateProbabilityNative"), const_cast<char *>("(JII)I"), reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative) diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 742e388e4..9a26fe051 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -298,6 +298,7 @@ static inline void prof_out(void) { #define NOT_AN_INDEX (-1) #define NOT_A_PROBABILITY (-1) #define NOT_A_DICT_POS (S_INT_MIN) +#define NOT_A_TIMESTAMP (-1) // A special value to mean the first word confidence makes no sense in this case, // e.g. this is not a multi-word suggestion. @@ -341,12 +342,21 @@ template<typename T> AK_FORCE_INLINE const T &max(const T &a, const T &b) { retu #define INPUTLENGTH_FOR_DEBUG (-1) #define MIN_OUTPUT_INDEX_FOR_DEBUG (-1) -#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ +#define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \ + TypeName() + +#define DISALLOW_COPY_CONSTRUCTOR(TypeName) \ + TypeName(const TypeName&) + +#define DISALLOW_ASSIGNMENT_OPERATOR(TypeName) \ void operator=(const TypeName&) +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + DISALLOW_COPY_CONSTRUCTOR(TypeName); \ + DISALLOW_ASSIGNMENT_OPERATOR(TypeName) + #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ - TypeName(); \ + DISALLOW_DEFAULT_CONSTRUCTOR(TypeName); \ DISALLOW_COPY_AND_ASSIGN(TypeName) // Used as a return value for character comparison @@ -392,24 +402,4 @@ typedef enum { // Create new word with space substitution CT_NEW_WORD_SPACE_SUBSTITUTION, } CorrectionType; - -// ErrorType is mainly decided by CorrectionType but it is also depending on if -// the correction has really been performed or not. -typedef enum { - // Substitution, omission and transposition - ET_EDIT_CORRECTION, - // Proximity error - ET_PROXIMITY_CORRECTION, - // Completion - ET_COMPLETION, - // New word - // TODO: Remove. - // A new word error should be an edit correction error or a proximity correction error. - ET_NEW_WORD, - // Treat error as an intentional omission when the CorrectionType is omission and the node can - // be intentional omission. - ET_INTENTIONAL_OMISSION, - // Not treated as an error. Tracked for checking exact match - ET_NOT_AN_ERROR -} ErrorType; #endif // LATINIME_DEFINES_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 49cfdecac..0b2b4a9e8 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -99,7 +99,7 @@ class DicNode { virtual ~DicNode() {} // Init for copy - void initByCopy(const DicNode *dicNode) { + void initByCopy(const DicNode *const dicNode) { mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.init(&dicNode->mDicNodeProperties); @@ -107,25 +107,25 @@ class DicNode { PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - // Init for root with prevWordNodePos which is used for bigram - void initAsRoot(const int rootGroupPos, const int prevWordNodePos) { + // Init for root with prevWordPtNodePos which is used for bigram + void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) { mIsUsed = true; mIsCachedForNextSuggestion = false; mDicNodeProperties.init( - NOT_A_DICT_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */, + NOT_A_DICT_POS /* pos */, rootPtNodeArrayPos, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, false /* isTerminal */, true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); - mDicNodeState.init(prevWordNodePos); + mDicNodeState.init(prevWordPtNodePos); PROF_NODE_RESET(mProfiler); } // Init for root with previous word - void initAsRootWithPreviousWord(DicNode *dicNode, const int rootGroupPos) { + void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) { mIsUsed = true; mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; mDicNodeProperties.init( - NOT_A_DICT_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */, + NOT_A_DICT_POS /* pos */, rootPtNodeArrayPos, NOT_A_CODE_POINT /* nodeCodePoint */, NOT_A_PROBABILITY /* probability */, false /* isTerminal */, true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */, 0 /* terminalDepth */); @@ -138,7 +138,7 @@ class DicNode { mDicNodeState.mDicNodeStatePrevWord.init( dicNode->mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() + 1, dicNode->mDicNodeProperties.getProbability(), - dicNode->mDicNodeProperties.getPos(), + dicNode->mDicNodeProperties.getPtNodePos(), dicNode->mDicNodeState.mDicNodeStatePrevWord.mPrevWord, dicNode->mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(), dicNode->getOutputWordBuf(), @@ -148,26 +148,27 @@ class DicNode { PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); } - void initAsPassingChild(DicNode *parentNode) { + void initAsPassingChild(DicNode *parentDicNode) { mIsUsed = true; - mIsCachedForNextSuggestion = parentNode->mIsCachedForNextSuggestion; - const int c = parentNode->getNodeTypedCodePoint(); - mDicNodeProperties.init(&parentNode->mDicNodeProperties, c); - mDicNodeState.init(&parentNode->mDicNodeState); - PROF_NODE_COPY(&parentNode->mProfiler, mProfiler); + mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion; + const int parentCodePoint = parentDicNode->getNodeTypedCodePoint(); + mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint); + mDicNodeState.init(&parentDicNode->mDicNodeState); + PROF_NODE_COPY(&parentDicNode->mProfiler, mProfiler); } - void initAsChild(const DicNode *const dicNode, const int pos, const int childrenPos, - const int probability, const bool isTerminal, const bool hasChildren, - const bool isBlacklistedOrNotAWord, const uint16_t mergedNodeCodePointCount, - const int *const mergedNodeCodePoints) { + void initAsChild(const DicNode *const dicNode, const int ptNodePos, + const int childrenPtNodeArrayPos, const int probability, const bool isTerminal, + const bool hasChildren, const bool isBlacklistedOrNotAWord, + const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { mIsUsed = true; uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1); mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion; const uint16_t newLeavingDepth = static_cast<uint16_t>( dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount); - mDicNodeProperties.init(pos, childrenPos, mergedNodeCodePoints[0], probability, - isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth, newLeavingDepth); + mDicNodeProperties.init(ptNodePos, childrenPtNodeArrayPos, mergedNodeCodePoints[0], + probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth, + newLeavingDepth); mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount, mergedNodeCodePoints); PROF_NODE_COPY(&dicNode->mProfiler, mProfiler); @@ -234,7 +235,7 @@ class DicNode { } bool isFirstWord() const { - return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_A_DICT_POS; + return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos() == NOT_A_DICT_POS; } bool isCompletion(const int inputSize) const { @@ -246,29 +247,30 @@ class DicNode { } // Used to get bigram probability in DicNodeUtils - int getPos() const { - return mDicNodeProperties.getPos(); + int getPtNodePos() const { + return mDicNodeProperties.getPtNodePos(); } // Used to get bigram probability in DicNodeUtils - int getPrevWordPos() const { - return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos(); + int getPrevWordTerminalPtNodePos() const { + return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos(); } // Used in DicNodeUtils - int getChildrenPos() const { - return mDicNodeProperties.getChildrenPos(); + int getChildrenPtNodeArrayPos() const { + return mDicNodeProperties.getChildrenPtNodeArrayPos(); } int getProbability() const { return mDicNodeProperties.getProbability(); } - AK_FORCE_INLINE bool isTerminalWordNode() const { - const bool isTerminalNodes = mDicNodeProperties.isTerminal(); - const int currentNodeDepth = getNodeCodePointCount(); - const int terminalNodeDepth = mDicNodeProperties.getLeavingDepth(); - return isTerminalNodes && currentNodeDepth > 0 && currentNodeDepth == terminalNodeDepth; + AK_FORCE_INLINE bool isTerminalDicNode() const { + const bool isTerminalPtNode = mDicNodeProperties.isTerminal(); + const int currentDicNodeDepth = getNodeCodePointCount(); + const int terminalDicNodeDepth = mDicNodeProperties.getLeavingDepth(); + return isTerminalPtNode && currentDicNodeDepth > 0 + && currentDicNodeDepth == terminalDicNodeDepth; } bool shouldBeFilteredBySafetyNetForBigram() const { @@ -374,8 +376,8 @@ class DicNode { } // Used to commit input partially - int getPrevWordNodePos() const { - return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos(); + int getPrevWordPtNodePos() const { + return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos(); } AK_FORCE_INLINE const int *getOutputWordBuf() const { @@ -410,7 +412,7 @@ class DicNode { // TODO: Remove once touch path is merged into ProximityInfoState // Note: Returned codepoint may be a digraph codepoint if the node is in a composite glyph. int getNodeCodePoint() const { - const int codePoint = mDicNodeProperties.getNodeCodePoint(); + const int codePoint = mDicNodeProperties.getDicNodeCodePoint(); const DigraphUtils::DigraphCodePointIndex digraphIndex = mDicNodeState.mDicNodeStateScoring.getDigraphIndex(); if (digraphIndex == DigraphUtils::NOT_A_DIGRAPH_INDEX) { @@ -423,8 +425,8 @@ class DicNode { // Utils for cost calculation // //////////////////////////////// AK_FORCE_INLINE bool isSameNodeCodePoint(const DicNode *const dicNode) const { - return mDicNodeProperties.getNodeCodePoint() - == dicNode->mDicNodeProperties.getNodeCodePoint(); + return mDicNodeProperties.getDicNodeCodePoint() + == dicNode->mDicNodeProperties.getDicNodeCodePoint(); } // TODO: remove @@ -574,7 +576,8 @@ class DicNode { // Caveat: Must not be called outside Weighting // This restriction is guaranteed by "friend" AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost, - const bool doNormalization, const int inputSize, const ErrorType errorType) { + const bool doNormalization, const int inputSize, + const ErrorTypeUtils::ErrorType errorType) { if (DEBUG_GEO_FULL) { LOGI_SHOW_ADD_COST_PROP; } diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index ec65114c7..5540b6df5 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -22,7 +22,6 @@ #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/multi_bigram_map.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "utils/char_utils.h" namespace latinime { @@ -32,19 +31,20 @@ namespace latinime { /* static */ void DicNodeUtils::initAsRoot( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int prevWordNodePos, DicNode *const newRootNode) { - newRootNode->initAsRoot(dictionaryStructurePolicy->getRootPosition(), prevWordNodePos); + const int prevWordPtNodePos, DicNode *const newRootDicNode) { + newRootDicNode->initAsRoot(dictionaryStructurePolicy->getRootPosition(), prevWordPtNodePos); } /*static */ void DicNodeUtils::initAsRootWithPreviousWord( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - DicNode *const prevWordLastNode, DicNode *const newRootNode) { - newRootNode->initAsRootWithPreviousWord( - prevWordLastNode, dictionaryStructurePolicy->getRootPosition()); + const DicNode *const prevWordLastDicNode, DicNode *const newRootDicNode) { + newRootDicNode->initAsRootWithPreviousWord( + prevWordLastDicNode, dictionaryStructurePolicy->getRootPosition()); } -/* static */ void DicNodeUtils::initByCopy(DicNode *srcNode, DicNode *destNode) { - destNode->initByCopy(srcNode); +/* static */ void DicNodeUtils::initByCopy(const DicNode *const srcDicNode, + DicNode *const destDicNode) { + destDicNode->initByCopy(srcDicNode); } /////////////////////////////////// @@ -52,14 +52,14 @@ namespace latinime { /////////////////////////////////// /* static */ void DicNodeUtils::getAllChildDicNodes(DicNode *dicNode, const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - DicNodeVector *childDicNodes) { + DicNodeVector *const childDicNodes) { if (dicNode->isTotalInputSizeExceedingLimit()) { return; } if (!dicNode->isLeavingNode()) { childDicNodes->pushPassingChild(dicNode); } else { - dictionaryStructurePolicy->createAndGetAllChildNodes(dicNode, childDicNodes); + dictionaryStructurePolicy->createAndGetAllChildDicNodes(dicNode, childDicNodes); } } @@ -71,11 +71,11 @@ namespace latinime { */ /* static */ float DicNodeUtils::getBigramNodeImprobability( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const DicNode *const node, MultiBigramMap *multiBigramMap) { - if (node->hasMultipleWords() && !node->isValidMultipleWordSuggestion()) { + const DicNode *const dicNode, MultiBigramMap *const multiBigramMap) { + if (dicNode->hasMultipleWords() && !dicNode->isValidMultipleWordSuggestion()) { return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); } - const int probability = getBigramNodeProbability(dictionaryStructurePolicy, node, + const int probability = getBigramNodeProbability(dictionaryStructurePolicy, dicNode, multiBigramMap); // TODO: This equation to calculate the improbability looks unreasonable. Investigate this. const float cost = static_cast<float>(MAX_PROBABILITY - probability) @@ -85,19 +85,19 @@ namespace latinime { /* static */ int DicNodeUtils::getBigramNodeProbability( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const DicNode *const node, MultiBigramMap *multiBigramMap) { - const int unigramProbability = node->getProbability(); - const int wordPos = node->getPos(); - const int prevWordPos = node->getPrevWordPos(); - if (NOT_A_DICT_POS == wordPos || NOT_A_DICT_POS == prevWordPos) { + const DicNode *const dicNode, MultiBigramMap *const multiBigramMap) { + const int unigramProbability = dicNode->getProbability(); + const int ptNodePos = dicNode->getPtNodePos(); + const int prevWordTerminalPtNodePos = dicNode->getPrevWordTerminalPtNodePos(); + if (NOT_A_DICT_POS == ptNodePos || NOT_A_DICT_POS == prevWordTerminalPtNodePos) { // Note: Normally wordPos comes from the dictionary and should never equal // NOT_A_VALID_WORD_POS. return dictionaryStructurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY); } if (multiBigramMap) { - return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, prevWordPos, - wordPos, unigramProbability); + return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, + prevWordTerminalPtNodePos, ptNodePos, unigramProbability); } return dictionaryStructurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY); @@ -109,7 +109,7 @@ namespace latinime { // TODO: Move to char_utils? /* static */ int DicNodeUtils::appendTwoWords(const int *const src0, const int16_t length0, - const int *const src1, const int16_t length1, int *dest) { + const int *const src1, const int16_t length1, int *const dest) { int actualLength0 = 0; for (int i = 0; i < length0; ++i) { if (src0[i] == 0) { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h index 3fb351a61..3f1514a52 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h @@ -31,20 +31,20 @@ class MultiBigramMap; class DicNodeUtils { public: static int appendTwoWords(const int *src0, const int16_t length0, const int *src1, - const int16_t length1, int *dest); + const int16_t length1, int *const dest); static void initAsRoot( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const int prevWordNodePos, DicNode *newRootNode); + const int prevWordPtNodePos, DicNode *const newRootDicNode); static void initAsRootWithPreviousWord( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - DicNode *prevWordLastNode, DicNode *newRootNode); - static void initByCopy(DicNode *srcNode, DicNode *destNode); + const DicNode *const prevWordLastDicNode, DicNode *const newRootDicNode); + static void initByCopy(const DicNode *const srcDicNode, DicNode *const destDicNode); static void getAllChildDicNodes(DicNode *dicNode, const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, DicNodeVector *childDicNodes); static float getBigramNodeImprobability( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const DicNode *const node, MultiBigramMap *const multiBigramMap); + const DicNode *const dicNode, MultiBigramMap *const multiBigramMap); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeUtils); @@ -53,7 +53,7 @@ class DicNodeUtils { static int getBigramNodeProbability( const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, - const DicNode *const node, MultiBigramMap *multiBigramMap); + const DicNode *const dicNode, MultiBigramMap *const multiBigramMap); }; } // namespace latinime #endif // LATINIME_DIC_NODE_UTILS_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node_vector.h b/native/jni/src/suggest/core/dicnode/dic_node_vector.h index 42addae8d..9364e7751 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_vector.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_vector.h @@ -62,14 +62,14 @@ class DicNodeVector { mDicNodes.back().initAsPassingChild(dicNode); } - void pushLeavingChild(const DicNode *const dicNode, const int pos, const int childrenPos, - const int probability, const bool isTerminal, const bool hasChildren, - const bool isBlacklistedOrNotAWord, const uint16_t mergedNodeCodePointCount, - const int *const mergedNodeCodePoints) { + void pushLeavingChild(const DicNode *const dicNode, const int ptNodePos, + const int childrenPtNodeArrayPos, const int probability, const bool isTerminal, + const bool hasChildren, const bool isBlacklistedOrNotAWord, + const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { ASSERT(!mLock); mDicNodes.push_back(mEmptyNode); - mDicNodes.back().initAsChild(dicNode, pos, childrenPos, probability, isTerminal, - hasChildren, isBlacklistedOrNotAWord, mergedNodeCodePointCount, + mDicNodes.back().initAsChild(dicNode, ptNodePos, childrenPtNodeArrayPos, probability, + isTerminal, hasChildren, isBlacklistedOrNotAWord, mergedNodeCodePointCount, mergedNodeCodePoints); } diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h index 9e0f62ceb..c41a7243a 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_properties.h @@ -24,15 +24,14 @@ namespace latinime { /** - * Node for traversing the lexicon trie. + * PtNode information related to the DicNode from the lexicon trie. */ -// TODO: Introduce a dictionary node class which has attribute members required to understand the -// dictionary structure. class DicNodeProperties { public: AK_FORCE_INLINE DicNodeProperties() - : mPos(0), mChildrenPos(0), mProbability(0), mNodeCodePoint(0), mIsTerminal(false), - mHasChildren(false), mIsBlacklistedOrNotAWord(false), mDepth(0), mLeavingDepth(0) {} + : mPtNodePos(0), mChildrenPtNodeArrayPos(0), mProbability(0), mDicNodeCodePoint(0), + mIsTerminal(false), mHasChildrenPtNodes(false), mIsBlacklistedOrNotAWord(false), + mDepth(0), mLeavingDepth(0) {} virtual ~DicNodeProperties() {} @@ -40,57 +39,57 @@ class DicNodeProperties { void init(const int pos, const int childrenPos, const int nodeCodePoint, const int probability, const bool isTerminal, const bool hasChildren, const bool isBlacklistedOrNotAWord, const uint16_t depth, const uint16_t leavingDepth) { - mPos = pos; - mChildrenPos = childrenPos; - mNodeCodePoint = nodeCodePoint; + mPtNodePos = pos; + mChildrenPtNodeArrayPos = childrenPos; + mDicNodeCodePoint = nodeCodePoint; mProbability = probability; mIsTerminal = isTerminal; - mHasChildren = hasChildren; + mHasChildrenPtNodes = hasChildren; mIsBlacklistedOrNotAWord = isBlacklistedOrNotAWord; mDepth = depth; mLeavingDepth = leavingDepth; } // Init for copy - void init(const DicNodeProperties *const nodeProp) { - mPos = nodeProp->mPos; - mChildrenPos = nodeProp->mChildrenPos; - mNodeCodePoint = nodeProp->mNodeCodePoint; - mProbability = nodeProp->mProbability; - mIsTerminal = nodeProp->mIsTerminal; - mHasChildren = nodeProp->mHasChildren; - mIsBlacklistedOrNotAWord = nodeProp->mIsBlacklistedOrNotAWord; - mDepth = nodeProp->mDepth; - mLeavingDepth = nodeProp->mLeavingDepth; + void init(const DicNodeProperties *const dicNodeProp) { + mPtNodePos = dicNodeProp->mPtNodePos; + mChildrenPtNodeArrayPos = dicNodeProp->mChildrenPtNodeArrayPos; + mDicNodeCodePoint = dicNodeProp->mDicNodeCodePoint; + mProbability = dicNodeProp->mProbability; + mIsTerminal = dicNodeProp->mIsTerminal; + mHasChildrenPtNodes = dicNodeProp->mHasChildrenPtNodes; + mIsBlacklistedOrNotAWord = dicNodeProp->mIsBlacklistedOrNotAWord; + mDepth = dicNodeProp->mDepth; + mLeavingDepth = dicNodeProp->mLeavingDepth; } // Init as passing child - void init(const DicNodeProperties *const nodeProp, const int codePoint) { - mPos = nodeProp->mPos; - mChildrenPos = nodeProp->mChildrenPos; - mNodeCodePoint = codePoint; // Overwrite the node char of a passing child - mProbability = nodeProp->mProbability; - mIsTerminal = nodeProp->mIsTerminal; - mHasChildren = nodeProp->mHasChildren; - mIsBlacklistedOrNotAWord = nodeProp->mIsBlacklistedOrNotAWord; - mDepth = nodeProp->mDepth + 1; // Increment the depth of a passing child - mLeavingDepth = nodeProp->mLeavingDepth; + void init(const DicNodeProperties *const dicNodeProp, const int codePoint) { + mPtNodePos = dicNodeProp->mPtNodePos; + mChildrenPtNodeArrayPos = dicNodeProp->mChildrenPtNodeArrayPos; + mDicNodeCodePoint = codePoint; // Overwrite the node char of a passing child + mProbability = dicNodeProp->mProbability; + mIsTerminal = dicNodeProp->mIsTerminal; + mHasChildrenPtNodes = dicNodeProp->mHasChildrenPtNodes; + mIsBlacklistedOrNotAWord = dicNodeProp->mIsBlacklistedOrNotAWord; + mDepth = dicNodeProp->mDepth + 1; // Increment the depth of a passing child + mLeavingDepth = dicNodeProp->mLeavingDepth; } - int getPos() const { - return mPos; + int getPtNodePos() const { + return mPtNodePos; } - int getChildrenPos() const { - return mChildrenPos; + int getChildrenPtNodeArrayPos() const { + return mChildrenPtNodeArrayPos; } int getProbability() const { return mProbability; } - int getNodeCodePoint() const { - return mNodeCodePoint; + int getDicNodeCodePoint() const { + return mDicNodeCodePoint; } uint16_t getDepth() const { @@ -107,7 +106,7 @@ class DicNodeProperties { } bool hasChildren() const { - return mHasChildren || mDepth != mLeavingDepth; + return mHasChildrenPtNodes || mDepth != mLeavingDepth; } bool isBlacklistedOrNotAWord() const { @@ -118,12 +117,12 @@ class DicNodeProperties { // Caution!!! // Use a default copy constructor and an assign operator because shallow copies are ok // for this class - int mPos; - int mChildrenPos; + int mPtNodePos; + int mChildrenPtNodeArrayPos; int mProbability; - int mNodeCodePoint; + int mDicNodeCodePoint; bool mIsTerminal; - bool mHasChildren; + bool mHasChildrenPtNodes; bool mIsBlacklistedOrNotAWord; uint16_t mDepth; uint16_t mLeavingDepth; diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h index b8986203d..dba57056b 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h @@ -30,7 +30,7 @@ class DicNodeStatePrevWord { public: AK_FORCE_INLINE DicNodeStatePrevWord() : mPrevWordCount(0), mPrevWordLength(0), mPrevWordStart(0), mPrevWordProbability(0), - mPrevWordNodePos(NOT_A_DICT_POS), mSecondWordFirstInputIndex(NOT_AN_INDEX) { + mPrevWordPtNodePos(NOT_A_DICT_POS), mSecondWordFirstInputIndex(NOT_AN_INDEX) { memset(mPrevWord, 0, sizeof(mPrevWord)); } @@ -41,7 +41,7 @@ class DicNodeStatePrevWord { mPrevWordCount = 0; mPrevWordStart = 0; mPrevWordProbability = -1; - mPrevWordNodePos = NOT_A_DICT_POS; + mPrevWordPtNodePos = NOT_A_DICT_POS; mSecondWordFirstInputIndex = NOT_AN_INDEX; } @@ -50,7 +50,7 @@ class DicNodeStatePrevWord { mPrevWordCount = 0; mPrevWordStart = 0; mPrevWordProbability = -1; - mPrevWordNodePos = prevWordNodePos; + mPrevWordPtNodePos = prevWordNodePos; mSecondWordFirstInputIndex = NOT_AN_INDEX; } @@ -60,7 +60,7 @@ class DicNodeStatePrevWord { mPrevWordCount = prevWord->mPrevWordCount; mPrevWordStart = prevWord->mPrevWordStart; mPrevWordProbability = prevWord->mPrevWordProbability; - mPrevWordNodePos = prevWord->mPrevWordNodePos; + mPrevWordPtNodePos = prevWord->mPrevWordPtNodePos; mSecondWordFirstInputIndex = prevWord->mSecondWordFirstInputIndex; memcpy(mPrevWord, prevWord->mPrevWord, prevWord->mPrevWordLength * sizeof(mPrevWord[0])); } @@ -71,7 +71,7 @@ class DicNodeStatePrevWord { const int prevWordSecondWordFirstInputIndex, const int lastInputIndex) { mPrevWordCount = min(prevWordCount, static_cast<int16_t>(MAX_RESULTS)); mPrevWordProbability = prevWordProbability; - mPrevWordNodePos = prevWordNodePos; + mPrevWordPtNodePos = prevWordNodePos; int twoWordsLen = DicNodeUtils::appendTwoWords(src0, length0, src1, length1, mPrevWord); if (twoWordsLen >= MAX_WORD_LENGTH) { @@ -116,8 +116,8 @@ class DicNodeStatePrevWord { return mPrevWordStart; } - int getPrevWordNodePos() const { - return mPrevWordNodePos; + int getPrevWordPtNodePos() const { + return mPrevWordPtNodePos; } int getPrevWordCodePointAt(const int id) const { @@ -147,7 +147,7 @@ class DicNodeStatePrevWord { int16_t mPrevWordLength; int16_t mPrevWordStart; int16_t mPrevWordProbability; - int mPrevWordNodePos; + int mPrevWordPtNodePos; int mSecondWordFirstInputIndex; }; } // namespace latinime diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h index 3c85d0e9d..74f9eee92 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_scoring.h @@ -21,6 +21,7 @@ #include "defines.h" #include "suggest/core/dictionary/digraph_utils.h" +#include "suggest/core/dictionary/error_type_utils.h" namespace latinime { @@ -31,7 +32,7 @@ class DicNodeStateScoring { mDigraphIndex(DigraphUtils::NOT_A_DIGRAPH_INDEX), mEditCorrectionCount(0), mProximityCorrectionCount(0), mNormalizedCompoundDistance(0.0f), mSpatialDistance(0.0f), mLanguageDistance(0.0f), - mRawLength(0.0f), mExactMatch(true), + mRawLength(0.0f), mContainingErrorTypes(ErrorTypeUtils::NOT_AN_ERROR), mNormalizedCompoundDistanceAfterFirstWord(MAX_VALUE_FOR_WEIGHTING) { } @@ -47,7 +48,7 @@ class DicNodeStateScoring { mDoubleLetterLevel = NOT_A_DOUBLE_LETTER; mDigraphIndex = DigraphUtils::NOT_A_DIGRAPH_INDEX; mNormalizedCompoundDistanceAfterFirstWord = MAX_VALUE_FOR_WEIGHTING; - mExactMatch = true; + mContainingErrorTypes = ErrorTypeUtils::NOT_AN_ERROR; } AK_FORCE_INLINE void init(const DicNodeStateScoring *const scoring) { @@ -59,34 +60,21 @@ class DicNodeStateScoring { mRawLength = scoring->mRawLength; mDoubleLetterLevel = scoring->mDoubleLetterLevel; mDigraphIndex = scoring->mDigraphIndex; - mExactMatch = scoring->mExactMatch; + mContainingErrorTypes = scoring->mContainingErrorTypes; mNormalizedCompoundDistanceAfterFirstWord = scoring->mNormalizedCompoundDistanceAfterFirstWord; } void addCost(const float spatialCost, const float languageCost, const bool doNormalization, - const int inputSize, const int totalInputIndex, const ErrorType errorType) { + const int inputSize, const int totalInputIndex, + const ErrorTypeUtils::ErrorType errorType) { addDistance(spatialCost, languageCost, doNormalization, inputSize, totalInputIndex); - switch (errorType) { - case ET_EDIT_CORRECTION: - ++mEditCorrectionCount; - mExactMatch = false; - break; - case ET_PROXIMITY_CORRECTION: - ++mProximityCorrectionCount; - mExactMatch = false; - break; - case ET_COMPLETION: - mExactMatch = false; - break; - case ET_NEW_WORD: - mExactMatch = false; - break; - case ET_INTENTIONAL_OMISSION: - mExactMatch = false; - break; - case ET_NOT_AN_ERROR: - break; + mContainingErrorTypes = mContainingErrorTypes | errorType; + if (ErrorTypeUtils::isEditCorrectionError(errorType)) { + ++mEditCorrectionCount; + } + if (ErrorTypeUtils::isProximityCorrectionError(errorType)) { + ++mProximityCorrectionCount; } } @@ -182,7 +170,7 @@ class DicNodeStateScoring { } bool isExactMatch() const { - return mExactMatch; + return ErrorTypeUtils::isExactMatch(mContainingErrorTypes); } private: @@ -199,7 +187,8 @@ class DicNodeStateScoring { float mSpatialDistance; float mLanguageDistance; float mRawLength; - bool mExactMatch; + // All accumulated error types so far + ErrorTypeUtils::ErrorType mContainingErrorTypes; float mNormalizedCompoundDistanceAfterFirstWord; AK_FORCE_INLINE void addDistance(float spatialDistance, float languageDistance, diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index 71f4ef6ea..2a62b555b 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -144,7 +144,7 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const int prevWordLength, const bool forceLowerCaseSearch) const { if (0 >= prevWordLength) return NOT_A_DICT_POS; - int pos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(prevWord, prevWordLength, + int pos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(prevWord, prevWordLength, forceLowerCaseSearch); if (NOT_A_DICT_POS == pos) return NOT_A_DICT_POS; return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos); @@ -155,7 +155,7 @@ int BigramDictionary::getBigramProbability(const int *word0, int length0, const int pos = getBigramListPositionForWord(word0, length0, false /* forceLowerCaseSearch */); // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY; - int nextWordPos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(word1, length1, + int nextWordPos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(word1, length1, false /* forceLowerCaseSearch */); if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY; @@ -163,7 +163,8 @@ int BigramDictionary::getBigramProbability(const int *word0, int length0, const mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos); while (bigramsIt.hasNext()) { bigramsIt.next(); - if (bigramsIt.getBigramPos() == nextWordPos) { + if (bigramsIt.getBigramPos() == nextWordPos + && bigramsIt.getProbability() != NOT_A_PROBABILITY) { return mDictionaryStructurePolicy->getProbability( mDictionaryStructurePolicy->getUnigramProbabilityOfPtNode(nextWordPos), bigramsIt.getProbability()); diff --git a/native/jni/src/suggest/core/dictionary/bloom_filter.h b/native/jni/src/suggest/core/dictionary/bloom_filter.h index 5205456a8..5f9700486 100644 --- a/native/jni/src/suggest/core/dictionary/bloom_filter.h +++ b/native/jni/src/suggest/core/dictionary/bloom_filter.h @@ -50,6 +50,8 @@ class BloomFilter { } private: + DISALLOW_ASSIGNMENT_OPERATOR(BloomFilter); + // Size, in bytes, of the bloom filter index for bigrams // 128 gives us 1024 buckets. The probability of false positive is (1 - e ** (-kn/m))**k, // where k is the number of hash functions, n the number of bigrams, and m the number of diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 59ead1894..e68c0a6d8 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -21,46 +21,39 @@ #include <stdint.h> #include "defines.h" -#include "suggest/core/dictionary/bigram_dictionary.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" -#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "suggest/core/session/dic_traverse_session.h" #include "suggest/core/suggest.h" #include "suggest/core/suggest_options.h" #include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h" #include "suggest/policyimpl/typing/typing_suggest_policy_factory.h" #include "utils/log_utils.h" +#include "utils/time_keeper.h" namespace latinime { const int Dictionary::HEADER_ATTRIBUTE_BUFFER_SIZE = 32; -Dictionary::Dictionary(JNIEnv *env, - DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy) +Dictionary::Dictionary(JNIEnv *env, const DictionaryStructureWithBufferPolicy::StructurePolicyPtr + &dictionaryStructureWithBufferPolicy) : mDictionaryStructureWithBufferPolicy(dictionaryStructureWithBufferPolicy), - mBigramDictionary(new BigramDictionary(mDictionaryStructureWithBufferPolicy)), + mBigramDictionary(new BigramDictionary(mDictionaryStructureWithBufferPolicy.get())), mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())), mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) { logDictionaryInfo(env); } -Dictionary::~Dictionary() { - delete mBigramDictionary; - delete mGestureSuggest; - delete mTypingSuggest; - delete mDictionaryStructureWithBufferPolicy; -} - int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, int inputSize, int *prevWordCodePoints, int prevWordLength, int commitPoint, const SuggestOptions *const suggestOptions, int *outWords, int *frequencies, int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const { + TimeKeeper::setCurrentTime(); int result = 0; if (suggestOptions->isGesture()) { DicTraverseSession::initSessionInstance( traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); - result = mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, + result = mGestureSuggest.get()->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); if (DEBUG_DICT) { @@ -70,7 +63,7 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } else { DicTraverseSession::initSessionInstance( traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); - result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, + result = mTypingSuggest.get()->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); @@ -83,12 +76,15 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession int Dictionary::getBigrams(const int *word, int length, int *outWords, int *frequencies, int *outputTypes) const { + TimeKeeper::setCurrentTime(); if (length <= 0) return 0; - return mBigramDictionary->getPredictions(word, length, outWords, frequencies, outputTypes); + return mBigramDictionary.get()->getPredictions(word, length, outWords, frequencies, + outputTypes); } int Dictionary::getProbability(const int *word, int length) const { - int pos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord(word, length, + TimeKeeper::setCurrentTime(); + int pos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(word, length, false /* forceLowerCaseSearch */); if (NOT_A_DICT_POS == pos) { return NOT_A_PROBABILITY; @@ -98,39 +94,60 @@ int Dictionary::getProbability(const int *word, int length) const { int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1, int length1) const { - return mBigramDictionary->getBigramProbability(word0, length0, word1, length1); + TimeKeeper::setCurrentTime(); + return mBigramDictionary.get()->getBigramProbability(word0, length0, word1, length1); } -void Dictionary::addUnigramWord(const int *const word, const int length, const int probability) { - mDictionaryStructureWithBufferPolicy->addUnigramWord(word, length, probability); +void Dictionary::addUnigramWord(const int *const word, const int length, const int probability, + const int *const shortcutTargetCodePoints, const int shortcutLength, + const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, + const int timestamp) { + TimeKeeper::setCurrentTime(); + mDictionaryStructureWithBufferPolicy.get()->addUnigramWord(word, length, probability, + shortcutTargetCodePoints, shortcutLength, shortcutProbability, isNotAWord, + isBlacklisted, timestamp); } void Dictionary::addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability) { - mDictionaryStructureWithBufferPolicy->addBigramWords(word0, length0, word1, length1, - probability); + const int length1, const int probability, const int timestamp) { + TimeKeeper::setCurrentTime(); + mDictionaryStructureWithBufferPolicy.get()->addBigramWords(word0, length0, word1, length1, + probability, timestamp); } void Dictionary::removeBigramWords(const int *const word0, const int length0, const int *const word1, const int length1) { - mDictionaryStructureWithBufferPolicy->removeBigramWords(word0, length0, word1, length1); + TimeKeeper::setCurrentTime(); + mDictionaryStructureWithBufferPolicy.get()->removeBigramWords(word0, length0, word1, length1); } void Dictionary::flush(const char *const filePath) { - mDictionaryStructureWithBufferPolicy->flush(filePath); + TimeKeeper::setCurrentTime(); + mDictionaryStructureWithBufferPolicy.get()->flush(filePath); } void Dictionary::flushWithGC(const char *const filePath) { - mDictionaryStructureWithBufferPolicy->flushWithGC(filePath); + TimeKeeper::setCurrentTime(); + mDictionaryStructureWithBufferPolicy.get()->flushWithGC(filePath); } bool Dictionary::needsToRunGC(const bool mindsBlockByGC) { - return mDictionaryStructureWithBufferPolicy->needsToRunGC(mindsBlockByGC); + TimeKeeper::setCurrentTime(); + return mDictionaryStructureWithBufferPolicy.get()->needsToRunGC(mindsBlockByGC); } -void Dictionary::getProperty(const char *const query, char *const outResult, +void Dictionary::getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength) { - return mDictionaryStructureWithBufferPolicy->getProperty(query, outResult, maxResultLength); + TimeKeeper::setCurrentTime(); + return mDictionaryStructureWithBufferPolicy.get()->getProperty(query, queryLength, outResult, + maxResultLength); +} + +const UnigramProperty Dictionary::getUnigramProperty(const int *const codePoints, + const int codePointCount) { + TimeKeeper::setCurrentTime(); + return mDictionaryStructureWithBufferPolicy.get()->getUnigramProperty( + codePoints, codePointCount); } void Dictionary::logDictionaryInfo(JNIEnv *const env) const { diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 0195d5bf0..b37b4aa18 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -21,15 +21,20 @@ #include "defines.h" #include "jni.h" +#include "suggest/core/dictionary/bigram_dictionary.h" +#include "suggest/core/dictionary/unigram_property.h" +#include "suggest/core/policy/dictionary_header_structure_policy.h" +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/core/suggest_interface.h" +#include "utils/exclusive_ownership_pointer.h" namespace latinime { -class BigramDictionary; class DictionaryStructureWithBufferPolicy; class DicTraverseSession; class ProximityInfo; -class SuggestInterface; class SuggestOptions; +class UnigramProperty; class Dictionary { public: @@ -53,8 +58,8 @@ class Dictionary { static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; static const int KIND_FLAG_EXACT_MATCH = 0x40000000; - Dictionary(JNIEnv *env, - DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPoilcy); + Dictionary(JNIEnv *env, const DictionaryStructureWithBufferPolicy::StructurePolicyPtr + &dictionaryStructureWithBufferPolicy); int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, @@ -69,10 +74,13 @@ class Dictionary { 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); + void addUnigramWord(const int *const word, const int length, const int probability, + const int *const shortcutTargetCodePoints, const int shortcutLength, + const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, + const int timestamp); void addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability); + const int length1, const int probability, const int timestamp); void removeBigramWords(const int *const word0, const int length0, const int *const word1, const int length1); @@ -83,24 +91,33 @@ class Dictionary { bool needsToRunGC(const bool mindsBlockByGC); - void getProperty(const char *const query, char *const outResult, + void getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength); + const UnigramProperty getUnigramProperty(const int *const codePoints, const int codePointCount); + const DictionaryStructureWithBufferPolicy *getDictionaryStructurePolicy() const { - return mDictionaryStructureWithBufferPolicy; + return mDictionaryStructureWithBufferPolicy.get(); } - virtual ~Dictionary(); + int getFormatVersionNumber() const { + return mDictionaryStructureWithBufferPolicy.get()->getHeaderStructurePolicy() + ->getFormatVersionNumber(); + } private: DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary); + typedef ExclusiveOwnershipPointer<BigramDictionary> BigramDictionaryPtr; + typedef ExclusiveOwnershipPointer<SuggestInterface> SuggestInterfacePtr; + static const int HEADER_ATTRIBUTE_BUFFER_SIZE; - DictionaryStructureWithBufferPolicy *const mDictionaryStructureWithBufferPolicy; - const BigramDictionary *const mBigramDictionary; - const SuggestInterface *const mGestureSuggest; - const SuggestInterface *const mTypingSuggest; + const DictionaryStructureWithBufferPolicy::StructurePolicyPtr + mDictionaryStructureWithBufferPolicy; + const BigramDictionaryPtr mBigramDictionary; + const SuggestInterfacePtr mGestureSuggest; + const SuggestInterfacePtr mTypingSuggest; void logDictionaryInfo(JNIEnv *const env) const; }; diff --git a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp index 3271c1bfb..5f9b8f3e2 100644 --- a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp @@ -28,11 +28,8 @@ const DigraphUtils::digraph_t DigraphUtils::GERMAN_UMLAUT_DIGRAPHS[] = { { 'a', 'e', 0x00E4 }, // U+00E4 : LATIN SMALL LETTER A WITH DIAERESIS { 'o', 'e', 0x00F6 }, // U+00F6 : LATIN SMALL LETTER O WITH DIAERESIS { 'u', 'e', 0x00FC } }; // U+00FC : LATIN SMALL LETTER U WITH DIAERESIS -const DigraphUtils::digraph_t DigraphUtils::FRENCH_LIGATURES_DIGRAPHS[] = - { { 'a', 'e', 0x00E6 }, // U+00E6 : LATIN SMALL LETTER AE - { 'o', 'e', 0x0153 } }; // U+0153 : LATIN SMALL LIGATURE OE const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = - { DIGRAPH_TYPE_GERMAN_UMLAUT, DIGRAPH_TYPE_FRENCH_LIGATURES }; + { DIGRAPH_TYPE_GERMAN_UMLAUT }; /* static */ bool DigraphUtils::hasDigraphForCodePoint( const DictionaryHeaderStructurePolicy *const headerPolicy, @@ -50,9 +47,6 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = if (headerPolicy->requiresGermanUmlautProcessing()) { return DIGRAPH_TYPE_GERMAN_UMLAUT; } - if (headerPolicy->requiresFrenchLigatureProcessing()) { - return DIGRAPH_TYPE_FRENCH_LIGATURES; - } return DIGRAPH_TYPE_NONE; } @@ -86,10 +80,6 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = *digraphs = GERMAN_UMLAUT_DIGRAPHS; return NELEMS(GERMAN_UMLAUT_DIGRAPHS); } - if (digraphType == DIGRAPH_TYPE_FRENCH_LIGATURES) { - *digraphs = FRENCH_LIGATURES_DIGRAPHS; - return NELEMS(FRENCH_LIGATURES_DIGRAPHS); - } return 0; } diff --git a/native/jni/src/suggest/core/dictionary/digraph_utils.h b/native/jni/src/suggest/core/dictionary/digraph_utils.h index 6ae16e390..bec2cd6e2 100644 --- a/native/jni/src/suggest/core/dictionary/digraph_utils.h +++ b/native/jni/src/suggest/core/dictionary/digraph_utils.h @@ -34,7 +34,6 @@ class DigraphUtils { typedef enum { DIGRAPH_TYPE_NONE, DIGRAPH_TYPE_GERMAN_UMLAUT, - DIGRAPH_TYPE_FRENCH_LIGATURES } DigraphType; typedef struct { int first; int second; int compositeGlyph; } digraph_t; @@ -55,7 +54,6 @@ class DigraphUtils { const DigraphType digraphType, const int compositeGlyphCodePoint); static const digraph_t GERMAN_UMLAUT_DIGRAPHS[]; - static const digraph_t FRENCH_LIGATURES_DIGRAPHS[]; static const DigraphType USED_DIGRAPH_TYPES[]; }; } // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp new file mode 100644 index 000000000..0635fef7e --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp @@ -0,0 +1,34 @@ +/* + * 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/core/dictionary/error_type_utils.h" + +namespace latinime { + +const ErrorTypeUtils::ErrorType ErrorTypeUtils::NOT_AN_ERROR = 0x0; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_CASE_ERROR = 0x1; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR = 0x2; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_DIGRAPH = 0x4; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::INTENTIONAL_OMISSION = 0x8; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::EDIT_CORRECTION = 0x10; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::PROXIMITY_CORRECTION = 0x20; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::COMPLETION = 0x40; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x80; + +const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH = + NOT_AN_ERROR | MATCH_WITH_CASE_ERROR | MATCH_WITH_ACCENT_ERROR | MATCH_WITH_DIGRAPH; + +} // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.h b/native/jni/src/suggest/core/dictionary/error_type_utils.h new file mode 100644 index 000000000..ab4a65e48 --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h @@ -0,0 +1,69 @@ +/* + * 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_ERROR_TYPE_UTILS_H +#define LATINIME_ERROR_TYPE_UTILS_H + +#include <stdint.h> + +#include "defines.h" + +namespace latinime { + +class ErrorTypeUtils { + public: + // ErrorType is mainly decided by CorrectionType but it is also depending on if + // the correction has really been performed or not. + typedef uint32_t ErrorType; + + static const ErrorType NOT_AN_ERROR; + static const ErrorType MATCH_WITH_CASE_ERROR; + static const ErrorType MATCH_WITH_ACCENT_ERROR; + static const ErrorType MATCH_WITH_DIGRAPH; + // Treat error as an intentional omission when the CorrectionType is omission and the node can + // be intentional omission. + static const ErrorType INTENTIONAL_OMISSION; + // Substitution, omission and transposition + static const ErrorType EDIT_CORRECTION; + // Proximity error + static const ErrorType PROXIMITY_CORRECTION; + // Completion + static const ErrorType COMPLETION; + // New word + // TODO: Remove. + // A new word error should be an edit correction error or a proximity correction error. + static const ErrorType NEW_WORD; + + // TODO: Differentiate errors. + static bool isExactMatch(const ErrorType containingErrors) { + return (containingErrors & ~ERRORS_TREATED_AS_AN_EXACT_MATCH) == 0; + } + + static bool isEditCorrectionError(const ErrorType errorType) { + return (errorType & EDIT_CORRECTION) != 0; + } + + static bool isProximityCorrectionError(const ErrorType errorType) { + return (errorType & PROXIMITY_CORRECTION) != 0; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorTypeUtils); + + static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH; +}; +} // namespace latinime +#endif // LATINIME_ERROR_TYPE_UTILS_H diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp index b1d2f4b4d..49d82e69a 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp @@ -30,4 +30,75 @@ const size_t MultiBigramMap::MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP = 25; // Most common previous word contexts currently have 100 bigrams const int MultiBigramMap::BigramMap::DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP = 100; +// Look up the bigram probability for the given word pair from the cached bigram maps. +// Also caches the bigrams if there is space remaining and they have not been cached already. +int MultiBigramMap::getBigramProbability( + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int wordPosition, const int nextWordPosition, const int unigramProbability) { + hash_map_compat<int, BigramMap>::const_iterator mapPosition = + mBigramMaps.find(wordPosition); + if (mapPosition != mBigramMaps.end()) { + return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition, + unigramProbability); + } + if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) { + addBigramsForWordPosition(structurePolicy, wordPosition); + return mBigramMaps[wordPosition].getBigramProbability(structurePolicy, + nextWordPosition, unigramProbability); + } + return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition, + nextWordPosition, unigramProbability); +} + +void MultiBigramMap::BigramMap::init( + const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos) { + const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); + BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), + bigramsListPos); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) { + continue; + } + mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability(); + mBloomFilter.setInFilter(bigramsIt.getBigramPos()); + } +} + +int MultiBigramMap::BigramMap::getBigramProbability( + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int nextWordPosition, const int unigramProbability) const { + int bigramProbability = NOT_A_PROBABILITY; + if (mBloomFilter.isInFilter(nextWordPosition)) { + const hash_map_compat<int, int>::const_iterator bigramProbabilityIt = + mBigramMap.find(nextWordPosition); + if (bigramProbabilityIt != mBigramMap.end()) { + bigramProbability = bigramProbabilityIt->second; + } + } + return structurePolicy->getProbability(unigramProbability, bigramProbability); +} + +void MultiBigramMap::addBigramsForWordPosition( + const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) { + mBigramMaps[position].init(structurePolicy, position); +} + +int MultiBigramMap::readBigramProbabilityFromBinaryDictionary( + const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, + const int nextWordPosition, const int unigramProbability) { + int bigramProbability = NOT_A_PROBABILITY; + const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); + BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), + bigramsListPos); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + if (bigramsIt.getBigramPos() == nextWordPosition) { + bigramProbability = bigramsIt.getProbability(); + break; + } + } + return structurePolicy->getProbability(unigramProbability, bigramProbability); +} + } // namespace latinime 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 4633c07b0..421b2681c 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h @@ -38,21 +38,7 @@ class MultiBigramMap { // Look up the bigram probability for the given word pair from the cached bigram maps. // Also caches the bigrams if there is space remaining and they have not been cached already. int getBigramProbability(const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int wordPosition, const int nextWordPosition, const int unigramProbability) { - hash_map_compat<int, BigramMap>::const_iterator mapPosition = - mBigramMaps.find(wordPosition); - if (mapPosition != mBigramMaps.end()) { - return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition, - unigramProbability); - } - if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) { - addBigramsForWordPosition(structurePolicy, wordPosition); - return mBigramMaps[wordPosition].getBigramProbability(structurePolicy, - nextWordPosition, unigramProbability); - } - return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition, - nextWordPosition, unigramProbability); - } + const int wordPosition, const int nextWordPosition, const int unigramProbability); void clear() { mBigramMaps.clear(); @@ -67,33 +53,11 @@ class MultiBigramMap { ~BigramMap() {} void init(const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int nodePos) { - const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); - BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), - bigramsListPos); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) { - continue; - } - mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability(); - mBloomFilter.setInFilter(bigramsIt.getBigramPos()); - } - } + const int nodePos); - AK_FORCE_INLINE int getBigramProbability( + int getBigramProbability( const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int nextWordPosition, const int unigramProbability) const { - int bigramProbability = NOT_A_PROBABILITY; - if (mBloomFilter.isInFilter(nextWordPosition)) { - const hash_map_compat<int, int>::const_iterator bigramProbabilityIt = - mBigramMap.find(nextWordPosition); - if (bigramProbabilityIt != mBigramMap.end()) { - bigramProbability = bigramProbabilityIt->second; - } - } - return structurePolicy->getProbability(unigramProbability, bigramProbability); - } + const int nextWordPosition, const int unigramProbability) const; private: // NOTE: The BigramMap class doesn't use DISALLOW_COPY_AND_ASSIGN() because its default @@ -103,27 +67,12 @@ class MultiBigramMap { BloomFilter mBloomFilter; }; - AK_FORCE_INLINE void addBigramsForWordPosition( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) { - mBigramMaps[position].init(structurePolicy, position); - } + void addBigramsForWordPosition( + const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position); - AK_FORCE_INLINE int readBigramProbabilityFromBinaryDictionary( + int readBigramProbabilityFromBinaryDictionary( const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, - const int nextWordPosition, const int unigramProbability) { - int bigramProbability = NOT_A_PROBABILITY; - const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos); - BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(), - bigramsListPos); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == nextWordPosition) { - bigramProbability = bigramsIt.getProbability(); - break; - } - } - return structurePolicy->getProbability(unigramProbability, bigramProbability); - } + const int nextWordPosition, const int unigramProbability); static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP; hash_map_compat<int, BigramMap> mBigramMaps; diff --git a/native/jni/src/suggest/core/dictionary/unigram_property.cpp b/native/jni/src/suggest/core/dictionary/unigram_property.cpp new file mode 100644 index 000000000..16bbb69d8 --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/unigram_property.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/core/dictionary/unigram_property.h" + +namespace latinime { + +void UnigramProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints, + jbooleanArray outFlags, jintArray outProbability, jintArray outHistoricalInfo, + jobject outShortcutTargets, jobject outShortcutProbabilities) const { + env->SetIntArrayRegion(outCodePoints, 0 /* start */, mCodePointCount, mCodePoints); + jboolean flags[] = {mIsNotAWord, mIsBlacklisted, mHasBigrams, mHasShortcuts}; + env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags); + env->SetIntArrayRegion(outProbability, 0 /* start */, 1 /* len */, &mProbability); + int historicalInfo[] = {mTimestamp, mLevel, mCount}; + env->SetIntArrayRegion(outHistoricalInfo, 0 /* start */, NELEMS(historicalInfo), + historicalInfo); + + jclass integerClass = env->FindClass("java/lang/Integer"); + jmethodID intToIntegerConstructorId = env->GetMethodID(integerClass, "<init>", "(I)V"); + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + jmethodID addMethodId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + const int shortcutTargetCount = mShortcutTargets.size(); + for (int i = 0; i < shortcutTargetCount; ++i) { + jintArray shortcutTargetCodePointArray = env->NewIntArray(mShortcutTargets[i].size()); + env->SetIntArrayRegion(shortcutTargetCodePointArray, 0 /* start */, + mShortcutTargets[i].size(), &mShortcutTargets[i][0]); + env->CallVoidMethod(outShortcutTargets, addMethodId, shortcutTargetCodePointArray); + env->DeleteLocalRef(shortcutTargetCodePointArray); + jobject integerProbability = env->NewObject(integerClass, intToIntegerConstructorId, + mShortcutProbabilities[i]); + env->CallVoidMethod(outShortcutProbabilities, addMethodId, integerProbability); + env->DeleteLocalRef(integerProbability); + } + env->DeleteLocalRef(integerClass); + env->DeleteLocalRef(arrayListClass); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/unigram_property.h b/native/jni/src/suggest/core/dictionary/unigram_property.h new file mode 100644 index 000000000..c4ebb86ab --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/unigram_property.h @@ -0,0 +1,87 @@ +/* + * 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_UNIGRAM_PROPERTY_H +#define LATINIME_UNIGRAM_PROPERTY_H + +#include <cstring> +#include <vector> + +#include "defines.h" +#include "jni.h" + +namespace latinime { + +// This class is used for returning information belonging to a unigram to java side. +class UnigramProperty { + public: + // Invalid unigram. + UnigramProperty() + : mCodePoints(), mCodePointCount(0), mIsNotAWord(false), mIsBlacklisted(false), + mHasBigrams(false), mHasShortcuts(false), mProbability(NOT_A_PROBABILITY), + mTimestamp(0), mLevel(0), mCount(0), mShortcutTargets(), mShortcutProbabilities() {} + + UnigramProperty(const UnigramProperty &unigramProperty) + : mCodePoints(), mCodePointCount(unigramProperty.mCodePointCount), + mIsNotAWord(unigramProperty.mIsNotAWord), + mIsBlacklisted(unigramProperty.mIsBlacklisted), + mHasBigrams(unigramProperty.mHasBigrams), + mHasShortcuts(unigramProperty.mHasShortcuts), + mProbability(unigramProperty.mProbability), + mTimestamp(unigramProperty.mTimestamp), mLevel(unigramProperty.mLevel), + mCount(unigramProperty.mCount), mShortcutTargets(unigramProperty.mShortcutTargets), + mShortcutProbabilities(unigramProperty.mShortcutProbabilities) { + memcpy(mCodePoints, unigramProperty.mCodePoints, sizeof(mCodePoints)); + } + + UnigramProperty(const int *const codePoints, const int codePointCount, + const bool isNotAWord, const bool isBlacklisted, const bool hasBigrams, + const bool hasShortcuts, const int probability, const int timestamp, + const int level, const int count, + const std::vector<std::vector<int> > *const shortcutTargets, + const std::vector<int> *const shortcutProbabilities) + : mCodePoints(), mCodePointCount(codePointCount), + mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mHasBigrams(hasBigrams), + mHasShortcuts(hasShortcuts), mProbability(probability), mTimestamp(timestamp), + mLevel(level), mCount(count), mShortcutTargets(*shortcutTargets), + mShortcutProbabilities(*shortcutProbabilities) { + memcpy(mCodePoints, codePoints, sizeof(mCodePoints)); + } + + void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags, + jintArray outProbability, jintArray outHistoricalInfo, jobject outShortcutTargets, + jobject outShortcutProbabilities) const; + + private: + DISALLOW_ASSIGNMENT_OPERATOR(UnigramProperty); + + int mCodePoints[MAX_WORD_LENGTH]; + int mCodePointCount; + bool mIsNotAWord; + bool mIsBlacklisted; + bool mHasBigrams; + bool mHasShortcuts; + int mProbability; + // Historical information + int mTimestamp; + int mLevel; + int mCount; + // Shortcut + std::vector<std::vector<int> > mShortcutTargets; + std::vector<int> mShortcutProbabilities; +}; +} // namespace latinime +#endif // LATINIME_UNIGRAM_PROPERTY_H diff --git a/native/jni/src/suggest/core/layout/proximity_info.cpp b/native/jni/src/suggest/core/layout/proximity_info.cpp index e64476d82..ee8e59ef9 100644 --- a/native/jni/src/suggest/core/layout/proximity_info.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info.cpp @@ -71,7 +71,7 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, && sweetSpotCenterYs && sweetSpotRadii), mProximityCharsArray(new int[GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE /* proximityCharsLength */]), - mCodeToKeyMap() { + mLowerCodePointToKeyMap() { /* Let's check the input array length here to make sure */ const jsize proximityCharsLength = env->GetArrayLength(proximityChars); if (proximityCharsLength != GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE) { @@ -147,7 +147,14 @@ int ProximityInfo::getCodePointOf(const int keyIndex) const { if (keyIndex < 0 || keyIndex >= KEY_COUNT) { return NOT_A_CODE_POINT; } - return mKeyIndexToCodePointG[keyIndex]; + return mKeyIndexToLowerCodePointG[keyIndex]; +} + +int ProximityInfo::getOriginalCodePointOf(const int keyIndex) const { + if (keyIndex < 0 || keyIndex >= KEY_COUNT) { + return NOT_A_CODE_POINT; + } + return mKeyIndexToOriginalCodePoint[keyIndex]; } void ProximityInfo::initializeG() { @@ -164,8 +171,9 @@ void ProximityInfo::initializeG() { const float gapY = sweetSpotCenterY - mCenterYsG[i]; mSweetSpotCenterYsG[i] = static_cast<int>(mCenterYsG[i] + gapY * verticalScale); } - mCodeToKeyMap[lowerCode] = i; - mKeyIndexToCodePointG[i] = lowerCode; + mLowerCodePointToKeyMap[lowerCode] = i; + mKeyIndexToOriginalCodePoint[i] = code; + mKeyIndexToLowerCodePointG[i] = lowerCode; } for (int i = 0; i < KEY_COUNT; i++) { mKeyKeyDistancesG[i][i] = 0; diff --git a/native/jni/src/suggest/core/layout/proximity_info.h b/native/jni/src/suggest/core/layout/proximity_info.h index f25949001..a91b9d674 100644 --- a/native/jni/src/suggest/core/layout/proximity_info.h +++ b/native/jni/src/suggest/core/layout/proximity_info.h @@ -39,6 +39,7 @@ class ProximityInfo { float getNormalizedSquaredDistanceFromCenterFloatG( const int keyId, const int x, const int y, const bool isGeometric) const; int getCodePointOf(const int keyIndex) const; + int getOriginalCodePointOf(const int keyIndex) const; bool hasSweetSpotData(const int keyIndex) const { // When there are no calibration data for a key, // the radius of the key is assigned to zero. @@ -76,11 +77,11 @@ class ProximityInfo { ProximityInfoUtils::initializeProximities(inputCodes, inputXCoordinates, inputYCoordinates, inputSize, mKeyXCoordinates, mKeyYCoordinates, mKeyWidths, mKeyHeights, mProximityCharsArray, CELL_HEIGHT, CELL_WIDTH, GRID_WIDTH, MOST_COMMON_KEY_WIDTH, - KEY_COUNT, mLocaleStr, &mCodeToKeyMap, allInputCodes); + KEY_COUNT, mLocaleStr, &mLowerCodePointToKeyMap, allInputCodes); } AK_FORCE_INLINE int getKeyIndexOf(const int c) const { - return ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, c, &mCodeToKeyMap); + return ProximityInfoUtils::getKeyIndexOf(KEY_COUNT, c, &mLowerCodePointToKeyMap); } AK_FORCE_INLINE bool isCodePointOnKeyboard(const int codePoint) const { @@ -117,9 +118,9 @@ class ProximityInfo { // Sweet spots for geometric input. Note that we have extra sweet spots only for Y coordinates. float mSweetSpotCenterYsG[MAX_KEY_COUNT_IN_A_KEYBOARD]; float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD]; - hash_map_compat<int, int> mCodeToKeyMap; - - int mKeyIndexToCodePointG[MAX_KEY_COUNT_IN_A_KEYBOARD]; + hash_map_compat<int, int> mLowerCodePointToKeyMap; + int mKeyIndexToOriginalCodePoint[MAX_KEY_COUNT_IN_A_KEYBOARD]; + int mKeyIndexToLowerCodePointG[MAX_KEY_COUNT_IN_A_KEYBOARD]; int mCenterXsG[MAX_KEY_COUNT_IN_A_KEYBOARD]; int mCenterYsG[MAX_KEY_COUNT_IN_A_KEYBOARD]; int mKeyKeyDistancesG[MAX_KEY_COUNT_IN_A_KEYBOARD][MAX_KEY_COUNT_IN_A_KEYBOARD]; diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp index fbabd92f2..bb4b41714 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp @@ -30,6 +30,12 @@ namespace latinime { +int ProximityInfoState::getPrimaryOriginalCodePointAt(const int index) const { + const int primaryCodePoint = getPrimaryCodePointAt(index); + const int keyIndex = mProximityInfo->getKeyIndexOf(primaryCodePoint); + return mProximityInfo->getOriginalCodePointOf(keyIndex); +} + // TODO: Remove the dependency of "isGeometric" void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize, diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h index c94060fa9..e941e43d8 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state.h @@ -65,6 +65,8 @@ class ProximityInfoState { return getProximityCodePointsAt(index)[0]; } + int getPrimaryOriginalCodePointAt(const int index) const; + inline bool sameAsTyped(const int *word, int length) const { if (length != mSampledInputSize) { return false; diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h index 5492c6070..b76b13971 100644 --- a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h @@ -29,12 +29,10 @@ class DictionaryHeaderStructurePolicy { public: virtual ~DictionaryHeaderStructurePolicy() {} - virtual bool supportsDynamicUpdate() const = 0; + virtual int getFormatVersionNumber() const = 0; virtual bool requiresGermanUmlautProcessing() const = 0; - virtual bool requiresFrenchLigatureProcessing() const = 0; - virtual float getMultiWordCostMultiplier() const = 0; virtual int getLastDecayedTime() const = 0; 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 41f82049f..c74a4ebbe 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 @@ -18,6 +18,8 @@ #define LATINIME_DICTIONARY_STRUCTURE_POLICY_H #include "defines.h" +#include "suggest/core/dictionary/unigram_property.h" +#include "utils/exclusive_ownership_pointer.h" namespace latinime { @@ -28,23 +30,25 @@ class DictionaryHeaderStructurePolicy; class DictionaryShortcutsStructurePolicy; /* - * This class abstracts structure of dictionaries. + * This class abstracts the structure of dictionaries. * Implement this policy to support additional dictionaries. */ class DictionaryStructureWithBufferPolicy { public: + typedef ExclusiveOwnershipPointer<DictionaryStructureWithBufferPolicy> StructurePolicyPtr; + virtual ~DictionaryStructureWithBufferPolicy() {} virtual int getRootPosition() const = 0; - virtual void createAndGetAllChildNodes(const DicNode *const dicNode, + virtual void createAndGetAllChildDicNodes(const DicNode *const dicNode, DicNodeVector *const childDicNodes) const = 0; virtual int getCodePointsAndProbabilityAndReturnCodePointCount( const int nodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) const = 0; - virtual int getTerminalNodePositionOfWord(const int *const inWord, + virtual int getTerminalPtNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const = 0; virtual int getProbability(const int unigramProbability, @@ -64,11 +68,13 @@ class DictionaryStructureWithBufferPolicy { // Returns whether the update was success or not. virtual bool addUnigramWord(const int *const word, const int length, - const int probability) = 0; + const int probability, const int *const shortcutTargetCodePoints, + const int shortcutLength, const int shortcutProbability, const bool isNotAWord, + const bool isBlacklisted,const int timestamp) = 0; // Returns whether the update was success or not. virtual bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability) = 0; + const int length1, const int probability, const int timestamp) = 0; // Returns whether the update was success or not. virtual bool removeBigramWords(const int *const word0, const int length0, @@ -82,9 +88,13 @@ class DictionaryStructureWithBufferPolicy { // Currently, this method is used only for testing. You may want to consider creating new // dedicated method instead of this if you want to use this in the production. - virtual void getProperty(const char *const query, char *const outResult, + virtual void getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength) = 0; + // Used for testing. + virtual const UnigramProperty getUnigramProperty(const int *const codePonts, + const int codePointCount) const = 0; + protected: DictionaryStructureWithBufferPolicy() {} diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index 0c4016893..c202b81fe 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -20,6 +20,7 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_profiler.h" #include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dictionary/error_type_utils.h" #include "suggest/core/session/dic_traverse_session.h" namespace latinime { @@ -82,8 +83,8 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n traverseSession, parentDicNode, dicNode, &inputStateG); const float languageCost = Weighting::getLanguageCost(weighting, correctionType, traverseSession, parentDicNode, dicNode, multiBigramMap); - const ErrorType errorType = weighting->getErrorType(correctionType, traverseSession, - parentDicNode, dicNode); + const ErrorTypeUtils::ErrorType errorType = weighting->getErrorType(correctionType, + traverseSession, parentDicNode, dicNode); profile(correctionType, dicNode); if (inputStateG.mNeedsToUpdateInputStateG) { dicNode->updateInputIndexG(&inputStateG); diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h index 2d49e98a6..bd6b3cf41 100644 --- a/native/jni/src/suggest/core/policy/weighting.h +++ b/native/jni/src/suggest/core/policy/weighting.h @@ -18,6 +18,7 @@ #define LATINIME_WEIGHTING_H #include "defines.h" +#include "suggest/core/dictionary/error_type_utils.h" namespace latinime { @@ -84,7 +85,7 @@ class Weighting { virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const = 0; - virtual ErrorType getErrorType(const CorrectionType correctionType, + virtual ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType, const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 50f2bbd8d..5070491f4 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -35,16 +35,16 @@ void DicTraverseSession::init(const Dictionary *const dictionary, const int *pre ->getMultiWordCostMultiplier(); mSuggestOptions = suggestOptions; if (!prevWord) { - mPrevWordPos = NOT_A_DICT_POS; + mPrevWordPtNodePos = NOT_A_DICT_POS; return; } // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call. - mPrevWordPos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord( + mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord( prevWord, prevWordLength, false /* forceLowerCaseSearch */); - if (mPrevWordPos == NOT_A_DICT_POS) { + if (mPrevWordPtNodePos == NOT_A_DICT_POS) { // Check bigrams for lower-cased previous word if original was not found. Useful for // auto-capitalized words like "The [current_word]". - mPrevWordPos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord( + mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord( prevWord, prevWordLength, true /* forceLowerCaseSearch */); } } diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index e0b1c67d9..6e4dda44d 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -59,7 +59,7 @@ class DicTraverseSession { } AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache) - : mPrevWordPos(NOT_A_DICT_POS), mProximityInfo(0), + : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(0), mDictionary(0), mSuggestOptions(0), mDicNodesCache(usesLargeCache), mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { @@ -86,11 +86,9 @@ class DicTraverseSession { //-------------------- const ProximityInfo *getProximityInfo() const { return mProximityInfo; } const SuggestOptions *getSuggestOptions() const { return mSuggestOptions; } - int getPrevWordPos() const { return mPrevWordPos; } + int getPrevWordPtNodePos() const { return mPrevWordPtNodePos; } // TODO: REMOVE - void setPrevWordPos(int pos) { mPrevWordPos = pos; } - // TODO: Use proper parameter when changed - int getDicRootPos() const { return 0; } + void setPrevWordPtNodePos(const int ptNodePos) { mPrevWordPtNodePos = ptNodePos; } DicNodesCache *getDicTraverseCache() { return &mDicNodesCache; } MultiBigramMap *getMultiBigramMap() { return &mMultiBigramMap; } const ProximityInfoState *getProximityInfoState(int id) const { @@ -119,26 +117,13 @@ class DicTraverseSession { return true; } - void getSearchKeys(const DicNode *node, std::vector<int> *const outputSearchKeyVector) const { - for (int i = 0; i < MAX_POINTER_COUNT_G; ++i) { - if (!mProximityInfoStates[i].isUsed()) { - continue; - } - const int pointerId = node->getInputIndex(i); - const std::vector<int> *const searchKeyVector = - mProximityInfoStates[i].getSearchKeyVector(pointerId); - outputSearchKeyVector->insert(outputSearchKeyVector->end(), searchKeyVector->begin(), - searchKeyVector->end()); - } - } - - ProximityType getProximityTypeG(const DicNode *const node, const int childCodePoint) const { + ProximityType getProximityTypeG(const DicNode *const dicNode, const int childCodePoint) const { ProximityType proximityType = UNRELATED_CHAR; for (int i = 0; i < MAX_POINTER_COUNT_G; ++i) { if (!mProximityInfoStates[i].isUsed()) { continue; } - const int pointerId = node->getInputIndex(i); + const int pointerId = dicNode->getInputIndex(i); proximityType = mProximityInfoStates[i].getProximityTypeG(pointerId, childCodePoint); ASSERT(proximityType == UNRELATED_CHAR || proximityType == MATCH_CHAR); // TODO: Make this more generic @@ -192,7 +177,7 @@ class DicTraverseSession { const int *const inputYs, const int *const times, const int *const pointerIds, const int inputSize, const float maxSpatialDistance, const int maxPointerCount); - int mPrevWordPos; + int mPrevWordPtNodePos; const ProximityInfo *mProximityInfo; const Dictionary *mDictionary; const SuggestOptions *mSuggestOptions; diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 73ccebc88..f84c84181 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -98,7 +98,7 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession, int commitPo // Continue suggestion after partial commit. DicNode *topDicNode = traverseSession->getDicTraverseCache()->setCommitPoint(commitPoint); - traverseSession->setPrevWordPos(topDicNode->getPrevWordNodePos()); + traverseSession->setPrevWordPtNodePos(topDicNode->getPrevWordPtNodePos()); traverseSession->getDicTraverseCache()->continueSearch(); traverseSession->setPartiallyCommited(); } @@ -109,7 +109,7 @@ void Suggest::initializeSearch(DicTraverseSession *traverseSession, int commitPo // Create a new dic node here DicNode rootNode; DicNodeUtils::initAsRoot(traverseSession->getDictionaryStructurePolicy(), - traverseSession->getPrevWordPos(), &rootNode); + traverseSession->getPrevWordPtNodePos(), &rootNode); traverseSession->getDicTraverseCache()->copyPushActive(&rootNode); } } @@ -231,12 +231,15 @@ int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequen BinaryDictionaryShortcutIterator shortcutIt( traverseSession->getDictionaryStructurePolicy()->getShortcutsStructurePolicy(), traverseSession->getDictionaryStructurePolicy() - ->getShortcutPositionOfPtNode(terminalDicNode->getPos())); + ->getShortcutPositionOfPtNode(terminalDicNode->getPtNodePos())); // Shortcut is not supported for multiple words suggestions. // TODO: Check shortcuts during traversal for multiple words suggestions. const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode); + const int shortcutBaseScore = SCORING->doesAutoCorrectValidWord() ? + SCORING->calculateFinalScore(compoundDistance, traverseSession->getInputSize(), + true /* forceCommit */) : finalScore; const int updatedOutputWordIndex = ShortcutUtils::outputShortcuts(&shortcutIt, - outputWordIndex, finalScore, outputCodePoints, frequencies, outputTypes, + outputWordIndex, shortcutBaseScore, outputCodePoints, frequencies, outputTypes, sameAsTyped); const int secondWordFirstInputIndex = terminalDicNode->getSecondWordFirstInputIndex( traverseSession->getProximityInfoState(0)); @@ -421,15 +424,15 @@ void Suggest::expandCurrentDicNodes(DicTraverseSession *traverseSession) const { } break; case UNRELATED_CHAR: - // Just drop this node and do nothing. + // Just drop this dicNode and do nothing. break; default: - // Just drop this node and do nothing. + // Just drop this dicNode and do nothing. break; } } - // Push the node for look-ahead correction + // Push the dicNode for look-ahead correction if (allowsErrorCorrections && canDoLookAheadCorrection) { traverseSession->getDicTraverseCache()->copyPushNextActive(&dicNode); } @@ -442,7 +445,7 @@ void Suggest::processTerminalDicNode( if (dicNode->getCompoundDistance() >= static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) { return; } - if (!dicNode->isTerminalWordNode()) { + if (!dicNode->isTerminalDicNode()) { return; } if (dicNode->shouldBeFilteredBySafetyNetForBigram()) { @@ -463,7 +466,7 @@ void Suggest::processTerminalDicNode( /** * Adds the expanded dicNode to the next search priority queue. Also creates an additional next word - * (by the space omission error correction) search path if input dicNode is on a terminal node. + * (by the space omission error correction) search path if input dicNode is on a terminal. */ void Suggest::processExpandedDicNode( DicTraverseSession *traverseSession, DicNode *dicNode) const { @@ -505,7 +508,7 @@ void Suggest::processDicNodeAsSubstitution(DicTraverseSession *traverseSession, processExpandedDicNode(traverseSession, childDicNode); } -// Process the node codepoint as a digraph. This means that composite glyphs like the German +// Process the DicNode codepoint as a digraph. This means that composite glyphs like the German // u-umlaut is expanded to the transliteration "ue". Note that this happens in parallel with // the normal non-digraph traversal, so both "uber" and "ueber" can be corrected to "[u-umlaut]ber". void Suggest::processDicNodeAsDigraph(DicTraverseSession *traverseSession, @@ -518,7 +521,7 @@ void Suggest::processDicNodeAsDigraph(DicTraverseSession *traverseSession, /** * Handle the dicNode as an omission error (e.g., ths => this). Skip the current letter and consider * matches for all possible next letters. Note that just skipping the current letter without any - * other conditions tends to flood the search dic nodes cache with omission nodes. Instead, check + * other conditions tends to flood the search DicNodes cache with omission DicNodes. Instead, check * the possible *next* letters after the omission to better limit search to plausible omissions. * Note that apostrophes are handled as omissions. */ @@ -605,7 +608,7 @@ void Suggest::processDicNodeAsTransposition(DicTraverseSession *traverseSession, } /** - * Weight child node by aligning it to the key + * Weight child dicNode by aligning it to the key */ void Suggest::weightChildNode(DicTraverseSession *traverseSession, DicNode *dicNode) const { const int inputSize = traverseSession->getInputSize(); 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 1926b9831..7d0d09631 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,6 @@ #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" @@ -38,7 +37,6 @@ const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::FLAG_ATTRI // Mask for attribute probability, stored on 4 bits inside the flags byte. const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F; -const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4; /* static */ void BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( const uint8_t *const bigramsBuf, BigramFlags *const outBigramFlags, @@ -79,11 +77,6 @@ const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4; offset = ByteArrayUtils::readUint24AndAdvancePosition(bigramsBuf, pos); break; } - 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; } else { @@ -91,92 +84,4 @@ 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 eabe4e099..7e1038300 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 @@ -45,34 +45,6 @@ public: // Bigrams reading methods 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) { - return (flags & MASK_ATTRIBUTE_ADDRESS_TYPE) >> ATTRIBUTE_ADDRESS_SHIFT; - /* Note: this is a value-dependant optimization of what may probably be - more readably written this way: - switch (flags * BinaryFormat::MASK_ATTRIBUTE_ADDRESS_TYPE) { - case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: return 1; - case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: return 2; - case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTE: return 3; - default: return 0; - } - */ - } - - 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); - } - - 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); @@ -83,11 +55,6 @@ private: static const BigramFlags FLAG_ATTRIBUTE_OFFSET_NEGATIVE; static const BigramFlags FLAG_ATTRIBUTE_HAS_NEXT; 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; @@ -95,8 +62,6 @@ private: 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 deleted file mode 100644 index b1170e251..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp +++ /dev/null @@ -1,391 +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/bigram/dynamic_bigram_list_policy.h" - -#include "suggest/core/policy/dictionary_shortcuts_structure_policy.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" -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" - -namespace latinime { - -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 bigramEntryPos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramEntryPos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - if (usesAdditionalBuffer) { - *bigramEntryPos -= mBuffer->getOriginalBufferSize(); - } - BigramListReadWriteUtils::BigramFlags bigramFlags; - int originalBigramPos; - BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(buffer, &bigramFlags, - &originalBigramPos, bigramEntryPos); - if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { - originalBigramPos += mBuffer->getOriginalBufferSize(); - } - *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags); - *outHasNext = BigramListReadWriteUtils::hasNext(bigramFlags); - if (mIsDecayingDict && !ForgettingCurveUtils::isValidEncodedProbability(*outProbability)) { - // This bigram is too weak to output. - *outBigramPos = NOT_A_DICT_POS; - } else { - *outBigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - } - if (usesAdditionalBuffer) { - *bigramEntryPos += mBuffer->getOriginalBufferSize(); - } -} - -void DynamicBigramListPolicy::skipAllBigrams(int *const bigramListPos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - if (usesAdditionalBuffer) { - *bigramListPos -= mBuffer->getOriginalBufferSize(); - } - BigramListReadWriteUtils::skipExistingBigrams(buffer, bigramListPos); - if (usesAdditionalBuffer) { - *bigramListPos += mBuffer->getOriginalBufferSize(); - } -} - -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 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. - int originalBigramPos; - BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( - mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, - fromPos); - if (originalBigramPos == NOT_A_DICT_POS) { - // skip invalid bigram entry. - continue; - } - if (usesAdditionalBuffer) { - originalBigramPos += mBuffer->getOriginalBufferSize(); - } - const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - if (bigramPos == NOT_A_DICT_POS) { - // Target PtNode has been invalidated. - continue; - } - lastWrittenEntryPos = *toPos; - if (!BigramListReadWriteUtils::createAndWriteBigramEntry(bufferToWrite, bigramPos, - BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags), - BigramListReadWriteUtils::hasNext(bigramFlags), toPos)) { - return false; - } - (*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; - } - } - if (usesAdditionalBuffer) { - *fromPos += mBuffer->getOriginalBufferSize(); - } - return true; -} - -// 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, int *const outValidBigramEntryCount) { - 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); - 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 /* targetPtNodePos */, &bigramEntryPos)) { - return false; - } - continue; - } - bool isRemoved = false; - if (!updateProbabilityForDecay(bigramFlags, bigramTargetNodePos, &bigramEntryPos, - &isRemoved)) { - return false; - } - if (!isRemoved) { - (*outValidBigramEntryCount) += 1; - } - } 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, int *const outBigramEntryCount) { - 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 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)); - (*outBigramEntryCount) = bigramEntryCount; - return true; -} - -bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramTargetPos, - const int probability, int *const bigramListPos, bool *const outAddedNewBigram) { - 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. - BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( - mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, - bigramListPos); - if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { - originalBigramPos += mBuffer->getOriginalBufferSize(); - } - if (followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos) == bigramTargetPos) { - // Update this bigram entry. - *outAddedNewBigram = false; - const int originalProbability = BigramListReadWriteUtils::getProbabilityFromFlags( - bigramFlags); - const int probabilityToWrite = mIsDecayingDict ? - ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability, - probability) : probability; - const BigramListReadWriteUtils::BigramFlags updatedFlags = - BigramListReadWriteUtils::setProbabilityInFlags(bigramFlags, - probabilityToWrite); - return BigramListReadWriteUtils::writeBigramEntry(mBuffer, updatedFlags, - originalBigramPos, &entryPos); - } - if (BigramListReadWriteUtils::hasNext(bigramFlags)) { - continue; - } - // The current last entry is found. - // First, update the flags of the last entry. - if (!BigramListReadWriteUtils::setHasNextFlag(mBuffer, true /* hasNext */, entryPos)) { - *outAddedNewBigram = false; - return false; - } - if (usesAdditionalBuffer) { - *bigramListPos += mBuffer->getOriginalBufferSize(); - } - // Then, add a new entry after the last entry. - *outAddedNewBigram = true; - 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 bigramTargetPos, const int probability, - int *const writingPos) { - // hasNext is false because we are adding a new bigram entry at the end of the bigram list. - const int probabilityToWrite = mIsDecayingDict ? - ForgettingCurveUtils::getUpdatedEncodedProbability(NOT_A_PROBABILITY, probability) : - probability; - return BigramListReadWriteUtils::createAndWriteBigramEntry(mBuffer, bigramTargetPos, - probabilityToWrite, false /* hasNext */, writingPos); -} - -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 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. - BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( - mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, &pos); - if (usesAdditionalBuffer) { - bigramEntryPos += mBuffer->getOriginalBufferSize(); - } - if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) { - originalBigramPos += mBuffer->getOriginalBufferSize(); - } - const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos); - if (bigramPos != bigramTargetPos) { - continue; - } - // 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; -} - -int DynamicBigramListPolicy::followBigramLinkAndGetCurrentBigramPtNodePos( - const int originalBigramPos) const { - if (originalBigramPos == NOT_A_DICT_POS) { - return NOT_A_DICT_POS; - } - int currentPos = originalBigramPos; - DynamicPatriciaTrieNodeReader nodeReader(mBuffer, this /* bigramsPolicy */, mShortcutPolicy); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos); - int bigramLinkCount = 0; - while (nodeReader.getBigramLinkedNodePos() != NOT_A_DICT_POS) { - currentPos = nodeReader.getBigramLinkedNodePos(); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos); - bigramLinkCount++; - if (bigramLinkCount > CONTINUING_BIGRAM_LINK_COUNT_LIMIT) { - AKLOGE("Bigram link is invalid. start position: %d", originalBigramPos); - ASSERT(false); - return NOT_A_DICT_POS; - } - } - return currentPos; -} - -bool DynamicBigramListPolicy::updateProbabilityForDecay( - const BigramListReadWriteUtils::BigramFlags bigramFlags, const int targetPtNodePos, - int *const bigramEntryPos, bool *const outRemoved) const { - *outRemoved = false; - if (mIsDecayingDict) { - // Update bigram probability for decaying. - const int newProbability = ForgettingCurveUtils::getEncodedProbabilityToSave( - BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags), mHeaderPolicy); - if (ForgettingCurveUtils::isValidEncodedProbability(newProbability)) { - // Write new probability. - const BigramListReadWriteUtils::BigramFlags updatedBigramFlags = - BigramListReadWriteUtils::setProbabilityInFlags( - bigramFlags, newProbability); - if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, updatedBigramFlags, - targetPtNodePos, bigramEntryPos)) { - return false; - } - } else { - // Remove current bigram entry. - *outRemoved = true; - if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags, - NOT_A_DICT_POS /* targetPtNodePos */, bigramEntryPos)) { - return false; - } - } - } - return true; -} - -} // namespace latinime 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 deleted file mode 100644 index 0504b59d5..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h +++ /dev/null @@ -1,92 +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. - */ - -#ifndef LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H -#define LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H - -#include <stdint.h> - -#include "defines.h" -#include "suggest/core/policy/dictionary_bigrams_structure_policy.h" -#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h" - -namespace latinime { - -class BufferWithExtendableBuffer; -class DictionaryHeaderStructurePolicy; -class DictionaryShortcutsStructurePolicy; - -/* - * This is a dynamic version of BigramListPolicy and supports an additional buffer. - */ -class DynamicBigramListPolicy : public DictionaryBigramsStructurePolicy { - public: - DynamicBigramListPolicy(const DictionaryHeaderStructurePolicy *const headerPolicy, - BufferWithExtendableBuffer *const buffer, - const DictionaryShortcutsStructurePolicy *const shortcutPolicy, - const bool isDecayingDict) - : mHeaderPolicy(headerPolicy), mBuffer(buffer), mShortcutPolicy(shortcutPolicy), - mIsDecayingDict(isDecayingDict) {} - - ~DynamicBigramListPolicy() {} - - void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, - 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; - - bool updateAllBigramEntriesAndDeleteUselessEntries(int *const bigramListPos, - int *const outBigramEntryCount); - - bool updateAllBigramTargetPtNodePositions(int *const bigramListPos, - const DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap *const - ptNodePositionRelocationMap, int *const outValidBigramEntryCount); - - bool addNewBigramEntryToBigramList(const int bigramTargetPos, const int probability, - int *const bigramListPos, bool *const outAddedNewBigram); - - bool writeNewBigramEntry(const int bigramTargetPos, const int probability, - int *const writingPos); - - // Return whether or not targetBigramPos is found. - bool removeBigram(const int bigramListPos, const int bigramTargetPos); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicBigramListPolicy); - - static const int CONTINUING_BIGRAM_LINK_COUNT_LIMIT; - static const int BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT; - - const DictionaryHeaderStructurePolicy *const mHeaderPolicy; - BufferWithExtendableBuffer *const mBuffer; - const DictionaryShortcutsStructurePolicy *const mShortcutPolicy; - const bool mIsDecayingDict; - - // Follow bigram link and return the position of bigram target PtNode that is currently valid. - int followBigramLinkAndGetCurrentBigramPtNodePos(const int originalBigramPos) const; - - bool updateProbabilityForDecay(const BigramListReadWriteUtils::BigramFlags bigramFlags, - const int targetPtNodePos, int *const bigramEntryPos, bool *const outRemoved) const; -}; -} // namespace latinime -#endif // LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp new file mode 100644 index 000000000..cd2243025 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.cpp @@ -0,0 +1,240 @@ +/* + * 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/bigram/ver4_bigram_list_policy.h" + +#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { + +void Ver4BigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability, + bool *const outHasNext, int *const bigramEntryPos) const { + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(bigramEntryPos); + if (outBigramPos) { + // Lookup target PtNode position. + *outBigramPos = mTerminalPositionLookupTable->getTerminalPtNodePosition( + bigramEntry.getTargetTerminalId()); + } + if (outProbability) { + if (bigramEntry.hasHistoricalInfo()) { + *outProbability = + ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo()); + } else { + *outProbability = bigramEntry.getProbability(); + } + } + if (outHasNext) { + *outHasNext = bigramEntry.hasNext(); + } +} + +bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTargetTerminalId, + const int newProbability, const int timestamp, bool *const outAddedNewEntry) { + if (outAddedNewEntry) { + *outAddedNewEntry = false; + } + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Updating PtNode doesn't have a bigram list. + // Create new bigram list. + if (!mBigramDictContent->createNewBigramList(terminalId)) { + return false; + } + const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY, + newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(&newBigramEntry, + newProbability, timestamp); + // Write an entry. + const int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (!mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, writingPos)) { + return false; + } + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + return true; + } + + const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos); + if (entryPosToUpdate != NOT_A_DICT_POS) { + // Overwrite existing entry. + const BigramEntry originalBigramEntry = + mBigramDictContent->getBigramEntry(entryPosToUpdate); + if (!originalBigramEntry.isValid()) { + // Reuse invalid entry. + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + } + const BigramEntry updatedBigramEntry = + originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( + &updatedBigramEntry, newProbability, timestamp); + return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate); + } + + // Add new entry to the bigram list. + // Create new bigram list. + if (!mBigramDictContent->createNewBigramList(terminalId)) { + return false; + } + // Write new entry at a head position of the bigram list. + int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId); + const BigramEntry newBigramEntry(true /* hasNext */, NOT_A_PROBABILITY, newTargetTerminalId); + const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom( + &newBigramEntry, newProbability, timestamp); + if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(&bigramEntryToWrite, &writingPos)) { + return false; + } + if (outAddedNewEntry) { + *outAddedNewEntry = true; + } + // Append existing entries by copying. + return mBigramDictContent->copyBigramList(bigramListPos, writingPos); +} + +bool Ver4BigramListPolicy::removeEntry(const int terminalId, const int targetTerminalId) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return false; + } + const int entryPosToUpdate = getEntryPosToUpdate(targetTerminalId, bigramListPos); + if (entryPosToUpdate == NOT_A_DICT_POS) { + // Bigram entry doesn't exist. + return false; + } + const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate); + if (targetTerminalId != bigramEntry.getTargetTerminalId()) { + // Bigram entry doesn't exist. + return false; + } + // Remove bigram entry by marking it as invalid entry and overwriting the original entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPosToUpdate); +} + +bool Ver4BigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId, + int *const outBigramCount) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return true; + } + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!bigramEntry.isValid()) { + continue; + } + const int targetPtNodePos = mTerminalPositionLookupTable->getTerminalPtNodePosition( + bigramEntry.getTargetTerminalId()); + if (targetPtNodePos == NOT_A_DICT_POS) { + // Invalidate bigram entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + } else if (bigramEntry.hasHistoricalInfo()) { + const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( + bigramEntry.getHistoricalInfo()); + if (ForgettingCurveUtils::needsToKeep(&historicalInfo)) { + const BigramEntry updatedBigramEntry = + bigramEntry.updateHistoricalInfoAndGetEntry(&historicalInfo); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + *outBigramCount += 1; + } else { + // Remove entry. + const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) { + return false; + } + } + } else { + *outBigramCount += 1; + } + } + return true; +} + +int Ver4BigramListPolicy::getBigramEntryConut(const int terminalId) { + const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId); + if (bigramListPos == NOT_A_DICT_POS) { + // Bigram list doesn't exist. + return 0; + } + int bigramCount = 0; + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (bigramEntry.isValid()) { + bigramCount++; + } + } + return bigramCount; +} + +int Ver4BigramListPolicy::getEntryPosToUpdate(const int targetTerminalIdToFind, + const int bigramListPos) const { + bool hasNext = true; + int invalidEntryPos = NOT_A_DICT_POS; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (bigramEntry.getTargetTerminalId() == targetTerminalIdToFind) { + // Entry with same target is found. + return entryPos; + } else if (!bigramEntry.isValid()) { + // Invalid entry that can be reused is found. + invalidEntryPos = entryPos; + } + } + return invalidEntryPos; +} + +const BigramEntry Ver4BigramListPolicy::createUpdatedBigramEntryFrom( + const BigramEntry *const originalBigramEntry, const int newProbability, + const int timestamp) const { + // TODO: Consolidate historical info and probability. + if (mHeaderPolicy->hasHistoricalInfoOfWords()) { + const HistoricalInfo updatedHistoricalInfo = + ForgettingCurveUtils::createUpdatedHistoricalInfo( + originalBigramEntry->getHistoricalInfo(), newProbability, timestamp); + return originalBigramEntry->updateHistoricalInfoAndGetEntry(&updatedHistoricalInfo); + } else { + return originalBigramEntry->updateProbabilityAndGetEntry(newProbability); + } +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h new file mode 100644 index 000000000..5b6c5a173 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h @@ -0,0 +1,69 @@ +/* + * 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_VER4_BIGRAM_LIST_POLICY_H +#define LATINIME_VER4_BIGRAM_LIST_POLICY_H + +#include "defines.h" +#include "suggest/core/policy/dictionary_bigrams_structure_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h" + +namespace latinime { + +class BigramDictContent; +class HeaderPolicy; +class TerminalPositionLookupTable; + +class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { + public: + Ver4BigramListPolicy(BigramDictContent *const bigramDictContent, + const TerminalPositionLookupTable *const terminalPositionLookupTable, + const HeaderPolicy *const headerPolicy) + : mBigramDictContent(bigramDictContent), + mTerminalPositionLookupTable(terminalPositionLookupTable), + mHeaderPolicy(headerPolicy) {} + + void getNextBigram(int *const outBigramPos, int *const outProbability, + bool *const outHasNext, int *const bigramEntryPos) const; + + void skipAllBigrams(int *const pos) const { + // Do nothing because we don't need to skip bigram lists in ver4 dictionaries. + } + + bool addNewEntry(const int terminalId, const int newTargetTerminalId, const int newProbability, + const int timestamp, bool *const outAddedNewEntry); + + bool removeEntry(const int terminalId, const int targetTerminalId); + + bool updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId, + int *const outBigramCount); + + int getBigramEntryConut(const int terminalId); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4BigramListPolicy); + + int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos) const; + + const BigramEntry createUpdatedBigramEntryFrom(const BigramEntry *const originalBigramEntry, + const int newProbability, const int timestamp) const; + + BigramDictContent *const mBigramDictContent; + const TerminalPositionLookupTable *const mTerminalPositionLookupTable; + const HeaderPolicy *const mHeaderPolicy; +}; +} // namespace latinime +#endif /* LATINIME_VER4_BIGRAM_LIST_POLICY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp deleted file mode 100644 index ff80dd2f6..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp +++ /dev/null @@ -1,53 +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/dictionary_structure_with_buffer_policy_factory.h" - -#include <stdint.h> - -#include "defines.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h" -#include "suggest/policyimpl/dictionary/patricia_trie_policy.h" -#include "suggest/policyimpl/dictionary/utils/format_utils.h" -#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" - -namespace latinime { - -/* static */ DictionaryStructureWithBufferPolicy *DictionaryStructureWithBufferPolicyFactory - ::newDictionaryStructureWithBufferPolicy(const char *const path, const int bufOffset, - const int size, const bool isUpdatable) { - // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of - // impl classes of DictionaryStructureWithBufferPolicy. - const MmappedBuffer *const mmapedBuffer = MmappedBuffer::openBuffer(path, bufOffset, size, - isUpdatable); - if (!mmapedBuffer) { - return 0; - } - switch (FormatUtils::detectFormatVersion(mmapedBuffer->getBuffer(), - mmapedBuffer->getBufferSize())) { - case FormatUtils::VERSION_2: - return new PatriciaTriePolicy(mmapedBuffer); - case FormatUtils::VERSION_3: - return new DynamicPatriciaTriePolicy(mmapedBuffer); - default: - AKLOGE("DICT: dictionary format is unknown, bad magic number"); - delete mmapedBuffer; - ASSERT(false); - return 0; - } -} - -} // namespace latinime 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 deleted file mode 100644 index 5724c5d88..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp +++ /dev/null @@ -1,191 +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/dynamic_patricia_trie_gc_event_listeners.h" - -#include "suggest/core/policy/dictionary_header_structure_policy.h" -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.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 (node->isTerminal() && mIsDecayingDict) { - const int newProbability = - ForgettingCurveUtils::getEncodedProbabilityToSave(node->getProbability(), - mHeaderPolicy); - int writingPos = node->getProbabilityFieldPos(); - // Update probability. - if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition( - mBuffer, newProbability, &writingPos)) { - return false; - } - if (!ForgettingCurveUtils::isValidEncodedProbability(newProbability)) { - isUselessPtNode = true; - } - } - 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 { - mValueStack.back() += 1; - if (node->isTerminal()) { - mValidUnigramCount += 1; - } - } - return true; -} - -bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateBigramProbability - ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, - const int *const nodeCodePoints) { - if (!node->isDeleted()) { - int pos = node->getBigramsPos(); - if (pos != NOT_A_DICT_POS) { - int bigramEntryCount = 0; - if (!mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries(&pos, - &bigramEntryCount)) { - return false; - } - mValidBigramEntryCount += bigramEntryCount; - } - } - 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) { - int bigramEntryCount; - if (!mBigramPolicy->updateAllBigramTargetPtNodePositions(&bigramsPos, - &mDictPositionRelocationMap->mPtNodePositionRelocationMap, &bigramEntryCount)) { - return false; - } - mBigramCount += bigramEntryCount; - } - if (node->isTerminal()) { - mUnigramCount++; - } - - return true; -} - -} // namespace latinime 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 deleted file mode 100644 index 2fa3111d3..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp +++ /dev/null @@ -1,124 +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/dynamic_patricia_trie_node_reader.h" - -#include "suggest/core/policy/dictionary_bigrams_structure_policy.h" -#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" - -namespace latinime { - -void DynamicPatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProcessMovedPtNode( - const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) { - if (ptNodePos < 0 || ptNodePos >= mBuffer->getTailPosition()) { - // Reading invalid position because of bug or broken dictionary. - AKLOGE("Fetching PtNode info from invalid dictionary position: %d, dictionary size: %d", - ptNodePos, mBuffer->getTailPosition()); - ASSERT(false); - invalidatePtNodeInfo(); - return; - } - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodePos); - const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); - int pos = ptNodePos; - mHeadPos = ptNodePos; - if (usesAdditionalBuffer) { - pos -= mBuffer->getOriginalBufferSize(); - } - mFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); - const int parentPosOffset = - DynamicPatriciaTrieReadingUtils::getParentPtNodePosOffsetAndAdvancePosition(dictBuf, - &pos); - mParentPos = DynamicPatriciaTrieReadingUtils::getParentPtNodePos(parentPosOffset, mHeadPos); - if (outCodePoints != 0) { - mCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( - dictBuf, mFlags, maxCodePointCount, outCodePoints, &pos); - } else { - mCodePointCount = PatriciaTrieReadingUtils::skipCharacters( - dictBuf, mFlags, MAX_WORD_LENGTH, &pos); - } - if (isTerminal()) { - mProbabilityFieldPos = pos; - if (usesAdditionalBuffer) { - mProbabilityFieldPos += mBuffer->getOriginalBufferSize(); - } - mProbability = PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictBuf, &pos); - } else { - mProbabilityFieldPos = NOT_A_DICT_POS; - mProbability = NOT_A_PROBABILITY; - } - mChildrenPosFieldPos = pos; - if (usesAdditionalBuffer) { - mChildrenPosFieldPos += mBuffer->getOriginalBufferSize(); - } - mChildrenPos = DynamicPatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition( - dictBuf, &pos); - if (usesAdditionalBuffer && mChildrenPos != NOT_A_DICT_POS) { - mChildrenPos += mBuffer->getOriginalBufferSize(); - } - if (mSiblingPos == NOT_A_DICT_POS) { - if (DynamicPatriciaTrieReadingUtils::isMoved(mFlags)) { - mBigramLinkedNodePos = mChildrenPos; - } else { - mBigramLinkedNodePos = NOT_A_DICT_POS; - } - } - if (usesAdditionalBuffer) { - pos += mBuffer->getOriginalBufferSize(); - } - if (PatriciaTrieReadingUtils::hasShortcutTargets(mFlags)) { - mShortcutPos = pos; - mShortcutsPolicy->skipAllShortcuts(&pos); - } else { - mShortcutPos = NOT_A_DICT_POS; - } - if (PatriciaTrieReadingUtils::hasBigrams(mFlags)) { - mBigramPos = pos; - mBigramsPolicy->skipAllBigrams(&pos); - } else { - mBigramPos = NOT_A_DICT_POS; - } - // Update siblingPos if needed. - if (mSiblingPos == NOT_A_DICT_POS) { - // Sibling position is the tail position of current node. - mSiblingPos = pos; - } - // 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. - fetchPtNodeInfoFromBufferAndProcessMovedPtNode(mParentPos, maxCodePointCount, - outCodePoints); - } -} - -void DynamicPatriciaTrieNodeReader::invalidatePtNodeInfo() { - mHeadPos = NOT_A_DICT_POS; - mFlags = 0; - mParentPos = NOT_A_DICT_POS; - mCodePointCount = 0; - mProbabilityFieldPos = NOT_A_DICT_POS; - mProbability = NOT_A_PROBABILITY; - mChildrenPosFieldPos = NOT_A_DICT_POS; - mChildrenPos = NOT_A_DICT_POS; - mBigramLinkedNodePos = NOT_A_DICT_POS; - mShortcutPos = NOT_A_DICT_POS; - mBigramPos = NOT_A_DICT_POS; - mSiblingPos = NOT_A_DICT_POS; -} - -} 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 deleted file mode 100644 index 3b36d425f..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h +++ /dev/null @@ -1,163 +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. - */ - -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H - -#include <stdint.h> - -#include "defines.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" -#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" - -namespace latinime { - -class BufferWithExtendableBuffer; -class DictionaryBigramsStructurePolicy; -class DictionaryShortcutsStructurePolicy; - -/* - * This class is used for helping to read nodes of dynamic patricia trie. This class handles moved - * node and reads node attributes. - */ -class DynamicPatriciaTrieNodeReader { - public: - DynamicPatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer, - const DictionaryBigramsStructurePolicy *const bigramsPolicy, - const DictionaryShortcutsStructurePolicy *const shortcutsPolicy) - : mBuffer(buffer), mBigramsPolicy(bigramsPolicy), - mShortcutsPolicy(shortcutsPolicy), mHeadPos(NOT_A_DICT_POS), mFlags(0), - mParentPos(NOT_A_DICT_POS), mCodePointCount(0), mProbabilityFieldPos(NOT_A_DICT_POS), - mProbability(NOT_A_PROBABILITY), mChildrenPosFieldPos(NOT_A_DICT_POS), - mChildrenPos(NOT_A_DICT_POS), mBigramLinkedNodePos(NOT_A_DICT_POS), - mShortcutPos(NOT_A_DICT_POS), mBigramPos(NOT_A_DICT_POS), - mSiblingPos(NOT_A_DICT_POS) {} - - ~DynamicPatriciaTrieNodeReader() {} - - // 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 fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints( - const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) { - mSiblingPos = NOT_A_DICT_POS; - mBigramLinkedNodePos = NOT_A_DICT_POS; - fetchPtNodeInfoFromBufferAndProcessMovedPtNode(ptNodePos, maxCodePointCount, outCodePoints); - } - - // HeadPos is different from NodePos when the current PtNode is a moved PtNode. - AK_FORCE_INLINE int getHeadPos() const { - return mHeadPos; - } - - // Flags - AK_FORCE_INLINE bool isDeleted() const { - return DynamicPatriciaTrieReadingUtils::isDeleted(mFlags); - } - - AK_FORCE_INLINE bool hasChildren() const { - return mChildrenPos != NOT_A_DICT_POS; - } - - AK_FORCE_INLINE bool isTerminal() const { - return PatriciaTrieReadingUtils::isTerminal(mFlags); - } - - AK_FORCE_INLINE bool isBlacklisted() const { - return PatriciaTrieReadingUtils::isBlacklisted(mFlags); - } - - AK_FORCE_INLINE bool isNotAWord() const { - return PatriciaTrieReadingUtils::isNotAWord(mFlags); - } - - // Parent node position - AK_FORCE_INLINE int getParentPos() const { - return mParentPos; - } - - // Number of code points - AK_FORCE_INLINE uint8_t getCodePointCount() const { - return mCodePointCount; - } - - // Probability - AK_FORCE_INLINE int getProbabilityFieldPos() const { - return mProbabilityFieldPos; - } - - AK_FORCE_INLINE int getProbability() const { - return mProbability; - } - - // Children PtNode array position - AK_FORCE_INLINE int getChildrenPosFieldPos() const { - return mChildrenPosFieldPos; - } - - AK_FORCE_INLINE int getChildrenPos() const { - return mChildrenPos; - } - - // Bigram linked node position. - AK_FORCE_INLINE int getBigramLinkedNodePos() const { - return mBigramLinkedNodePos; - } - - // Shortcutlist position - AK_FORCE_INLINE int getShortcutPos() const { - return mShortcutPos; - } - - // Bigrams position - AK_FORCE_INLINE int getBigramsPos() const { - return mBigramPos; - } - - // Sibling node position - AK_FORCE_INLINE int getSiblingNodePos() const { - return mSiblingPos; - } - - private: - DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieNodeReader); - - const BufferWithExtendableBuffer *const mBuffer; - const DictionaryBigramsStructurePolicy *const mBigramsPolicy; - const DictionaryShortcutsStructurePolicy *const mShortcutsPolicy; - int mHeadPos; - DynamicPatriciaTrieReadingUtils::NodeFlags mFlags; - int mParentPos; - uint8_t mCodePointCount; - int mProbabilityFieldPos; - int mProbability; - int mChildrenPosFieldPos; - int mChildrenPos; - int mBigramLinkedNodePos; - int mShortcutPos; - int mBigramPos; - int mSiblingPos; - - void fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos, - const int maxCodePointCount, int *const outCodePoints); - - void invalidatePtNodeInfo(); -}; -} // namespace latinime -#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H */ 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 deleted file mode 100644 index 495b146c2..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp +++ /dev/null @@ -1,380 +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/dynamic_patricia_trie_policy.h" - -#include <cstdio> -#include <cstring> -#include <ctime> - -#include "defines.h" -#include "suggest/core/dicnode/dic_node.h" -#include "suggest/core/dicnode/dic_node_vector.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_helper.h" -#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" -#include "suggest/policyimpl/dictionary/utils/probability_utils.h" - -namespace latinime { - -// Note that these are corresponding definitions in Java side in BinaryDictionaryTests and -// BinaryDictionaryDecayingTests. -const char *const DynamicPatriciaTriePolicy::UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT"; -const char *const DynamicPatriciaTriePolicy::BIGRAM_COUNT_QUERY = "BIGRAM_COUNT"; -const char *const DynamicPatriciaTriePolicy::MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT"; -const char *const DynamicPatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; -const char *const DynamicPatriciaTriePolicy::SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY = - "SET_NEEDS_TO_DECAY_FOR_TESTING"; -const int DynamicPatriciaTriePolicy::MAX_DICT_EXTENDED_REGION_SIZE = 1024 * 1024; -const int DynamicPatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS = - DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE - 1024; - -void DynamicPatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, - DicNodeVector *const childDicNodes) const { - if (!dicNode->hasChildren()) { - return; - } - DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPos()); - const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); - while (!readingHelper.isEnd()) { - bool isTerminal = nodeReader->isTerminal() && !nodeReader->isDeleted(); - if (isTerminal && mHeaderPolicy.isDecayingDict()) { - // A DecayingDict may have a terminal PtNode that has a terminal DicNode whose - // probability is NOT_A_PROBABILITY. In such case, we don't want to treat it as a - // valid terminal DicNode. - isTerminal = getProbability(nodeReader->getProbability(), NOT_A_PROBABILITY) - != NOT_A_PROBABILITY; - } - childDicNodes->pushLeavingChild(dicNode, nodeReader->getHeadPos(), - nodeReader->getChildrenPos(), nodeReader->getProbability(), isTerminal, - nodeReader->hasChildren(), nodeReader->isBlacklisted() || nodeReader->isNotAWord(), - nodeReader->getCodePointCount(), readingHelper.getMergedNodeCodePoints()); - readingHelper.readNextSiblingNode(); - } -} - -int DynamicPatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( - 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. - int reverseCodePoints[maxCodePointCount]; - DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - // First, read the terminal node and get its probability. - readingHelper.initWithPtNodePos(ptNodePos); - if (!readingHelper.isValidTerminalNode()) { - // Node at the ptNodePos is not a valid terminal node. - *outUnigramProbability = NOT_A_PROBABILITY; - return 0; - } - // Store terminal node probability. - *outUnigramProbability = readingHelper.getNodeReader()->getProbability(); - // Then, following parent node link to the dictionary root and fetch node code points. - while (!readingHelper.isEnd()) { - if (readingHelper.getTotalCodePointCount() > maxCodePointCount) { - // The ptNodePos is not a valid terminal node position in the dictionary. - *outUnigramProbability = NOT_A_PROBABILITY; - return 0; - } - // Store node code points to buffer in the reverse order. - readingHelper.fetchMergedNodeCodePointsInReverseOrder( - readingHelper.getPrevTotalCodePointCount(), reverseCodePoints); - // Follow parent node toward the root node. - readingHelper.readParentNode(); - } - if (readingHelper.isError()) { - // The node position or the dictionary is invalid. - *outUnigramProbability = NOT_A_PROBABILITY; - return 0; - } - // Reverse the stored code points to output them. - const int codePointCount = readingHelper.getTotalCodePointCount(); - for (int i = 0; i < codePointCount; ++i) { - outCodePoints[i] = reverseCodePoints[codePointCount - i - 1]; - } - return codePointCount; -} - -int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const inWord, - const int length, const bool forceLowerCaseSearch) const { - int searchCodePoints[length]; - for (int i = 0; i < length; ++i) { - searchCodePoints[i] = forceLowerCaseSearch ? CharUtils::toLowerCase(inWord[i]) : inWord[i]; - } - DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithPtNodeArrayPos(getRootPosition()); - const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); - while (!readingHelper.isEnd()) { - const int matchedCodePointCount = readingHelper.getPrevTotalCodePointCount(); - if (readingHelper.getTotalCodePointCount() > length - || !readingHelper.isMatchedCodePoint(0 /* index */, - searchCodePoints[matchedCodePointCount])) { - // Current node has too many code points or its first code point is different from - // target code point. Skip this node and read the next sibling node. - readingHelper.readNextSiblingNode(); - continue; - } - // Check following merged node code points. - const int nodeCodePointCount = nodeReader->getCodePointCount(); - for (int j = 1; j < nodeCodePointCount; ++j) { - if (!readingHelper.isMatchedCodePoint( - j, searchCodePoints[matchedCodePointCount + j])) { - // Different code point is found. The given word is not included in the dictionary. - return NOT_A_DICT_POS; - } - } - // All characters are matched. - if (length == readingHelper.getTotalCodePointCount()) { - // Terminal position is found. - return nodeReader->getHeadPos(); - } - if (!nodeReader->hasChildren()) { - return NOT_A_DICT_POS; - } - // Advance to the children nodes. - readingHelper.readChildNode(); - } - // If we already traversed the tree further than the word is long, there means - // there was no match (or we would have found it). - return NOT_A_DICT_POS; -} - -int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability, - const int bigramProbability) const { - if (mHeaderPolicy.isDecayingDict()) { - return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability); - } else { - if (unigramProbability == NOT_A_PROBABILITY) { - return NOT_A_PROBABILITY; - } else if (bigramProbability == NOT_A_PROBABILITY) { - return ProbabilityUtils::backoff(unigramProbability); - } else { - return ProbabilityUtils::computeProbabilityForBigram(unigramProbability, - bigramProbability); - } - } -} - -int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { - if (ptNodePos == NOT_A_DICT_POS) { - return NOT_A_PROBABILITY; - } - DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); - if (nodeReader.isDeleted() || nodeReader.isBlacklisted() || nodeReader.isNotAWord()) { - return NOT_A_PROBABILITY; - } - return getProbability(nodeReader.getProbability(), NOT_A_PROBABILITY); -} - -int DynamicPatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { - if (ptNodePos == NOT_A_DICT_POS) { - return NOT_A_DICT_POS; - } - DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); - if (nodeReader.isDeleted()) { - return NOT_A_DICT_POS; - } - return nodeReader.getShortcutPos(); -} - -int DynamicPatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { - if (ptNodePos == NOT_A_DICT_POS) { - return NOT_A_DICT_POS; - } - DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); - if (nodeReader.isDeleted()) { - return NOT_A_DICT_POS; - } - return nodeReader.getBigramsPos(); -} - -bool DynamicPatriciaTriePolicy::addUnigramWord(const int *const word, const int length, - const int probability) { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); - return false; - } - if (mBufferWithExtendableBuffer.getTailPosition() - >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { - AKLOGE("The dictionary is too large to dynamically update."); - return false; - } - DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer, - getBigramsStructurePolicy(), getShortcutsStructurePolicy()); - readingHelper.initWithPtNodeArrayPos(getRootPosition()); - DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, - &mBigramListPolicy, &mShortcutListPolicy, mHeaderPolicy.isDecayingDict()); - bool addedNewUnigram = false; - if (writingHelper.addUnigramWord(&readingHelper, word, length, probability, - &addedNewUnigram)) { - if (addedNewUnigram) { - mUnigramCount++; - } - return true; - } else { - return false; - } -} - -bool DynamicPatriciaTriePolicy::addBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1, const int probability) { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); - return false; - } - if (mBufferWithExtendableBuffer.getTailPosition() - >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { - AKLOGE("The dictionary is too large to dynamically update."); - return false; - } - const int word0Pos = getTerminalNodePositionOfWord(word0, length0, - false /* forceLowerCaseSearch */); - if (word0Pos == NOT_A_DICT_POS) { - return false; - } - const int word1Pos = getTerminalNodePositionOfWord(word1, length1, - false /* forceLowerCaseSearch */); - if (word1Pos == NOT_A_DICT_POS) { - return false; - } - DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, - &mBigramListPolicy, &mShortcutListPolicy, mHeaderPolicy.isDecayingDict()); - bool addedNewBigram = false; - if (writingHelper.addBigramWords(word0Pos, word1Pos, probability, &addedNewBigram)) { - if (addedNewBigram) { - mBigramCount++; - } - return true; - } else { - return false; - } -} - -bool DynamicPatriciaTriePolicy::removeBigramWords(const int *const word0, const int length0, - const int *const word1, const int length1) { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: removeBigramWords() is called for non-updatable dictionary."); - return false; - } - if (mBufferWithExtendableBuffer.getTailPosition() - >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { - AKLOGE("The dictionary is too large to dynamically update."); - return false; - } - const int word0Pos = getTerminalNodePositionOfWord(word0, length0, - false /* forceLowerCaseSearch */); - if (word0Pos == NOT_A_DICT_POS) { - return false; - } - const int word1Pos = getTerminalNodePositionOfWord(word1, length1, - false /* forceLowerCaseSearch */); - if (word1Pos == NOT_A_DICT_POS) { - return false; - } - DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, - &mBigramListPolicy, &mShortcutListPolicy, mHeaderPolicy.isDecayingDict()); - if (writingHelper.removeBigramWords(word0Pos, word1Pos)) { - mBigramCount--; - return true; - } else { - return false; - } -} - -void DynamicPatriciaTriePolicy::flush(const char *const filePath) { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: flush() is called for non-updatable dictionary."); - return; - } - DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, - &mBigramListPolicy, &mShortcutListPolicy, false /* needsToDecay */); - writingHelper.writeToDictFile(filePath, &mHeaderPolicy, mUnigramCount, mBigramCount); -} - -void DynamicPatriciaTriePolicy::flushWithGC(const char *const filePath) { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); - return; - } - const bool needsToDecay = mHeaderPolicy.isDecayingDict() - && (mNeedsToDecayForTesting || ForgettingCurveUtils::needsToDecay( - false /* mindsBlockByDecay */, mUnigramCount, mBigramCount, &mHeaderPolicy)); - DynamicBigramListPolicy bigramListPolicyForGC(&mHeaderPolicy, &mBufferWithExtendableBuffer, - &mShortcutListPolicy, needsToDecay); - DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer, - &bigramListPolicyForGC, &mShortcutListPolicy, needsToDecay); - writingHelper.writeToDictFileWithGC(getRootPosition(), filePath, &mHeaderPolicy); - mNeedsToDecayForTesting = false; -} - -bool DynamicPatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { - if (!mBuffer->isUpdatable()) { - AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary."); - return false; - } - if (mBufferWithExtendableBuffer.isNearSizeLimit()) { - // Additional buffer size is near the limit. - return true; - } else if (mHeaderPolicy.getExtendedRegionSize() - + mBufferWithExtendableBuffer.getUsedAdditionalBufferSize() - > MAX_DICT_EXTENDED_REGION_SIZE) { - // Total extended region size exceeds the limit. - return true; - } else if (mBufferWithExtendableBuffer.getTailPosition() - >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS - && mBufferWithExtendableBuffer.getUsedAdditionalBufferSize() > 0) { - // Needs to reduce dictionary size. - return true; - } else if (mHeaderPolicy.isDecayingDict()) { - return mNeedsToDecayForTesting || ForgettingCurveUtils::needsToDecay( - mindsBlockByGC, mUnigramCount, mBigramCount, &mHeaderPolicy); - } - return false; -} - -void DynamicPatriciaTriePolicy::getProperty(const char *const query, char *const outResult, - const int maxResultLength) { - if (strncmp(query, UNIGRAM_COUNT_QUERY, maxResultLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mUnigramCount); - } else if (strncmp(query, BIGRAM_COUNT_QUERY, maxResultLength) == 0) { - snprintf(outResult, maxResultLength, "%d", mBigramCount); - } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, maxResultLength) == 0) { - snprintf(outResult, maxResultLength, "%d", - mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_UNIGRAM_COUNT : - static_cast<int>(DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE)); - } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, maxResultLength) == 0) { - snprintf(outResult, maxResultLength, "%d", - mHeaderPolicy.isDecayingDict() ? ForgettingCurveUtils::MAX_BIGRAM_COUNT : - static_cast<int>(DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE)); - } else if (strncmp(query, SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY, maxResultLength) == 0) { - mNeedsToDecayForTesting = true; - } -} - -} // 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 deleted file mode 100644 index be97ee1a5..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h +++ /dev/null @@ -1,121 +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. - */ - -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_POLICY_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_POLICY_H - -#include "defines.h" -#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" -#include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" -#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" - -namespace latinime { - -class DicNode; -class DicNodeVector; - -class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { - public: - DynamicPatriciaTriePolicy(const MmappedBuffer *const buffer) - : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()), - mBufferWithExtendableBuffer(mBuffer->getBuffer() + mHeaderPolicy.getSize(), - mBuffer->getBufferSize() - mHeaderPolicy.getSize()), - mShortcutListPolicy(&mBufferWithExtendableBuffer), - mBigramListPolicy(&mHeaderPolicy, &mBufferWithExtendableBuffer, &mShortcutListPolicy, - mHeaderPolicy.isDecayingDict()), - mUnigramCount(mHeaderPolicy.getUnigramCount()), - mBigramCount(mHeaderPolicy.getBigramCount()), mNeedsToDecayForTesting(false) {} - - ~DynamicPatriciaTriePolicy() { - delete mBuffer; - } - - AK_FORCE_INLINE int getRootPosition() const { - return 0; - } - - void createAndGetAllChildNodes(const DicNode *const dicNode, - DicNodeVector *const childDicNodes) const; - - int getCodePointsAndProbabilityAndReturnCodePointCount( - const int terminalPtNodePos, const int maxCodePointCount, int *const outCodePoints, - int *const outUnigramProbability) const; - - int getTerminalNodePositionOfWord(const int *const inWord, - const int length, const bool forceLowerCaseSearch) const; - - int getProbability(const int unigramProbability, const int bigramProbability) const; - - int getUnigramProbabilityOfPtNode(const int ptNodePos) const; - - int getShortcutPositionOfPtNode(const int ptNodePos) const; - - int getBigramsPositionOfPtNode(const int ptNodePos) const; - - const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { - return &mHeaderPolicy; - } - - const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const { - return &mBigramListPolicy; - } - - const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const { - return &mShortcutListPolicy; - } - - bool addUnigramWord(const int *const word, const int length, const int probability); - - bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability); - - bool removeBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1); - - void flush(const char *const filePath); - - void flushWithGC(const char *const filePath); - - bool needsToRunGC(const bool mindsBlockByGC) const; - - void getProperty(const char *const query, char *const outResult, - const int maxResultLength); - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTriePolicy); - - static const char *const UNIGRAM_COUNT_QUERY; - static const char *const BIGRAM_COUNT_QUERY; - static const char *const MAX_UNIGRAM_COUNT_QUERY; - static const char *const MAX_BIGRAM_COUNT_QUERY; - static const char *const SET_NEEDS_TO_DECAY_FOR_TESTING_QUERY; - static const int MAX_DICT_EXTENDED_REGION_SIZE; - static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS; - - const MmappedBuffer *const mBuffer; - const HeaderPolicy mHeaderPolicy; - BufferWithExtendableBuffer mBufferWithExtendableBuffer; - DynamicShortcutListPolicy mShortcutListPolicy; - DynamicBigramListPolicy mBigramListPolicy; - int mUnigramCount; - int mBigramCount; - int mNeedsToDecayForTesting; -}; -} // namespace latinime -#endif // LATINIME_DYNAMIC_PATRICIA_TRIE_POLICY_H 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 deleted file mode 100644 index 052558bfc..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp +++ /dev/null @@ -1,558 +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/dynamic_patricia_trie_writing_helper.h" - -#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 "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" -#include "utils/hash_map_compat.h" - -namespace latinime { - -const int DynamicPatriciaTrieWritingHelper::CHILDREN_POSITION_FIELD_SIZE = 3; -// TODO: Make MAX_DICTIONARY_SIZE 8MB. -const size_t DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024; - -bool DynamicPatriciaTrieWritingHelper::addUnigramWord( - DynamicPatriciaTrieReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, const int probability, - bool *const outAddedNewUnigram) { - int parentPos = NOT_A_DICT_POS; - while (!readingHelper->isEnd()) { - const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount(); - if (!readingHelper->isMatchedCodePoint(0 /* index */, - wordCodePoints[matchedCodePointCount])) { - // The first code point is different from target code point. Skip this node and read - // the next sibling node. - readingHelper->readNextSiblingNode(); - continue; - } - // Check following merged node code points. - const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper->getNodeReader(); - const int nodeCodePointCount = nodeReader->getCodePointCount(); - for (int j = 1; j < nodeCodePointCount; ++j) { - const int nextIndex = matchedCodePointCount + j; - if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(j, - wordCodePoints[matchedCodePointCount + j])) { - *outAddedNewUnigram = true; - return reallocatePtNodeAndAddNewPtNodes(nodeReader, - readingHelper->getMergedNodeCodePoints(), j, - getUpdatedProbability(NOT_A_PROBABILITY /* originalProbability */, - probability), - wordCodePoints + matchedCodePointCount, - codePointCount - matchedCodePointCount); - } - } - // All characters are matched. - if (codePointCount == readingHelper->getTotalCodePointCount()) { - return setPtNodeProbability(nodeReader, probability, - readingHelper->getMergedNodeCodePoints(), outAddedNewUnigram); - } - if (!nodeReader->hasChildren()) { - *outAddedNewUnigram = true; - return createChildrenPtNodeArrayAndAChildPtNode(nodeReader, - getUpdatedProbability(NOT_A_PROBABILITY /* originalProbability */, probability), - wordCodePoints + readingHelper->getTotalCodePointCount(), - codePointCount - readingHelper->getTotalCodePointCount()); - } - // Advance to the children nodes. - parentPos = nodeReader->getHeadPos(); - readingHelper->readChildNode(); - } - if (readingHelper->isError()) { - // The dictionary is invalid. - return false; - } - int pos = readingHelper->getPosOfLastForwardLinkField(); - *outAddedNewUnigram = true; - return createAndInsertNodeIntoPtNodeArray(parentPos, - wordCodePoints + readingHelper->getPrevTotalCodePointCount(), - codePointCount - readingHelper->getPrevTotalCodePointCount(), - getUpdatedProbability(NOT_A_PROBABILITY /* originalProbability */, probability), &pos); -} - -bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const int word1Pos, - const int probability, bool *const outAddedNewBigram) { - int mMergedNodeCodePoints[MAX_WORD_LENGTH]; - DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - nodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(word0Pos, MAX_WORD_LENGTH, - mMergedNodeCodePoints); - // Move node to add bigram entry. - const int newNodePos = mBuffer->getTailPosition(); - if (!markNodeAsMovedAndSetPosition(&nodeReader, newNodePos, newNodePos)) { - return false; - } - int writingPos = newNodePos; - // 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.fetchNodeInfoInBufferFromPtNodePos(newNodePos); - if (nodeReader.getBigramsPos() != NOT_A_DICT_POS) { - // Insert a new bigram entry into the existing bigram list. - int bigramListPos = nodeReader.getBigramsPos(); - return mBigramPolicy->addNewBigramEntryToBigramList(word1Pos, probability, &bigramListPos, - outAddedNewBigram); - } else { - // The PtNode doesn't have a bigram list. - *outAddedNewBigram = true; - // First, Write a bigram entry at the tail position of the PtNode. - if (!mBigramPolicy->writeNewBigramEntry(word1Pos, probability, &writingPos)) { - return false; - } - // Then, Mark as the PtNode having bigram list in the flags. - const PatriciaTrieReadingUtils::NodeFlags updatedFlags = - PatriciaTrieReadingUtils::createAndGetFlags(nodeReader.isBlacklisted(), - nodeReader.isNotAWord(), nodeReader.getProbability() != NOT_A_PROBABILITY, - nodeReader.getShortcutPos() != NOT_A_DICT_POS, true /* hasBigrams */, - nodeReader.getCodePointCount() > 1, CHILDREN_POSITION_FIELD_SIZE); - writingPos = newNodePos; - // Write updated flags into the moved PtNode's flags field. - return DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags, - &writingPos); - } -} - -// Remove a bigram relation from word0Pos to word1Pos. -bool DynamicPatriciaTrieWritingHelper::removeBigramWords(const int word0Pos, const int word1Pos) { - DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - 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, const int unigramCount, const int bigramCount) { - BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); - const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + - mBuffer->getUsedAdditionalBufferSize(); - if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */, - false /* updatesLastDecayedTime */, unigramCount, bigramCount, extendedRegionSize)) { - return; - } - DictFileWritingUtils::flushAllHeaderAndBodyToFile(fileName, &headerBuffer, mBuffer); -} - -void DynamicPatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeArrayPos, - const char *const fileName, const HeaderPolicy *const headerPolicy) { - BufferWithExtendableBuffer newDictBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */, - MAX_DICTIONARY_SIZE); - int unigramCount = 0; - int bigramCount = 0; - if (mNeedsToDecay) { - ForgettingCurveUtils::sTimeKeeper.setCurrentTime(); - } - if (!runGC(rootPtNodeArrayPos, headerPolicy, &newDictBuffer, &unigramCount, &bigramCount)) { - return; - } - BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); - if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, true /* updatesLastUpdatedTime */, - mNeedsToDecay, unigramCount, bigramCount, 0 /* extendedRegionSize */)) { - return; - } - DictFileWritingUtils::flushAllHeaderAndBodyToFile(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) { - int pos = originalNode->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, true /* isMoved */, - false /* isDeleted */); - int writingPos = originalNode->getHeadPos(); - // Update flags. - if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags, - &writingPos)) { - return false; - } - // Update moved position, which is stored in the parent offset field. - if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( - mBuffer, movedPos, originalNode->getHeadPos(), &writingPos)) { - return false; - } - // Update bigram linked node position, which is stored in the children position field. - int childrenPosFieldPos = originalNode->getChildrenPosFieldPos(); - if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( - mBuffer, bigramLinkedNodePos, &childrenPosFieldPos)) { - return false; - } - if (originalNode->hasChildren()) { - // Update children's parent position. - DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy); - const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader(); - readingHelper.initWithPtNodeArrayPos(originalNode->getChildrenPos()); - while (!readingHelper.isEnd()) { - int parentOffsetFieldPos = nodeReader->getHeadPos() - + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE; - if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( - mBuffer, bigramLinkedNodePos, 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; - } - readingHelper.readNextSiblingNode(); - } - } - return true; -} - -// Write new PtNode at writingPos. -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, - int *const writingPos) { - 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(bufferToWrite, - 0 /* nodeFlags */, writingPos)) { - return false; - } - // Calculate a parent offset and write the offset. - if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(bufferToWrite, - parentPos, nodePos, writingPos)) { - return false; - } - // Write code points - 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(bufferToWrite, - probability, writingPos)) { - return false; - } - } - // Write children position - 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(bufferToWrite, &fromPos, - writingPos)) { - return false; - } - } - // Copy bigram list when the originalBigramListPos is valid dictionary position. - int bigramCount = 0; - if (originalBigramListPos != NOT_A_DICT_POS) { - int fromPos = originalBigramListPos; - if (!mBigramPolicy->copyAllBigrams(bufferToWrite, &fromPos, writingPos, &bigramCount)) { - return false; - } - } - // Create node flags and write them. - PatriciaTrieReadingUtils::NodeFlags nodeFlags = - PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord, - probability != NOT_A_PROBABILITY /* isTerminal */, - originalShortcutListPos != NOT_A_DICT_POS /* hasShortcutTargets */, - bigramCount > 0 /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */, - CHILDREN_POSITION_FIELD_SIZE); - int flagsFieldPos = nodePos; - if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(bufferToWrite, nodeFlags, - &flagsFieldPos)) { - return false; - } - return true; -} - -bool DynamicPatriciaTrieWritingHelper::writePtNodeToBuffer( - BufferWithExtendableBuffer *const bufferToWrite, const int parentPos, - const int *const codePoints, const int codePointCount, const int probability, - int *const writingPos) { - 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(bufferToWrite, originalNode->isBlacklisted(), - originalNode->isNotAWord(), parentPos, codePoints, codePointCount, probability, - originalNode->getChildrenPos(), originalNode->getBigramsPos(), - originalNode->getShortcutPos(), writingPos); -} - -bool DynamicPatriciaTrieWritingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos, - const int *const nodeCodePoints, const int nodeCodePointCount, const int probability, - int *const forwardLinkFieldPos) { - const int newPtNodeArrayPos = mBuffer->getTailPosition(); - if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, - newPtNodeArrayPos, forwardLinkFieldPos)) { - return false; - } - return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount, - probability); -} - -bool DynamicPatriciaTrieWritingHelper::setPtNodeProbability( - const DynamicPatriciaTrieNodeReader *const originalPtNode, const int probability, - const int *const codePoints, bool *const outAddedNewUnigram) { - if (originalPtNode->isTerminal()) { - // Overwrites the probability. - *outAddedNewUnigram = false; - const int probabilityToWrite = getUpdatedProbability(originalPtNode->getProbability(), - probability); - int probabilityFieldPos = originalPtNode->getProbabilityFieldPos(); - if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer, - probabilityToWrite, &probabilityFieldPos)) { - return false; - } - } else { - // Make the node terminal and write the probability. - *outAddedNewUnigram = true; - int movedPos = mBuffer->getTailPosition(); - if (!markNodeAsMovedAndSetPosition(originalPtNode, movedPos, movedPos)) { - return false; - } - if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, originalPtNode, - originalPtNode->getParentPos(), codePoints, originalPtNode->getCodePointCount(), - getUpdatedProbability(NOT_A_PROBABILITY /* originalProbability */, probability), - &movedPos)) { - return false; - } - } - return true; -} - -bool DynamicPatriciaTrieWritingHelper::createChildrenPtNodeArrayAndAChildPtNode( - const DynamicPatriciaTrieNodeReader *const parentNode, const int probability, - const int *const codePoints, const int codePointCount) { - const int newPtNodeArrayPos = mBuffer->getTailPosition(); - int childrenPosFieldPos = parentNode->getChildrenPosFieldPos(); - if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, - newPtNodeArrayPos, &childrenPosFieldPos)) { - return false; - } - return createNewPtNodeArrayWithAChildPtNode(parentNode->getHeadPos(), codePoints, - codePointCount, probability); -} - -bool DynamicPatriciaTrieWritingHelper::createNewPtNodeArrayWithAChildPtNode( - const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount, - const int probability) { - int writingPos = mBuffer->getTailPosition(); - if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, - 1 /* arraySize */, &writingPos)) { - return false; - } - if (!writePtNodeToBuffer(mBuffer, parentPtNodePos, nodeCodePoints, nodeCodePointCount, - probability, &writingPos)) { - return false; - } - if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, - NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { - return false; - } - return true; -} - -// Returns whether the dictionary updating was succeeded or not. -bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes( - const DynamicPatriciaTrieNodeReader *const reallocatingPtNode, - const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount, - const int probabilityOfNewPtNode, const int *const newNodeCodePoints, - const int newNodeCodePointCount) { - // When addsExtraChild is true, split the reallocating PtNode and add new child. - // Reallocating PtNode: abcde, newNode: abcxy. - // abc (1st, not terminal) __ de (2nd) - // \_ xy (extra child, terminal) - // Otherwise, this method makes 1st part terminal and write probabilityOfNewPtNode. - // Reallocating PtNode: abcde, newNode: abc. - // abc (1st, terminal) __ de (2nd) - const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount; - const int firstPartOfReallocatedPtNodePos = mBuffer->getTailPosition(); - int writingPos = firstPartOfReallocatedPtNodePos; - // 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(mBuffer, reallocatingPtNode->getParentPos(), - reallocatingPtNodeCodePoints, overlappingCodePointCount, newProbability, - &writingPos)) { - return false; - } - const int actualChildrenPos = writingPos; - // Create new children PtNode array. - const size_t newPtNodeCount = addsExtraChild ? 2 : 1; - if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, - newPtNodeCount, &writingPos)) { - return false; - } - // Write the 2nd part of the reallocating node. - const int secondPartOfReallocatedPtNodePos = writingPos; - if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, reallocatingPtNode, - firstPartOfReallocatedPtNodePos, - reallocatingPtNodeCodePoints + overlappingCodePointCount, - reallocatingPtNode->getCodePointCount() - overlappingCodePointCount, - reallocatingPtNode->getProbability(), &writingPos)) { - return false; - } - if (addsExtraChild) { - if (!writePtNodeToBuffer(mBuffer, firstPartOfReallocatedPtNodePos, - newNodeCodePoints + overlappingCodePointCount, - newNodeCodePointCount - overlappingCodePointCount, probabilityOfNewPtNode, - &writingPos)) { - return false; - } - } - if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, - NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { - return false; - } - // Update original reallocatingPtNode as moved. - if (!markNodeAsMovedAndSetPosition(reallocatingPtNode, firstPartOfReallocatedPtNodePos, - secondPartOfReallocatedPtNodePos)) { - return false; - } - // Load node info. Information of the 1st part will be fetched. - DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy); - nodeReader.fetchNodeInfoInBufferFromPtNodePos(firstPartOfReallocatedPtNodePos); - // Update children position. - int childrenPosFieldPos = nodeReader.getChildrenPosFieldPos(); - if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer, - actualChildrenPos, &childrenPosFieldPos)) { - return false; - } - return true; -} - -bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, - const HeaderPolicy *const headerPolicy, BufferWithExtendableBuffer *const bufferToWrite, - int *const outUnigramCount, int *const outBigramCount) { - DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy); - readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); - DynamicPatriciaTrieGcEventListeners - ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted - traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( - headerPolicy, this, mBuffer, mNeedsToDecay); - if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( - &traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) { - return false; - } - if (mNeedsToDecay && traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted - .getValidUnigramCount() > ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC) { - // TODO: Remove more unigrams. - } - - readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); - DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateBigramProbability - traversePolicyToUpdateBigramProbability(mBigramPolicy); - if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( - &traversePolicyToUpdateBigramProbability)) { - return false; - } - if (mNeedsToDecay && traversePolicyToUpdateBigramProbability.getValidBigramEntryCount() - > ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC) { - // TODO: Remove more bigrams. - } - - // 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(headerPolicy, bufferToWrite, &newDictShortcutPolicy, - mNeedsToDecay); - // 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; - } - *outUnigramCount = traversePolicyToUpdateAllPositionFields.getUnigramCount(); - *outBigramCount = traversePolicyToUpdateAllPositionFields.getBigramCount(); - return true; -} - -int DynamicPatriciaTrieWritingHelper::getUpdatedProbability(const int originalProbability, - const int newProbability) { - if (mNeedsToDecay) { - return ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability, - newProbability); - } else { - return newProbability; - } -} - -} // 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 deleted file mode 100644 index ca8664729..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h +++ /dev/null @@ -1,138 +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. - */ - -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H - -#include <stdint.h> - -#include "defines.h" -#include "utils/hash_map_compat.h" - -namespace latinime { - -class BufferWithExtendableBuffer; -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); - }; - - static const size_t MAX_DICTIONARY_SIZE; - - DynamicPatriciaTrieWritingHelper(BufferWithExtendableBuffer *const buffer, - DynamicBigramListPolicy *const bigramPolicy, - DynamicShortcutListPolicy *const shortcutPolicy, const bool needsToDecay) - : mBuffer(buffer), mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy), - mNeedsToDecay(needsToDecay) {} - - ~DynamicPatriciaTrieWritingHelper() {} - - // Add a word to the dictionary. If the word already exists, update the probability. - bool addUnigramWord(DynamicPatriciaTrieReadingHelper *const readingHelper, - const int *const wordCodePoints, const int codePointCount, const int probability, - bool *const outAddedNewUnigram); - - // Add a bigram relation from word0Pos to word1Pos. - bool addBigramWords(const int word0Pos, const int word1Pos, const int probability, - bool *const outAddedNewBigram); - - // 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, - const int unigramCount, const int bigramCount); - - 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; - - BufferWithExtendableBuffer *const mBuffer; - DynamicBigramListPolicy *const mBigramPolicy; - DynamicShortcutListPolicy *const mShortcutPolicy; - const bool mNeedsToDecay; - - bool markNodeAsMovedAndSetPosition(const DynamicPatriciaTrieNodeReader *const nodeToUpdate, - const int movedPos, const int bigramLinkedNodePos); - - 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(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); - - bool setPtNodeProbability(const DynamicPatriciaTrieNodeReader *const originalNode, - const int probability, const int *const codePoints, bool *const outAddedNewUnigram); - - bool createChildrenPtNodeArrayAndAChildPtNode( - const DynamicPatriciaTrieNodeReader *const parentNode, const int probability, - const int *const codePoints, const int codePointCount); - - bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints, - const int nodeCodePointCount, const int probability); - - bool reallocatePtNodeAndAddNewPtNodes( - const DynamicPatriciaTrieNodeReader *const reallocatingPtNode, - const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount, - const int probabilityOfNewPtNode, const int *const newNodeCodePoints, - const int newNodeCodePointCount); - - bool runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, - BufferWithExtendableBuffer *const bufferToWrite, int *const outUnigramCount, - int *const outBigramCount); - - int getUpdatedProbability(const int originalProbability, const int newProbability); -}; -} // namespace latinime -#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_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 eb072fbaf..be7a3c228 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -20,6 +20,8 @@ namespace latinime { // Note that these are corresponding definitions in Java side in FormatSpec.FileHeader. const char *const HeaderPolicy::MULTIPLE_WORDS_DEMOTION_RATE_KEY = "MULTIPLE_WORDS_DEMOTION_RATE"; +const char *const HeaderPolicy::REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY = + "REQUIRES_GERMAN_UMLAUT_PROCESSING"; // TODO: Change attribute string to "IS_DECAYING_DICT". const char *const HeaderPolicy::IS_DECAYING_DICT_KEY = "USES_FORGETTING_CURVE"; const char *const HeaderPolicy::LAST_UPDATED_TIME_KEY = "date"; @@ -27,6 +29,9 @@ const char *const HeaderPolicy::LAST_DECAYED_TIME_KEY = "LAST_DECAYED_TIME"; const char *const HeaderPolicy::UNIGRAM_COUNT_KEY = "UNIGRAM_COUNT"; const char *const HeaderPolicy::BIGRAM_COUNT_KEY = "BIGRAM_COUNT"; const char *const HeaderPolicy::EXTENDED_REGION_SIZE_KEY = "EXTENDED_REGION_SIZE"; +// Historical info is information that is needed to support decaying such as timestamp, level and +// count. +const char *const HeaderPolicy::HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO"; const int HeaderPolicy::DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE = 100; const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f; @@ -63,6 +68,11 @@ float HeaderPolicy::readMultipleWordCostMultiplier() const { return MULTIPLE_WORD_COST_MULTIPLIER_SCALE / static_cast<float>(demotionRate); } +bool HeaderPolicy::readRequiresGermanUmlautProcessing() const { + return HeaderReadWriteUtils::readBoolAttributeValue(&mAttributeMap, + REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY, false); +} + bool HeaderPolicy::writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite, const bool updatesLastUpdatedTime, const bool updatesLastDecayedTime, const int unigramCount, const int bigramCount, const int extendedRegionSize) const { @@ -89,12 +99,12 @@ bool HeaderPolicy::writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferT if (updatesLastUpdatedTime) { // Set current time as a last updated time. HeaderReadWriteUtils::setIntAttribute(&attributeMapTowrite, LAST_UPDATED_TIME_KEY, - time(0)); + TimeKeeper::peekCurrentTime()); } if (updatesLastDecayedTime) { // Set current time as a last updated time. HeaderReadWriteUtils::setIntAttribute(&attributeMapTowrite, LAST_DECAYED_TIME_KEY, - time(0)); + TimeKeeper::peekCurrentTime()); } if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &attributeMapTowrite, &writingPos)) { 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 a9c7805a8..1208d2c2a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -17,37 +17,40 @@ #ifndef LATINIME_HEADER_POLICY_H #define LATINIME_HEADER_POLICY_H -#include <ctime> #include <stdint.h> #include "defines.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/policyimpl/dictionary/header/header_read_write_utils.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" +#include "utils/time_keeper.h" namespace latinime { class HeaderPolicy : public DictionaryHeaderStructurePolicy { public: // Reads information from existing dictionary buffer. - HeaderPolicy(const uint8_t *const dictBuf, const int dictSize) - : mDictFormatVersion(FormatUtils::detectFormatVersion(dictBuf, dictSize)), + HeaderPolicy(const uint8_t *const dictBuf, const FormatUtils::FORMAT_VERSION formatVersion) + : mDictFormatVersion(formatVersion), mDictionaryFlags(HeaderReadWriteUtils::getFlags(dictBuf)), mSize(HeaderReadWriteUtils::getHeaderSize(dictBuf)), mAttributeMap(createAttributeMapAndReadAllAttributes(dictBuf)), mMultiWordCostMultiplier(readMultipleWordCostMultiplier()), + mRequiresGermanUmlautProcessing(readRequiresGermanUmlautProcessing()), mIsDecayingDict(HeaderReadWriteUtils::readBoolAttributeValue(&mAttributeMap, IS_DECAYING_DICT_KEY, false /* defaultValue */)), mLastUpdatedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, - LAST_UPDATED_TIME_KEY, time(0) /* defaultValue */)), + LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), mLastDecayedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, - LAST_DECAYED_TIME_KEY, time(0) /* defaultValue */)), + LAST_DECAYED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), mUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, UNIGRAM_COUNT_KEY, 0 /* defaultValue */)), mBigramCount(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, BIGRAM_COUNT_KEY, 0 /* defaultValue */)), mExtendedRegionSize(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, - EXTENDED_REGION_SIZE_KEY, 0 /* defaultValue */)) {} + EXTENDED_REGION_SIZE_KEY, 0 /* defaultValue */)), + mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue( + &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)) {} // Constructs header information using an attribute map. HeaderPolicy(const FormatUtils::FORMAT_VERSION dictFormatVersion, @@ -56,30 +59,55 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { mDictionaryFlags(HeaderReadWriteUtils::createAndGetDictionaryFlagsUsingAttributeMap( attributeMap)), mSize(0), mAttributeMap(*attributeMap), mMultiWordCostMultiplier(readMultipleWordCostMultiplier()), + mRequiresGermanUmlautProcessing(readRequiresGermanUmlautProcessing()), mIsDecayingDict(HeaderReadWriteUtils::readBoolAttributeValue(&mAttributeMap, IS_DECAYING_DICT_KEY, false /* defaultValue */)), mLastUpdatedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, - LAST_UPDATED_TIME_KEY, time(0) /* defaultValue */)), + LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), mLastDecayedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap, - LAST_UPDATED_TIME_KEY, time(0) /* defaultValue */)), - mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0) {} + LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)), + mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0), + mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue( + &mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)) {} + + // Temporary dummy header. + HeaderPolicy() + : mDictFormatVersion(FormatUtils::UNKNOWN_VERSION), mDictionaryFlags(0), mSize(0), + mAttributeMap(), mMultiWordCostMultiplier(0.0f), + mRequiresGermanUmlautProcessing(false), mIsDecayingDict(false), + mLastUpdatedTime(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0), + mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false) {} ~HeaderPolicy() {} - AK_FORCE_INLINE int getSize() const { - return mSize; - } - - AK_FORCE_INLINE bool supportsDynamicUpdate() const { - return HeaderReadWriteUtils::supportsDynamicUpdate(mDictionaryFlags); + virtual int getFormatVersionNumber() const { + // Conceptually this converts the symbolic value we use in the code into the + // hardcoded of the bytes in the file. But we want the constants to be the + // same so we use them for both here. + switch (mDictFormatVersion) { + case FormatUtils::VERSION_2: + return FormatUtils::VERSION_2; + case FormatUtils::VERSION_4: + return FormatUtils::VERSION_4; + default: + return FormatUtils::UNKNOWN_VERSION; + } } - AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const { - return HeaderReadWriteUtils::requiresGermanUmlautProcessing(mDictionaryFlags); + AK_FORCE_INLINE bool isValid() const { + // Decaying dictionary must have historical information. + if (!mIsDecayingDict) { + return true; + } + if (mHasHistoricalInfoOfWords) { + return true; + } else { + return false; + } } - AK_FORCE_INLINE bool requiresFrenchLigatureProcessing() const { - return HeaderReadWriteUtils::requiresFrenchLigatureProcessing(mDictionaryFlags); + AK_FORCE_INLINE int getSize() const { + return mSize; } AK_FORCE_INLINE float getMultiWordCostMultiplier() const { @@ -90,6 +118,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return mIsDecayingDict; } + AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const { + return mRequiresGermanUmlautProcessing; + } + AK_FORCE_INLINE int getLastUpdatedTime() const { return mLastUpdatedTime; } @@ -110,6 +142,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return mExtendedRegionSize; } + AK_FORCE_INLINE bool hasHistoricalInfoOfWords() const { + return mHasHistoricalInfoOfWords; + } + void readHeaderValueOrQuestionMark(const char *const key, int *outValue, int outValueSize) const; @@ -118,15 +154,17 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { const int unigramCount, const int bigramCount, const int extendedRegionSize) const; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPolicy); + DISALLOW_COPY_AND_ASSIGN(HeaderPolicy); static const char *const MULTIPLE_WORDS_DEMOTION_RATE_KEY; + static const char *const REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY; static const char *const IS_DECAYING_DICT_KEY; static const char *const LAST_UPDATED_TIME_KEY; static const char *const LAST_DECAYED_TIME_KEY; static const char *const UNIGRAM_COUNT_KEY; static const char *const BIGRAM_COUNT_KEY; static const char *const EXTENDED_REGION_SIZE_KEY; + static const char *const HAS_HISTORICAL_INFO_KEY; static const int DEFAULT_MULTIPLE_WORDS_DEMOTION_RATE; static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE; @@ -135,14 +173,17 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { const int mSize; HeaderReadWriteUtils::AttributeMap mAttributeMap; const float mMultiWordCostMultiplier; + const bool mRequiresGermanUmlautProcessing; const bool mIsDecayingDict; const int mLastUpdatedTime; const int mLastDecayedTime; const int mUnigramCount; const int mBigramCount; const int mExtendedRegionSize; + const bool mHasHistoricalInfoOfWords; float readMultipleWordCostMultiplier() const; + bool readRequiresGermanUmlautProcessing() const; static HeaderReadWriteUtils::AttributeMap createAttributeMapAndReadAllAttributes( const uint8_t *const dictBuf); diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp index 5ded8f6a1..6b4598642 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp @@ -35,22 +35,6 @@ 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; - -// Note that these are corresponding definitions in Java side in FormatSpec.FileHeader. -const char *const HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_KEY = "SUPPORTS_DYNAMIC_UPDATE"; -const char *const HeaderReadWriteUtils::REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY = - "REQUIRES_GERMAN_UMLAUT_PROCESSING"; -const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY = - "REQUIRES_FRENCH_LIGATURE_PROCESSING"; /* static */ int HeaderReadWriteUtils::getHeaderSize(const uint8_t *const dictBuf) { // See the format of the header in the comment in @@ -68,17 +52,7 @@ const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY /* static */ HeaderReadWriteUtils::DictionaryFlags HeaderReadWriteUtils::createAndGetDictionaryFlagsUsingAttributeMap( const HeaderReadWriteUtils::AttributeMap *const attributeMap) { - const bool requiresGermanUmlautProcessing = readBoolAttributeValue(attributeMap, - REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY, false /* defaultValue */); - const bool requiresFrenchLigatureProcessing = readBoolAttributeValue(attributeMap, - REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY, false /* defaultValue */); - const bool supportsDynamicUpdate = readBoolAttributeValue(attributeMap, - SUPPORTS_DYNAMIC_UPDATE_KEY, false /* defaultValue */); - DictionaryFlags dictflags = NO_FLAGS; - dictflags |= requiresGermanUmlautProcessing ? GERMAN_UMLAUT_PROCESSING_FLAG : 0; - dictflags |= requiresFrenchLigatureProcessing ? FRENCH_LIGATURE_PROCESSING_FLAG : 0; - dictflags |= supportsDynamicUpdate ? SUPPORTS_DYNAMIC_UPDATE_FLAG : 0; - return dictflags; + return NO_FLAGS; } /* static */ void HeaderReadWriteUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf, @@ -115,8 +89,8 @@ const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY case FormatUtils::VERSION_2: // Version 2 dictionary writing is not supported. return false; - case FormatUtils::VERSION_3: - return buffer->writeUintAndAdvancePosition(3 /* data */, + case FormatUtils::VERSION_4: + return buffer->writeUintAndAdvancePosition(FormatUtils::VERSION_4 /* data */, HEADER_DICTIONARY_VERSION_SIZE, writingPos); default: return false; diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h index 225968323..fc24bbdd5 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h @@ -37,18 +37,6 @@ class HeaderReadWriteUtils { static DictionaryFlags getFlags(const uint8_t *const dictBuf); - static AK_FORCE_INLINE bool supportsDynamicUpdate(const DictionaryFlags flags) { - return (flags & SUPPORTS_DYNAMIC_UPDATE_FLAG) != 0; - } - - static AK_FORCE_INLINE bool requiresGermanUmlautProcessing(const DictionaryFlags flags) { - return (flags & GERMAN_UMLAUT_PROCESSING_FLAG) != 0; - } - - static AK_FORCE_INLINE bool requiresFrenchLigatureProcessing(const DictionaryFlags flags) { - return (flags & FRENCH_LIGATURE_PROCESSING_FLAG) != 0; - } - static AK_FORCE_INLINE int getHeaderOptionsPosition() { return HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE + HEADER_SIZE_FIELD_SIZE; @@ -101,17 +89,8 @@ class HeaderReadWriteUtils { static const int HEADER_FLAG_SIZE; static const int HEADER_SIZE_FIELD_SIZE; + // Value for the "flags" field. It's unused at the moment. static const DictionaryFlags NO_FLAGS; - // Flags for special processing - // Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAGS) or - // something very bad (like, the apocalypse) will happen. Please update both at the same time. - static const DictionaryFlags GERMAN_UMLAUT_PROCESSING_FLAG; - static const DictionaryFlags SUPPORTS_DYNAMIC_UPDATE_FLAG; - static const DictionaryFlags FRENCH_LIGATURE_PROCESSING_FLAG; - - static const char *const SUPPORTS_DYNAMIC_UPDATE_KEY; - static const char *const REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY; - static const char *const REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY; static void setIntAttributeInner(AttributeMap *const headerAttributes, const AttributeMap::key_type *const key, const int value); 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 deleted file mode 100644 index bd3211f6a..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h +++ /dev/null @@ -1,123 +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. - */ - -#ifndef LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H -#define LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H - -#include <stdint.h> - -#include "defines.h" -#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" -#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" - -namespace latinime { - -/* - * This is a dynamic version of ShortcutListPolicy and supports an additional buffer. - */ -class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy { - public: - explicit DynamicShortcutListPolicy(const BufferWithExtendableBuffer *const buffer) - : mBuffer(buffer) {} - - ~DynamicShortcutListPolicy() {} - - int getStartPos(const int pos) const { - if (pos == NOT_A_DICT_POS) { - return NOT_A_DICT_POS; - } - return pos + ShortcutListReadingUtils::getShortcutListSizeFieldSize(); - } - - void getNextShortcut(const int maxCodePointCount, int *const outCodePoint, - int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext, - int *const pos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - if (usesAdditionalBuffer) { - *pos -= mBuffer->getOriginalBufferSize(); - } - const ShortcutListReadingUtils::ShortcutFlags flags = - ShortcutListReadingUtils::getFlagsAndForwardPointer(buffer, pos); - if (outHasNext) { - *outHasNext = ShortcutListReadingUtils::hasNext(flags); - } - if (outIsWhitelist) { - *outIsWhitelist = ShortcutListReadingUtils::isWhitelist(flags); - } - if (outCodePoint) { - *outCodePointCount = ShortcutListReadingUtils::readShortcutTarget( - buffer, maxCodePointCount, outCodePoint, pos); - } - if (usesAdditionalBuffer) { - *pos += mBuffer->getOriginalBufferSize(); - } - } - - void skipAllShortcuts(int *const pos) const { - const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos); - const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer); - if (usesAdditionalBuffer) { - *pos -= mBuffer->getOriginalBufferSize(); - } - const int shortcutListSize = ShortcutListReadingUtils - ::getShortcutListSizeAndForwardPointer(buffer, pos); - *pos += shortcutListSize; - if (usesAdditionalBuffer) { - *pos += mBuffer->getOriginalBufferSize(); - } - } - - // 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); - if (usesAdditionalBuffer) { - *fromPos -= mBuffer->getOriginalBufferSize(); - } - const int shortcutListSize = ShortcutListReadingUtils - ::getShortcutListSizeAndForwardPointer(mBuffer->getBuffer(usesAdditionalBuffer), - fromPos); - // Copy shortcut list size. - if (!bufferToWrite->writeUintAndAdvancePosition( - shortcutListSize + ShortcutListReadingUtils::getShortcutListSizeFieldSize(), - ShortcutListReadingUtils::getShortcutListSizeFieldSize(), toPos)) { - return false; - } - // Copy shortcut list. - for (int i = 0; i < shortcutListSize; ++i) { - const uint8_t data = ByteArrayUtils::readUint8AndAdvancePosition( - mBuffer->getBuffer(usesAdditionalBuffer), fromPos); - if (!bufferToWrite->writeUintAndAdvancePosition(data, 1 /* size */, toPos)) { - return false; - } - } - if (usesAdditionalBuffer) { - *fromPos += mBuffer->getOriginalBufferSize(); - } - return true; - } - - private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicShortcutListPolicy); - - const BufferWithExtendableBuffer *const mBuffer; -}; -} // namespace latinime -#endif // LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h new file mode 100644 index 000000000..ae863af57 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_VER4_SHORTCUT_LIST_POLICY_H +#define LATINIME_VER4_SHORTCUT_LIST_POLICY_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h" +#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" + +namespace latinime { + +class Ver4ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { + public: + Ver4ShortcutListPolicy(ShortcutDictContent *const shortcutDictContent, + const TerminalPositionLookupTable *const terminalPositionLookupTable) + : mShortcutDictContent(shortcutDictContent), + mTerminalPositionLookupTable(terminalPositionLookupTable) {} + + ~Ver4ShortcutListPolicy() {} + + int getStartPos(const int pos) const { + // The first shortcut entry is located at the head position of the shortcut list. + return pos; + } + + void getNextShortcut(const int maxCodePointCount, int *const outCodePoint, + int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext, + int *const pos) const { + int probability = 0; + mShortcutDictContent->getShortcutEntryAndAdvancePosition(maxCodePointCount, + outCodePoint, outCodePointCount, &probability, outHasNext, pos); + if (outIsWhitelist) { + *outIsWhitelist = ShortcutListReadingUtils::isWhitelist(probability); + } + } + + void skipAllShortcuts(int *const pos) const { + // Do nothing because we don't need to skip shortcut lists in ver4 dictionaries. + } + + bool addNewShortcut(const int terminalId, const int *const codePoints, const int codePointCount, + const int probability) { + const int shortcutListPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + if (shortcutListPos == NOT_A_DICT_POS) { + // Create shortcut list. + if (!mShortcutDictContent->createNewShortcutList(terminalId)) { + AKLOGE("Cannot create new shortcut list. terminal id: %d", terminalId); + return false; + } + const int writingPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + return mShortcutDictContent->writeShortcutEntry(codePoints, codePointCount, probability, + false /* hasNext */, writingPos); + } + const int entryPos = mShortcutDictContent->findShortcutEntryAndGetPos(shortcutListPos, + codePoints, codePointCount); + if (entryPos == NOT_A_DICT_POS) { + // Add new entry to the shortcut list. + // Create new shortcut list. + if (!mShortcutDictContent->createNewShortcutList(terminalId)) { + AKLOGE("Cannot create new shortcut list. terminal id: %d", terminalId); + return false; + } + int writingPos = mShortcutDictContent->getShortcutListHeadPos(terminalId); + if (!mShortcutDictContent->writeShortcutEntryAndAdvancePosition(codePoints, + codePointCount, probability, true /* hasNext */, &writingPos)) { + AKLOGE("Cannot write shortcut entry. terminal id: %d, pos: %d", terminalId, + writingPos); + return false; + } + return mShortcutDictContent->copyShortcutList(shortcutListPos, writingPos); + } + // Overwrite existing entry. + bool hasNext = false; + mShortcutDictContent->getShortcutEntry(MAX_WORD_LENGTH, 0 /* outCodePoint */, + 0 /* outCodePointCount */ , 0 /* probability */, &hasNext, entryPos); + if (!mShortcutDictContent->writeShortcutEntry(codePoints, + codePointCount, probability, hasNext, entryPos)) { + AKLOGE("Cannot overwrite shortcut entry. terminal id: %d, pos: %d", terminalId, + entryPos); + return false; + } + return true; + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4ShortcutListPolicy); + + ShortcutDictContent *const mShortcutDictContent; + const TerminalPositionLookupTable *const mTerminalPositionLookupTable; +}; +} // namespace latinime +#endif // LATINIME_VER4_SHORTCUT_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp new file mode 100644 index 000000000..c81c61d23 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp @@ -0,0 +1,132 @@ +/* + * 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/structure/dictionary_structure_with_buffer_policy_factory.h" + +#include <climits> +#include <stdint.h> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +namespace latinime { + +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory + ::newDictionaryStructureWithBufferPolicy(const char *const path, + const int bufOffset, const int size, const bool isUpdatable) { + if (FileUtils::existsDir(path)) { + // Given path represents a directory. + return newPolicyforDirectoryDict(path, isUpdatable); + } else { + if (isUpdatable) { + AKLOGE("One file dictionaries don't support updating. path: %s", path); + ASSERT(false); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + } + return newPolicyforFileDict(path, bufOffset, size); + } +} + +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyforDirectoryDict( + const char *const path, const bool isUpdatable) { + const int headerFilePathBufSize = PATH_MAX + 1 /* terminator */; + char headerFilePath[headerFilePathBufSize]; + getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath); + // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of + // MmappedBufferPtr if the instance has the responsibility. + MmappedBuffer::MmappedBufferPtr mmappedBuffer = MmappedBuffer::openBuffer(headerFilePath, + isUpdatable); + if (!mmappedBuffer.get()) { + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + } + switch (FormatUtils::detectFormatVersion(mmappedBuffer.get()->getBuffer(), + mmappedBuffer.get()->getBufferSize())) { + case FormatUtils::VERSION_2: + AKLOGE("Given path is a directory but the format is version 2. path: %s", path); + break; + case FormatUtils::VERSION_4: { + const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */; + char dictPath[dictDirPathBufSize]; + if (!FileUtils::getFilePathWithoutSuffix(headerFilePath, + Ver4DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) { + AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path); + ASSERT(false); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + } + const Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = + Ver4DictBuffers::openVer4DictBuffers(dictPath, mmappedBuffer); + if (!dictBuffers.get()->isValid()) { + AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", + path); + ASSERT(false); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + } + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new Ver4PatriciaTriePolicy(dictBuffers)); + } + default: + AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path); + break; + } + ASSERT(false); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); +} + +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyforFileDict( + const char *const path, const int bufOffset, const int size) { + // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of + // MmappedBufferPtr if the instance has the responsibility. + MmappedBuffer::MmappedBufferPtr mmappedBuffer = MmappedBuffer::openBuffer(path, bufOffset, + size, false /* isUpdatable */); + if (!mmappedBuffer.get()) { + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + } + switch (FormatUtils::detectFormatVersion(mmappedBuffer.get()->getBuffer(), + mmappedBuffer.get()->getBufferSize())) { + case FormatUtils::VERSION_2: + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new PatriciaTriePolicy(mmappedBuffer)); + case FormatUtils::VERSION_4: + AKLOGE("Given path is a file but the format is version 4. path: %s", path); + break; + default: + AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path); + break; + } + ASSERT(false); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); +} + +/* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir( + const char *const dictDirPath, const int outHeaderFileBufSize, + char *const outHeaderFilePath) { + const int dictNameBufSize = strlen(dictDirPath) + 1 /* terminator */; + char dictName[dictNameBufSize]; + FileUtils::getBasename(dictDirPath, dictNameBufSize, dictName); + snprintf(outHeaderFilePath, outHeaderFileBufSize, "%s/%s%s", dictDirPath, + dictName, Ver4DictConstants::HEADER_FILE_EXTENSION); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h index 8cebc3b16..45ab52931 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h @@ -21,16 +21,27 @@ #include "defines.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "utils/exclusive_ownership_pointer.h" namespace latinime { class DictionaryStructureWithBufferPolicyFactory { public: - static DictionaryStructureWithBufferPolicy *newDictionaryStructureWithBufferPolicy( - const char *const path, const int bufOffset, const int size, const bool isUpdatable); + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr + newDictionaryStructureWithBufferPolicy(const char *const path, const int bufOffset, + const int size, const bool isUpdatable); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory); + + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr + newPolicyforDirectoryDict(const char *const path, const bool isUpdatable); + + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr + newPolicyforFileDict(const char *const path, const int bufOffset, const int size); + + static void getHeaderFilePathInDictDir(const char *const dirPath, + const int outHeaderFileBufSize, char *const outHeaderFilePath); }; } // namespace latinime #endif // LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp new file mode 100644 index 000000000..8f42df6d2 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp @@ -0,0 +1,144 @@ +/* + * 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/structure/pt_common/dynamic_pt_gc_event_listeners.h" + +#include "suggest/core/policy/dictionary_header_structure_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" + +namespace latinime { + +bool DynamicPtGcEventListeners + ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless + // children. + bool isUselessPtNode = !ptNodeParams->isTerminal(); + if (ptNodeParams->isTerminal()) { + bool needsToKeepPtNode = true; + if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(ptNodeParams, + &needsToKeepPtNode)) { + AKLOGE("Cannot update PtNode probability or get needs to keep PtNode after GC."); + return false; + } + if (!needsToKeepPtNode) { + isUselessPtNode = true; + } + } + if (mChildrenValue > 0) { + isUselessPtNode = false; + } else if (ptNodeParams->isTerminal()) { + // Remove children as all children are useless. + if (!mPtNodeWriter->updateChildrenPosition(ptNodeParams, + NOT_A_DICT_POS /* newChildrenPosition */)) { + return false; + } + } + if (isUselessPtNode) { + // Current PtNode is no longer needed. Mark it as deleted. + if (!mPtNodeWriter->markPtNodeAsDeleted(ptNodeParams)) { + return false; + } + } else { + mValueStack.back() += 1; + if (ptNodeParams->isTerminal()) { + mValidUnigramCount += 1; + } + } + return true; +} + +bool DynamicPtGcEventListeners::TraversePolicyToUpdateBigramProbability + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + if (!ptNodeParams->isDeleted() && ptNodeParams->hasBigrams()) { + int bigramEntryCount = 0; + if (!mPtNodeWriter->updateAllBigramEntriesAndDeleteUselessEntries(ptNodeParams, + &bigramEntryCount)) { + return false; + } + mValidBigramEntryCount += bigramEntryCount; + } + return true; +} + +// Writes dummy PtNode array size when the head of PtNode array is read. +bool DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onDescend(const int ptNodeArrayPos) { + mValidPtNodeCount = 0; + int writingPos = mBufferToWrite->getTailPosition(); + mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.insert( + PtNodeWriter::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 DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition( + mBufferToWrite, 0 /* arraySize */, &writingPos); +} + +// Write PtNode array terminal and actual PtNode array size. +bool DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onReadingPtNodeArrayTail() { + int writingPos = mBufferToWrite->getTailPosition(); + // Write PtNode array terminal. + if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition( + mBufferToWrite, NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + // Write actual PtNode array size. + if (!DynamicPtWritingUtils::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 DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + if (ptNodeParams->isDeleted()) { + // Current PtNode is not written in new buffer because it has been deleted. + mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert( + PtNodeWriter::PtNodePositionRelocationMap::value_type( + ptNodeParams->getHeadPos(), NOT_A_DICT_POS)); + return true; + } + int writingPos = mBufferToWrite->getTailPosition(); + mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert( + PtNodeWriter::PtNodePositionRelocationMap::value_type( + ptNodeParams->getHeadPos(), writingPos)); + mValidPtNodeCount++; + // Writes current PtNode. + return mPtNodeWriter->writePtNodeAndAdvancePosition(ptNodeParams, &writingPos); +} + +bool DynamicPtGcEventListeners::TraversePolicyToUpdateAllPositionFields + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + // Updates parent position. + int bigramCount = 0; + if (!mPtNodeWriter->updateAllPositionFields(ptNodeParams, mDictPositionRelocationMap, + &bigramCount)) { + return false; + } + mBigramCount += bigramCount; + if (ptNodeParams->isTerminal()) { + mUnigramCount++; + } + 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/structure/pt_common/dynamic_pt_gc_event_listeners.h index 9755120b0..d8867754d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.h @@ -14,37 +14,32 @@ * limitations under the License. */ -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H +#ifndef LATINIME_DYNAMIC_PT_GC_EVENT_LISTENERS_H +#define LATINIME_DYNAMIC_PT_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/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" #include "utils/hash_map_compat.h" namespace latinime { -class DictionaryHeaderStructurePolicy; +class PtNodeParams; -class DynamicPatriciaTrieGcEventListeners { +class DynamicPtGcEventListeners { 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 DynamicPtReadingHelper::TraversingEventListener { public: TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( - const DictionaryHeaderStructurePolicy *const headerPolicy, - DynamicPatriciaTrieWritingHelper *const writingHelper, - BufferWithExtendableBuffer *const buffer, const bool isDecayingDict) - : mHeaderPolicy(headerPolicy), mWritingHelper(writingHelper), mBuffer(buffer), - mIsDecayingDict(isDecayingDict), mValueStack(), mChildrenValue(0), + PtNodeWriter *const ptNodeWriter) + : mPtNodeWriter(ptNodeWriter), mValueStack(), mChildrenValue(0), mValidUnigramCount(0) {} ~TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted() {}; @@ -66,8 +61,7 @@ class DynamicPatriciaTrieGcEventListeners { bool onReadingPtNodeArrayTail() { return true; } - bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, - const int *const nodeCodePoints); + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); int getValidUnigramCount() const { return mValidUnigramCount; @@ -77,10 +71,7 @@ class DynamicPatriciaTrieGcEventListeners { DISALLOW_IMPLICIT_CONSTRUCTORS( TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted); - const DictionaryHeaderStructurePolicy *const mHeaderPolicy; - DynamicPatriciaTrieWritingHelper *const mWritingHelper; - BufferWithExtendableBuffer *const mBuffer; - const bool mIsDecayingDict; + PtNodeWriter *const mPtNodeWriter; std::vector<int> mValueStack; int mChildrenValue; int mValidUnigramCount; @@ -89,11 +80,10 @@ class DynamicPatriciaTrieGcEventListeners { // Updates all bigram entries that are held by valid PtNodes. This removes useless bigram // entries. class TraversePolicyToUpdateBigramProbability - : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + : public DynamicPtReadingHelper::TraversingEventListener { public: - TraversePolicyToUpdateBigramProbability( - DynamicBigramListPolicy *const bigramPolicy) - : mBigramPolicy(bigramPolicy), mValidBigramEntryCount(0) {} + TraversePolicyToUpdateBigramProbability(PtNodeWriter *const ptNodeWriter) + : mPtNodeWriter(ptNodeWriter), mValidBigramEntryCount(0) {} bool onAscend() { return true; } @@ -101,8 +91,7 @@ class DynamicPatriciaTrieGcEventListeners { bool onReadingPtNodeArrayTail() { return true; } - bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, - const int *const nodeCodePoints); + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); int getValidBigramEntryCount() const { return mValidBigramEntryCount; @@ -111,19 +100,17 @@ class DynamicPatriciaTrieGcEventListeners { private: DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateBigramProbability); - DynamicBigramListPolicy *const mBigramPolicy; + PtNodeWriter *const mPtNodeWriter; int mValidBigramEntryCount; }; class TraversePolicyToPlaceAndWriteValidPtNodesToBuffer - : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + : public DynamicPtReadingHelper::TraversingEventListener { public: TraversePolicyToPlaceAndWriteValidPtNodesToBuffer( - DynamicPatriciaTrieWritingHelper *const writingHelper, - BufferWithExtendableBuffer *const bufferToWrite, - DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const - dictPositionRelocationMap) - : mWritingHelper(writingHelper), mBufferToWrite(bufferToWrite), + PtNodeWriter *const ptNodeWriter, BufferWithExtendableBuffer *const bufferToWrite, + PtNodeWriter::DictPositionRelocationMap *const dictPositionRelocationMap) + : mPtNodeWriter(ptNodeWriter), mBufferToWrite(bufferToWrite), mDictPositionRelocationMap(dictPositionRelocationMap), mValidPtNodeCount(0), mPtNodeArraySizeFieldPos(NOT_A_DICT_POS) {}; @@ -133,31 +120,24 @@ class DynamicPatriciaTrieGcEventListeners { bool onReadingPtNodeArrayTail(); - bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, - const int *const nodeCodePoints); + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); private: DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToPlaceAndWriteValidPtNodesToBuffer); - DynamicPatriciaTrieWritingHelper *const mWritingHelper; + PtNodeWriter *const mPtNodeWriter; BufferWithExtendableBuffer *const mBufferToWrite; - DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const - mDictPositionRelocationMap; + PtNodeWriter::DictPositionRelocationMap *const mDictPositionRelocationMap; int mValidPtNodeCount; int mPtNodeArraySizeFieldPos; }; class TraversePolicyToUpdateAllPositionFields - : public DynamicPatriciaTrieReadingHelper::TraversingEventListener { + : public DynamicPtReadingHelper::TraversingEventListener { public: - TraversePolicyToUpdateAllPositionFields( - DynamicPatriciaTrieWritingHelper *const writingHelper, - DynamicBigramListPolicy *const bigramPolicy, - BufferWithExtendableBuffer *const bufferToWrite, - const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const - dictPositionRelocationMap) - : mWritingHelper(writingHelper), mBigramPolicy(bigramPolicy), - mBufferToWrite(bufferToWrite), + TraversePolicyToUpdateAllPositionFields(PtNodeWriter *const ptNodeWriter, + const PtNodeWriter::DictPositionRelocationMap *const dictPositionRelocationMap) + : mPtNodeWriter(ptNodeWriter), mDictPositionRelocationMap(dictPositionRelocationMap), mUnigramCount(0), mBigramCount(0) {}; @@ -167,8 +147,7 @@ class DynamicPatriciaTrieGcEventListeners { bool onReadingPtNodeArrayTail() { return true; } - bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node, - const int *const nodeCodePoints); + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); int getUnigramCount() const { return mUnigramCount; @@ -181,17 +160,14 @@ class DynamicPatriciaTrieGcEventListeners { private: DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateAllPositionFields); - DynamicPatriciaTrieWritingHelper *const mWritingHelper; - DynamicBigramListPolicy *const mBigramPolicy; - BufferWithExtendableBuffer *const mBufferToWrite; - const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const - mDictPositionRelocationMap; + PtNodeWriter *const mPtNodeWriter; + const PtNodeWriter::DictPositionRelocationMap *const mDictPositionRelocationMap; int mUnigramCount; int mBigramCount; }; private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieGcEventListeners); + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPtGcEventListeners); }; } // namespace latinime -#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H */ +#endif /* LATINIME_DYNAMIC_PT_GC_EVENT_LISTENERS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp index f108c219f..b918e0765 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.cpp @@ -14,22 +14,25 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "utils/char_utils.h" 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; +const int DynamicPtReadingHelper::MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP = 100000; +const int DynamicPtReadingHelper::MAX_PT_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP = 100000; +const size_t DynamicPtReadingHelper::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( +bool DynamicPtReadingHelper::traverseAllPtNodesInPostorderDepthFirstManner( TraversingEventListener *const listener) { bool alreadyVisitedChildren = false; // Descend from the root to the root PtNode array. @@ -37,22 +40,26 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPostorderDepthFirstMa return false; } while (!isEnd()) { + const PtNodeParams ptNodeParams(getPtNodeParams()); + if (!ptNodeParams.isValid()) { + break; + } if (!alreadyVisitedChildren) { - if (mNodeReader.hasChildren()) { + if (ptNodeParams.hasChildren()) { // Move to the first child. - if (!listener->onDescend(mNodeReader.getChildrenPos())) { + if (!listener->onDescend(ptNodeParams.getChildrenPos())) { return false; } pushReadingStateToStack(); - readChildNode(); + readChildNode(ptNodeParams); } else { alreadyVisitedChildren = true; } } else { - if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) { + if (!listener->onVisitingPtNode(&ptNodeParams)) { return false; } - readNextSiblingNode(); + readNextSiblingNode(ptNodeParams); if (isEnd()) { // All PtNodes in current linked PtNode arrays have been visited. // Return to the parent. @@ -85,7 +92,7 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPostorderDepthFirstMa // For example, visits a -> b -> x -> c -> y for the following dictionary: // a _ b _ c // \ x _ y -bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( +bool DynamicPtReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( TraversingEventListener *const listener) { bool alreadyVisitedAllPtNodesInArray = false; bool alreadyVisitedChildren = false; @@ -101,10 +108,14 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreor } pushReadingStateToStack(); while (!isEnd()) { + const PtNodeParams ptNodeParams(getPtNodeParams()); + if (!ptNodeParams.isValid()) { + break; + } if (alreadyVisitedAllPtNodesInArray) { if (alreadyVisitedChildren) { // Move to next sibling PtNode's children. - readNextSiblingNode(); + readNextSiblingNode(ptNodeParams); if (isEnd()) { // Return to the parent PTNode. if (!listener->onAscend()) { @@ -120,13 +131,13 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreor alreadyVisitedChildren = false; } } else { - if (mNodeReader.hasChildren()) { + if (ptNodeParams.hasChildren()) { // Move to the first child. - if (!listener->onDescend(mNodeReader.getChildrenPos())) { + if (!listener->onDescend(ptNodeParams.getChildrenPos())) { return false; } pushReadingStateToStack(); - readChildNode(); + readChildNode(ptNodeParams); // Push state to return the head of PtNode array. pushReadingStateToStack(); alreadyVisitedAllPtNodesInArray = false; @@ -136,10 +147,10 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreor } } } else { - if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) { + if (!listener->onVisitingPtNode(&ptNodeParams)) { return false; } - readNextSiblingNode(); + readNextSiblingNode(ptNodeParams); if (isEnd()) { if (!listener->onReadingPtNodeArrayTail()) { return false; @@ -158,9 +169,95 @@ bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreor return !isError(); } +int DynamicPtReadingHelper::getCodePointsAndProbabilityAndReturnCodePointCount( + const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) { + // 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. + int reverseCodePoints[maxCodePointCount]; + const PtNodeParams terminalPtNodeParams(getPtNodeParams()); + // First, read the terminal node and get its probability. + if (!isValidTerminalNode(terminalPtNodeParams)) { + // Node at the ptNodePos is not a valid terminal node. + *outUnigramProbability = NOT_A_PROBABILITY; + return 0; + } + // Store terminal node probability. + *outUnigramProbability = terminalPtNodeParams.getProbability(); + // Then, following parent node link to the dictionary root and fetch node code points. + int totalCodePointCount = 0; + while (!isEnd()) { + const PtNodeParams ptNodeParams(getPtNodeParams()); + totalCodePointCount = getTotalCodePointCount(ptNodeParams); + if (!ptNodeParams.isValid() || totalCodePointCount > maxCodePointCount) { + // The ptNodePos is not a valid terminal node position in the dictionary. + *outUnigramProbability = NOT_A_PROBABILITY; + return 0; + } + // Store node code points to buffer in the reverse order. + fetchMergedNodeCodePointsInReverseOrder(ptNodeParams, getPrevTotalCodePointCount(), + reverseCodePoints); + // Follow parent node toward the root node. + readParentNode(ptNodeParams); + } + if (isError()) { + // The node position or the dictionary is invalid. + *outUnigramProbability = NOT_A_PROBABILITY; + return 0; + } + // Reverse the stored code points to output them. + for (int i = 0; i < totalCodePointCount; ++i) { + outCodePoints[i] = reverseCodePoints[totalCodePointCount - i - 1]; + } + return totalCodePointCount; +} + +int DynamicPtReadingHelper::getTerminalPtNodePositionOfWord(const int *const inWord, + const int length, const bool forceLowerCaseSearch) { + int searchCodePoints[length]; + for (int i = 0; i < length; ++i) { + searchCodePoints[i] = forceLowerCaseSearch ? CharUtils::toLowerCase(inWord[i]) : inWord[i]; + } + while (!isEnd()) { + const PtNodeParams ptNodeParams(getPtNodeParams()); + const int matchedCodePointCount = getPrevTotalCodePointCount(); + if (getTotalCodePointCount(ptNodeParams) > length + || !isMatchedCodePoint(ptNodeParams, 0 /* index */, + searchCodePoints[matchedCodePointCount])) { + // Current node has too many code points or its first code point is different from + // target code point. Skip this node and read the next sibling node. + readNextSiblingNode(ptNodeParams); + continue; + } + // Check following merged node code points. + const int nodeCodePointCount = ptNodeParams.getCodePointCount(); + for (int j = 1; j < nodeCodePointCount; ++j) { + if (!isMatchedCodePoint(ptNodeParams, j, searchCodePoints[matchedCodePointCount + j])) { + // Different code point is found. The given word is not included in the dictionary. + return NOT_A_DICT_POS; + } + } + // All characters are matched. + if (length == getTotalCodePointCount(ptNodeParams)) { + if (!ptNodeParams.isTerminal()) { + return NOT_A_DICT_POS; + } + // Terminal position is found. + return ptNodeParams.getHeadPos(); + } + if (!ptNodeParams.hasChildren()) { + return NOT_A_DICT_POS; + } + // Advance to the children nodes. + readChildNode(ptNodeParams); + } + // If we already traversed the tree further than the word is long, there means + // there was no match (or we would have found it). + return NOT_A_DICT_POS; +} + // 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::nextPtNodeArray() { +void DynamicPtReadingHelper::nextPtNodeArray() { if (mReadingState.mPos < 0 || mReadingState.mPos >= mBuffer->getTailPosition()) { // Reading invalid position because of a bug or a broken dictionary. AKLOGE("Reading PtNode array info from invalid dictionary position: %d, dict size: %d", @@ -170,42 +267,48 @@ void DynamicPatriciaTrieReadingHelper::nextPtNodeArray() { mReadingState.mPos = NOT_A_DICT_POS; return; } - mReadingState.mPosOfLastPtNodeArrayHead = mReadingState.mPos; + mReadingState.mPosOfThisPtNodeArrayHead = mReadingState.mPos; const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos); const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); if (usesAdditionalBuffer) { mReadingState.mPos -= mBuffer->getOriginalBufferSize(); } - mReadingState.mNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition( - dictBuf, &mReadingState.mPos); + mReadingState.mRemainingPtNodeCountInThisArray = + PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(dictBuf, + &mReadingState.mPos); if (usesAdditionalBuffer) { mReadingState.mPos += mBuffer->getOriginalBufferSize(); } // Count up nodes and node arrays 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) { + mReadingState.mTotalPtNodeIndexInThisArrayChain += + mReadingState.mRemainingPtNodeCountInThisArray; + mReadingState.mPtNodeArrayIndexInThisArrayChain++; + if (mReadingState.mRemainingPtNodeCountInThisArray < 0 + || mReadingState.mTotalPtNodeIndexInThisArrayChain + > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP + || mReadingState.mPtNodeArrayIndexInThisArrayChain + > MAX_PT_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", - mReadingState.mNodeCount, mReadingState.mTotalNodeCount, - MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP, mReadingState.mNodeArrayCount, - MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP); + mReadingState.mRemainingPtNodeCountInThisArray, + mReadingState.mTotalPtNodeIndexInThisArrayChain, + MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP, + mReadingState.mPtNodeArrayIndexInThisArrayChain, + MAX_PT_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP); ASSERT(false); mIsError = true; mReadingState.mPos = NOT_A_DICT_POS; return; } - if (mReadingState.mNodeCount == 0) { + if (mReadingState.mRemainingPtNodeCountInThisArray == 0) { // Empty node array. Try following forward link. followForwardLink(); } } // Follow the forward link and read the next node array if exists. -void DynamicPatriciaTrieReadingHelper::followForwardLink() { +void DynamicPtReadingHelper::followForwardLink() { if (mReadingState.mPos < 0 || mReadingState.mPos >= mBuffer->getTailPosition()) { // Reading invalid position because of bug or broken dictionary. AKLOGE("Reading forward link from invalid dictionary position: %d, dict size: %d", @@ -221,12 +324,12 @@ void DynamicPatriciaTrieReadingHelper::followForwardLink() { mReadingState.mPos -= mBuffer->getOriginalBufferSize(); } const int forwardLinkPosition = - DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(dictBuf, mReadingState.mPos); + DynamicPtReadingUtils::getForwardLinkPosition(dictBuf, mReadingState.mPos); if (usesAdditionalBuffer) { mReadingState.mPos += mBuffer->getOriginalBufferSize(); } mReadingState.mPosOfLastForwardLinkField = mReadingState.mPos; - if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) { + if (DynamicPtReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) { // Follow the forward link. mReadingState.mPos += forwardLinkPosition; nextPtNodeArray(); diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h index a71c06971..a69490943 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h @@ -14,16 +14,15 @@ * limitations under the License. */ -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H +#ifndef LATINIME_DYNAMIC_PT_READING_HELPER_H +#define LATINIME_DYNAMIC_PT_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" -#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h" namespace latinime { @@ -35,7 +34,7 @@ class DictionaryShortcutsStructurePolicy; * This class is used for traversing dynamic patricia trie. This class supports iterating nodes and * dealing with additional buffer. This class counts nodes and node arrays to avoid infinite loop. */ -class DynamicPatriciaTrieReadingHelper { +class DynamicPtReadingHelper { public: class TraversingEventListener { public: @@ -51,8 +50,7 @@ class DynamicPatriciaTrieReadingHelper { 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; + virtual bool onVisitingPtNode(const PtNodeParams *const node) = 0; protected: TraversingEventListener() {}; @@ -61,13 +59,12 @@ class DynamicPatriciaTrieReadingHelper { DISALLOW_COPY_AND_ASSIGN(TraversingEventListener); }; - DynamicPatriciaTrieReadingHelper(const BufferWithExtendableBuffer *const buffer, - const DictionaryBigramsStructurePolicy *const bigramsPolicy, - const DictionaryShortcutsStructurePolicy *const shortcutsPolicy) + DynamicPtReadingHelper(const BufferWithExtendableBuffer *const buffer, + const PtNodeReader *const ptNodeReader) : mIsError(false), mReadingState(), mBuffer(buffer), - mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy), mReadingStateStack() {} + mPtNodeReader(ptNodeReader), mReadingStateStack() {} - ~DynamicPatriciaTrieReadingHelper() {} + ~DynamicPtReadingHelper() {} AK_FORCE_INLINE bool isError() const { return mIsError; @@ -84,15 +81,12 @@ class DynamicPatriciaTrieReadingHelper { } else { mIsError = false; mReadingState.mPos = ptNodeArrayPos; - mReadingState.mPrevTotalCodePointCount = 0; - mReadingState.mTotalNodeCount = 0; - mReadingState.mNodeArrayCount = 0; + mReadingState.mTotalCodePointCountSinceInitialization = 0; + mReadingState.mTotalPtNodeIndexInThisArrayChain = 0; + mReadingState.mPtNodeArrayIndexInThisArrayChain = 0; mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; mReadingStateStack.clear(); nextPtNodeArray(); - if (!isEnd()) { - fetchPtNodeInfo(); - } } } @@ -103,94 +97,88 @@ class DynamicPatriciaTrieReadingHelper { } else { mIsError = false; mReadingState.mPos = ptNodePos; - mReadingState.mNodeCount = 1; - mReadingState.mPrevTotalCodePointCount = 0; - mReadingState.mTotalNodeCount = 1; - mReadingState.mNodeArrayCount = 1; + mReadingState.mRemainingPtNodeCountInThisArray = 1; + mReadingState.mTotalCodePointCountSinceInitialization = 0; + mReadingState.mTotalPtNodeIndexInThisArrayChain = 1; + mReadingState.mPtNodeArrayIndexInThisArrayChain = 1; mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; - mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS; + mReadingState.mPosOfThisPtNodeArrayHead = NOT_A_DICT_POS; mReadingStateStack.clear(); - fetchPtNodeInfo(); } } - AK_FORCE_INLINE const DynamicPatriciaTrieNodeReader* getNodeReader() const { - return &mNodeReader; + AK_FORCE_INLINE const PtNodeParams getPtNodeParams() const { + if (isEnd()) { + return PtNodeParams(); + } + return mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(mReadingState.mPos); } - AK_FORCE_INLINE bool isValidTerminalNode() const { - return !isEnd() && !mNodeReader.isDeleted() && mNodeReader.isTerminal(); + AK_FORCE_INLINE bool isValidTerminalNode(const PtNodeParams &ptNodeParams) const { + return !isEnd() && !ptNodeParams.isDeleted() && ptNodeParams.isTerminal(); } - AK_FORCE_INLINE bool isMatchedCodePoint(const int index, const int codePoint) const { - return mMergedNodeCodePoints[index] == codePoint; + AK_FORCE_INLINE bool isMatchedCodePoint(const PtNodeParams &ptNodeParams, const int index, + const int codePoint) const { + return ptNodeParams.getCodePoints()[index] == codePoint; } // Return code point count exclude the last read node's code points. AK_FORCE_INLINE int getPrevTotalCodePointCount() const { - return mReadingState.mPrevTotalCodePointCount; + return mReadingState.mTotalCodePointCountSinceInitialization; } // Return code point count include the last read node's code points. - AK_FORCE_INLINE int getTotalCodePointCount() const { - return mReadingState.mPrevTotalCodePointCount + mNodeReader.getCodePointCount(); + AK_FORCE_INLINE int getTotalCodePointCount(const PtNodeParams &ptNodeParams) const { + return mReadingState.mTotalCodePointCountSinceInitialization + + ptNodeParams.getCodePointCount(); } - AK_FORCE_INLINE void fetchMergedNodeCodePointsInReverseOrder( + AK_FORCE_INLINE void fetchMergedNodeCodePointsInReverseOrder(const PtNodeParams &ptNodeParams, const int index, int *const outCodePoints) const { - const int nodeCodePointCount = mNodeReader.getCodePointCount(); + const int nodeCodePointCount = ptNodeParams.getCodePointCount(); + const int *const nodeCodePoints = ptNodeParams.getCodePoints(); for (int i = 0; i < nodeCodePointCount; ++i) { - outCodePoints[index + i] = mMergedNodeCodePoints[nodeCodePointCount - 1 - i]; + outCodePoints[index + i] = nodeCodePoints[nodeCodePointCount - 1 - i]; } } - AK_FORCE_INLINE const int *getMergedNodeCodePoints() const { - return mMergedNodeCodePoints; - } - - AK_FORCE_INLINE void readNextSiblingNode() { - mReadingState.mNodeCount -= 1; - mReadingState.mPos = mNodeReader.getSiblingNodePos(); - if (mReadingState.mNodeCount <= 0) { + AK_FORCE_INLINE void readNextSiblingNode(const PtNodeParams &ptNodeParams) { + mReadingState.mRemainingPtNodeCountInThisArray -= 1; + mReadingState.mPos = ptNodeParams.getSiblingNodePos(); + if (mReadingState.mRemainingPtNodeCountInThisArray <= 0) { // All nodes in the current node array have been read. followForwardLink(); - if (!isEnd()) { - fetchPtNodeInfo(); - } - } else { - fetchPtNodeInfo(); } } // Read the first child node of the current node. - AK_FORCE_INLINE void readChildNode() { - if (mNodeReader.hasChildren()) { - mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); - mReadingState.mTotalNodeCount = 0; - mReadingState.mNodeArrayCount = 0; - mReadingState.mPos = mNodeReader.getChildrenPos(); + AK_FORCE_INLINE void readChildNode(const PtNodeParams &ptNodeParams) { + if (ptNodeParams.hasChildren()) { + mReadingState.mTotalCodePointCountSinceInitialization += + ptNodeParams.getCodePointCount(); + mReadingState.mTotalPtNodeIndexInThisArrayChain = 0; + mReadingState.mPtNodeArrayIndexInThisArrayChain = 0; + mReadingState.mPos = ptNodeParams.getChildrenPos(); mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; // Read children node array. nextPtNodeArray(); - if (!isEnd()) { - fetchPtNodeInfo(); - } } else { 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) { - mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount(); - mReadingState.mTotalNodeCount = 1; - mReadingState.mNodeArrayCount = 1; - mReadingState.mNodeCount = 1; - mReadingState.mPos = mNodeReader.getParentPos(); + AK_FORCE_INLINE void readParentNode(const PtNodeParams &ptNodeParams) { + if (ptNodeParams.getParentPos() != NOT_A_DICT_POS) { + mReadingState.mTotalCodePointCountSinceInitialization += + ptNodeParams.getCodePointCount(); + mReadingState.mTotalPtNodeIndexInThisArrayChain = 1; + mReadingState.mPtNodeArrayIndexInThisArrayChain = 1; + mReadingState.mRemainingPtNodeCountInThisArray = 1; + mReadingState.mPos = ptNodeParams.getParentPos(); mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS; - mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS; - fetchPtNodeInfo(); + mReadingState.mPosOfThisPtNodeArrayHead = NOT_A_DICT_POS; } else { mReadingState.mPos = NOT_A_DICT_POS; } @@ -201,13 +189,7 @@ class DynamicPatriciaTrieReadingHelper { } AK_FORCE_INLINE int getPosOfLastPtNodeArrayHead() const { - return mReadingState.mPosOfLastPtNodeArrayHead; - } - - AK_FORCE_INLINE void reloadCurrentPtNodeInfo() { - if (!isEnd()) { - fetchPtNodeInfo(); - } + return mReadingState.mPosOfThisPtNodeArrayHead; } bool traverseAllPtNodesInPostorderDepthFirstManner(TraversingEventListener *const listener); @@ -215,53 +197,54 @@ class DynamicPatriciaTrieReadingHelper { bool traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( TraversingEventListener *const listener); + int getCodePointsAndProbabilityAndReturnCodePointCount(const int maxCodePointCount, + int *const outCodePoints, int *const outUnigramProbability); + + int getTerminalPtNodePositionOfWord(const int *const inWord, const int length, + const bool forceLowerCaseSearch); + private: - DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieReadingHelper); + DISALLOW_COPY_AND_ASSIGN(DynamicPtReadingHelper); - class ReadingState { + // This class encapsulates the reading state of a position in the dictionary. It points at a + // specific PtNode in the dictionary. + class PtNodeReadingState { 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) {} + PtNodeReadingState() : mPos(NOT_A_DICT_POS), mRemainingPtNodeCountInThisArray(0), + mTotalCodePointCountSinceInitialization(0), mTotalPtNodeIndexInThisArrayChain(0), + mPtNodeArrayIndexInThisArrayChain(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS), + mPosOfThisPtNodeArrayHead(NOT_A_DICT_POS) {} int mPos; - // Node count of a node array. - int mNodeCount; - int mPrevTotalCodePointCount; - int mTotalNodeCount; - int mNodeArrayCount; + // Remaining node count in the current array. + int mRemainingPtNodeCountInThisArray; + int mTotalCodePointCountSinceInitialization; + // Counter of PtNodes used to avoid infinite loops caused by broken or malicious links. + int mTotalPtNodeIndexInThisArrayChain; + // Counter of PtNode arrays used to avoid infinite loops caused by cyclic links of empty + // PtNode arrays. + int mPtNodeArrayIndexInThisArrayChain; int mPosOfLastForwardLinkField; - int mPosOfLastPtNodeArrayHead; + int mPosOfThisPtNodeArrayHead; }; static const int MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP; - static const int MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP; + static const int MAX_PT_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP; static const size_t MAX_READING_STATE_STACK_SIZE; // TODO: Introduce error code to track what caused the error. bool mIsError; - ReadingState mReadingState; + PtNodeReadingState mReadingState; const BufferWithExtendableBuffer *const mBuffer; - DynamicPatriciaTrieNodeReader mNodeReader; - int mMergedNodeCodePoints[MAX_WORD_LENGTH]; - std::vector<ReadingState> mReadingStateStack; + const PtNodeReader *const mPtNodeReader; + std::vector<PtNodeReadingState> mReadingStateStack; void nextPtNodeArray(); void followForwardLink(); - AK_FORCE_INLINE void fetchPtNodeInfo() { - mNodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(mReadingState.mPos, - MAX_WORD_LENGTH, mMergedNodeCodePoints); - if (mNodeReader.getCodePointCount() <= 0) { - // Empty node is not allowed. - mIsError = true; - 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: %zd", MAX_READING_STATE_STACK_SIZE); @@ -279,11 +262,8 @@ class DynamicPatriciaTrieReadingHelper { } else { mReadingState = mReadingStateStack.back(); mReadingStateStack.pop_back(); - if (!isEnd()) { - fetchPtNodeInfo(); - } } } }; } // namespace latinime -#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H */ +#endif /* LATINIME_DYNAMIC_PT_READING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.cpp index d68446db6..3586b50ab 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.cpp @@ -14,38 +14,38 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" #include "defines.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" namespace latinime { -typedef DynamicPatriciaTrieReadingUtils DptReadingUtils; - -const DptReadingUtils::NodeFlags DptReadingUtils::MASK_MOVED = 0xC0; -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; +const DynamicPtReadingUtils::NodeFlags DynamicPtReadingUtils::MASK_MOVED = 0xC0; +const DynamicPtReadingUtils::NodeFlags DynamicPtReadingUtils::FLAG_IS_NOT_MOVED = 0xC0; +const DynamicPtReadingUtils::NodeFlags DynamicPtReadingUtils::FLAG_IS_MOVED = 0x40; +const DynamicPtReadingUtils::NodeFlags DynamicPtReadingUtils::FLAG_IS_DELETED = 0x80; +const DynamicPtReadingUtils::NodeFlags DynamicPtReadingUtils::FLAG_WILL_BECOME_NON_TERMINAL = 0x00; // 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; +const int DynamicPtReadingUtils::DICT_OFFSET_INVALID = 0; +const int DynamicPtReadingUtils::DICT_OFFSET_ZERO_OFFSET = 0x7FFFFF; -/* static */ int DptReadingUtils::getForwardLinkPosition(const uint8_t *const buffer, +/* static */ int DynamicPtReadingUtils::getForwardLinkPosition(const uint8_t *const buffer, const int pos) { int linkAddressPos = pos; return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos); } -/* static */ int DptReadingUtils::getParentPtNodePosOffsetAndAdvancePosition( +/* static */ int DynamicPtReadingUtils::getParentPtNodePosOffsetAndAdvancePosition( const uint8_t *const buffer, int *const pos) { return ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); } -/* static */ int DptReadingUtils::getParentPtNodePos(const int parentOffset, const int ptNodePos) { +/* static */ int DynamicPtReadingUtils::getParentPtNodePos(const int parentOffset, + const int ptNodePos) { if (parentOffset == DICT_OFFSET_INVALID) { return NOT_A_DICT_POS; } else if (parentOffset == DICT_OFFSET_ZERO_OFFSET) { @@ -55,7 +55,7 @@ const int DptReadingUtils::DICT_OFFSET_ZERO_OFFSET = 0x7FFFFF; } } -/* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition( +/* static */ int DynamicPtReadingUtils::readChildrenPositionAndAdvancePosition( const uint8_t *const buffer, int *const pos) { const int base = *pos; const int offset = ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos); diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h index 67c3cc57e..89ae12c0b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H +#ifndef LATINIME_DYNAMIC_PT_READING_UTILS_H +#define LATINIME_DYNAMIC_PT_READING_UTILS_H #include <stdint.h> @@ -23,7 +23,7 @@ namespace latinime { -class DynamicPatriciaTrieReadingUtils { +class DynamicPtReadingUtils { public: typedef uint8_t NodeFlags; @@ -54,22 +54,30 @@ class DynamicPatriciaTrieReadingUtils { return FLAG_IS_DELETED == (MASK_MOVED & flags); } + static AK_FORCE_INLINE bool willBecomeNonTerminal(const NodeFlags flags) { + return FLAG_WILL_BECOME_NON_TERMINAL == (MASK_MOVED & flags); + } + static AK_FORCE_INLINE NodeFlags updateAndGetFlags(const NodeFlags originalFlags, - const bool isMoved, const bool isDeleted) { + const bool isMoved, const bool isDeleted, const bool willBecomeNonTerminal) { NodeFlags flags = originalFlags; + flags = willBecomeNonTerminal ? + ((flags & (~MASK_MOVED)) | FLAG_WILL_BECOME_NON_TERMINAL) : flags; flags = isMoved ? ((flags & (~MASK_MOVED)) | FLAG_IS_MOVED) : flags; flags = isDeleted ? ((flags & (~MASK_MOVED)) | FLAG_IS_DELETED) : flags; - flags = (!isMoved && !isDeleted) ? ((flags & (~MASK_MOVED)) | FLAG_IS_NOT_MOVED) : flags; + flags = (!isMoved && !isDeleted && !willBecomeNonTerminal) ? + ((flags & (~MASK_MOVED)) | FLAG_IS_NOT_MOVED) : flags; return flags; } private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils); + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPtReadingUtils); static const NodeFlags MASK_MOVED; static const NodeFlags FLAG_IS_NOT_MOVED; static const NodeFlags FLAG_IS_MOVED; static const NodeFlags FLAG_IS_DELETED; + static const NodeFlags FLAG_WILL_BECOME_NON_TERMINAL; }; } // namespace latinime -#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_READING_UTILS_H */ +#endif /* LATINIME_DYNAMIC_PT_READING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp new file mode 100644 index 000000000..2457b49c8 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp @@ -0,0 +1,294 @@ +/* + * 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/structure/pt_common/dynamic_pt_updating_helper.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +const int DynamicPtUpdatingHelper::CHILDREN_POSITION_FIELD_SIZE = 3; + +bool DynamicPtUpdatingHelper::addUnigramWord( + DynamicPtReadingHelper *const readingHelper, + const int *const wordCodePoints, const int codePointCount, const int probability, + const bool isNotAWord, const bool isBlacklisted, const int timestamp, + bool *const outAddedNewUnigram) { + int parentPos = NOT_A_DICT_POS; + while (!readingHelper->isEnd()) { + const PtNodeParams ptNodeParams(readingHelper->getPtNodeParams()); + if (!ptNodeParams.isValid()) { + break; + } + const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount(); + if (!readingHelper->isMatchedCodePoint(ptNodeParams, 0 /* index */, + wordCodePoints[matchedCodePointCount])) { + // The first code point is different from target code point. Skip this node and read + // the next sibling node. + readingHelper->readNextSiblingNode(ptNodeParams); + continue; + } + // Check following merged node code points. + const int nodeCodePointCount = ptNodeParams.getCodePointCount(); + for (int j = 1; j < nodeCodePointCount; ++j) { + const int nextIndex = matchedCodePointCount + j; + if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(ptNodeParams, j, + wordCodePoints[matchedCodePointCount + j])) { + *outAddedNewUnigram = true; + return reallocatePtNodeAndAddNewPtNodes(&ptNodeParams, j, isNotAWord, isBlacklisted, + probability, timestamp, wordCodePoints + matchedCodePointCount, + codePointCount - matchedCodePointCount); + } + } + // All characters are matched. + if (codePointCount == readingHelper->getTotalCodePointCount(ptNodeParams)) { + return setPtNodeProbability(&ptNodeParams, isNotAWord, isBlacklisted, probability, + timestamp, outAddedNewUnigram); + } + if (!ptNodeParams.hasChildren()) { + *outAddedNewUnigram = true; + return createChildrenPtNodeArrayAndAChildPtNode(&ptNodeParams, + isNotAWord, isBlacklisted, probability, timestamp, + wordCodePoints + readingHelper->getTotalCodePointCount(ptNodeParams), + codePointCount - readingHelper->getTotalCodePointCount(ptNodeParams)); + } + // Advance to the children nodes. + parentPos = ptNodeParams.getHeadPos(); + readingHelper->readChildNode(ptNodeParams); + } + if (readingHelper->isError()) { + // The dictionary is invalid. + return false; + } + int pos = readingHelper->getPosOfLastForwardLinkField(); + *outAddedNewUnigram = true; + return createAndInsertNodeIntoPtNodeArray(parentPos, + wordCodePoints + readingHelper->getPrevTotalCodePointCount(), + codePointCount - readingHelper->getPrevTotalCodePointCount(), + isNotAWord, isBlacklisted, probability, timestamp, &pos); +} + +bool DynamicPtUpdatingHelper::addBigramWords(const int word0Pos, const int word1Pos, + const int probability, const int timestamp, bool *const outAddedNewBigram) { + const PtNodeParams sourcePtNodeParams( + mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word0Pos)); + const PtNodeParams targetPtNodeParams( + mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word1Pos)); + return mPtNodeWriter->addNewBigramEntry(&sourcePtNodeParams, &targetPtNodeParams, probability, + timestamp, outAddedNewBigram); +} + +// Remove a bigram relation from word0Pos to word1Pos. +bool DynamicPtUpdatingHelper::removeBigramWords(const int word0Pos, const int word1Pos) { + const PtNodeParams sourcePtNodeParams( + mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word0Pos)); + const PtNodeParams targetPtNodeParams( + mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(word1Pos)); + return mPtNodeWriter->removeBigramEntry(&sourcePtNodeParams, &targetPtNodeParams); +} + +bool DynamicPtUpdatingHelper::addShortcutTarget(const int wordPos, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability) { + const PtNodeParams ptNodeParams(mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(wordPos)); + return mPtNodeWriter->addShortcutTarget(&ptNodeParams, targetCodePoints, targetCodePointCount, + shortcutProbability); +} + +bool DynamicPtUpdatingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos, + const int *const nodeCodePoints, const int nodeCodePointCount, + const bool isNotAWord, const bool isBlacklisted, const int probability, + const int timestamp, int *const forwardLinkFieldPos) { + const int newPtNodeArrayPos = mBuffer->getTailPosition(); + if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + newPtNodeArrayPos, forwardLinkFieldPos)) { + return false; + } + return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount, + isNotAWord, isBlacklisted, probability, timestamp); +} + +bool DynamicPtUpdatingHelper::setPtNodeProbability( + const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, + const bool isBlacklisted, const int probability, const int timestamp, + bool *const outAddedNewUnigram) { + if (originalPtNodeParams->isTerminal()) { + // Overwrites the probability. + *outAddedNewUnigram = false; + return mPtNodeWriter->updatePtNodeProbability(originalPtNodeParams, probability, timestamp); + } else { + // Make the node terminal and write the probability. + *outAddedNewUnigram = true; + const int movedPos = mBuffer->getTailPosition(); + int writingPos = movedPos; + const PtNodeParams ptNodeParamsToWrite(getUpdatedPtNodeParams(originalPtNodeParams, + isNotAWord, isBlacklisted, true /* isTerminal */, + originalPtNodeParams->getParentPos(), originalPtNodeParams->getCodePointCount(), + originalPtNodeParams->getCodePoints(), probability)); + if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, + timestamp, &writingPos)) { + return false; + } + if (!mPtNodeWriter->markPtNodeAsMoved(originalPtNodeParams, movedPos, movedPos)) { + return false; + } + } + return true; +} + +bool DynamicPtUpdatingHelper::createChildrenPtNodeArrayAndAChildPtNode( + const PtNodeParams *const parentPtNodeParams, const bool isNotAWord, + const bool isBlacklisted, const int probability, const int timestamp, + const int *const codePoints, const int codePointCount) { + const int newPtNodeArrayPos = mBuffer->getTailPosition(); + if (!mPtNodeWriter->updateChildrenPosition(parentPtNodeParams, newPtNodeArrayPos)) { + return false; + } + return createNewPtNodeArrayWithAChildPtNode(parentPtNodeParams->getHeadPos(), codePoints, + codePointCount, isNotAWord, isBlacklisted, probability, timestamp); +} + +bool DynamicPtUpdatingHelper::createNewPtNodeArrayWithAChildPtNode( + const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount, + const bool isNotAWord, const bool isBlacklisted, const int probability, + const int timestamp) { + int writingPos = mBuffer->getTailPosition(); + if (!DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, + 1 /* arraySize */, &writingPos)) { + return false; + } + const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( + isNotAWord, isBlacklisted, true /* isTerminal */, + parentPtNodePos, nodeCodePointCount, nodeCodePoints, probability)); + if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, timestamp, + &writingPos)) { + return false; + } + if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + return true; +} + +// Returns whether the dictionary updating was succeeded or not. +bool DynamicPtUpdatingHelper::reallocatePtNodeAndAddNewPtNodes( + const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, + const bool isNotAWord, const bool isBlacklisted, const int probabilityOfNewPtNode, + const int timestamp, const int *const newNodeCodePoints, const int newNodeCodePointCount) { + // When addsExtraChild is true, split the reallocating PtNode and add new child. + // Reallocating PtNode: abcde, newNode: abcxy. + // abc (1st, not terminal) __ de (2nd) + // \_ xy (extra child, terminal) + // Otherwise, this method makes 1st part terminal and write probabilityOfNewPtNode. + // Reallocating PtNode: abcde, newNode: abc. + // abc (1st, terminal) __ de (2nd) + const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount; + const int firstPartOfReallocatedPtNodePos = mBuffer->getTailPosition(); + int writingPos = firstPartOfReallocatedPtNodePos; + // Write the 1st part of the reallocating node. The children position will be updated later + // with actual children position. + if (addsExtraChild) { + const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( + false /* isNotAWord */, false /* isBlacklisted */, false /* isTerminal */, + reallocatingPtNodeParams->getParentPos(), overlappingCodePointCount, + reallocatingPtNodeParams->getCodePoints(), NOT_A_PROBABILITY)); + if (!mPtNodeWriter->writePtNodeAndAdvancePosition(&ptNodeParamsToWrite, &writingPos)) { + return false; + } + } else { + const PtNodeParams ptNodeParamsToWrite(getPtNodeParamsForNewPtNode( + isNotAWord, isBlacklisted, true /* isTerminal */, + reallocatingPtNodeParams->getParentPos(), overlappingCodePointCount, + reallocatingPtNodeParams->getCodePoints(), probabilityOfNewPtNode)); + if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&ptNodeParamsToWrite, + timestamp, &writingPos)) { + return false; + } + } + const int actualChildrenPos = writingPos; + // Create new children PtNode array. + const size_t newPtNodeCount = addsExtraChild ? 2 : 1; + if (!DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer, + newPtNodeCount, &writingPos)) { + return false; + } + // Write the 2nd part of the reallocating node. + const int secondPartOfReallocatedPtNodePos = writingPos; + const PtNodeParams childPartPtNodeParams(getUpdatedPtNodeParams(reallocatingPtNodeParams, + reallocatingPtNodeParams->isNotAWord(), reallocatingPtNodeParams->isBlacklisted(), + reallocatingPtNodeParams->isTerminal(), firstPartOfReallocatedPtNodePos, + reallocatingPtNodeParams->getCodePointCount() - overlappingCodePointCount, + reallocatingPtNodeParams->getCodePoints() + overlappingCodePointCount, + reallocatingPtNodeParams->getProbability())); + if (!mPtNodeWriter->writePtNodeAndAdvancePosition(&childPartPtNodeParams, &writingPos)) { + return false; + } + if (addsExtraChild) { + const PtNodeParams extraChildPtNodeParams(getPtNodeParamsForNewPtNode( + isNotAWord, isBlacklisted, true /* isTerminal */, + firstPartOfReallocatedPtNodePos, newNodeCodePointCount - overlappingCodePointCount, + newNodeCodePoints + overlappingCodePointCount, probabilityOfNewPtNode)); + if (!mPtNodeWriter->writeNewTerminalPtNodeAndAdvancePosition(&extraChildPtNodeParams, + timestamp, &writingPos)) { + return false; + } + } + if (!DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer, + NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) { + return false; + } + // Update original reallocating PtNode as moved. + if (!mPtNodeWriter->markPtNodeAsMoved(reallocatingPtNodeParams, firstPartOfReallocatedPtNodePos, + secondPartOfReallocatedPtNodePos)) { + return false; + } + // Load node info. Information of the 1st part will be fetched. + const PtNodeParams ptNodeParams( + mPtNodeReader->fetchNodeInfoInBufferFromPtNodePos(firstPartOfReallocatedPtNodePos)); + // Update children position. + return mPtNodeWriter->updateChildrenPosition(&ptNodeParams, actualChildrenPos); +} + +const PtNodeParams DynamicPtUpdatingHelper::getUpdatedPtNodeParams( + const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, + const bool isBlacklisted, const bool isTerminal, const int parentPos, + const int codePointCount, const int *const codePoints, const int probability) const { + const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( + isBlacklisted, isNotAWord, isTerminal, originalPtNodeParams->hasShortcutTargets(), + originalPtNodeParams->hasBigrams(), codePointCount > 1 /* hasMultipleChars */, + CHILDREN_POSITION_FIELD_SIZE); + return PtNodeParams(originalPtNodeParams, flags, parentPos, codePointCount, codePoints, + probability); +} + +const PtNodeParams DynamicPtUpdatingHelper::getPtNodeParamsForNewPtNode( + const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, + const int parentPos, const int codePointCount, const int *const codePoints, + const int probability) const { + const PatriciaTrieReadingUtils::NodeFlags flags = PatriciaTrieReadingUtils::createAndGetFlags( + isBlacklisted, isNotAWord, isTerminal, false /* hasShortcutTargets */, + false /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */, + CHILDREN_POSITION_FIELD_SIZE); + return PtNodeParams(flags, parentPos, codePointCount, codePoints, probability); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h new file mode 100644 index 000000000..71f473096 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h @@ -0,0 +1,99 @@ +/* + * 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_PT_UPDATING_HELPER_H +#define LATINIME_DYNAMIC_PT_UPDATING_HELPER_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "utils/hash_map_compat.h" + +namespace latinime { + +class BufferWithExtendableBuffer; +class DynamicPtReadingHelper; +class PtNodeReader; +class PtNodeWriter; + +class DynamicPtUpdatingHelper { + public: + DynamicPtUpdatingHelper(BufferWithExtendableBuffer *const buffer, + const PtNodeReader *const ptNodeReader, PtNodeWriter *const ptNodeWriter) + : mBuffer(buffer), mPtNodeReader(ptNodeReader), mPtNodeWriter(ptNodeWriter) {} + + ~DynamicPtUpdatingHelper() {} + + // Add a word to the dictionary. If the word already exists, update the probability. + bool addUnigramWord(DynamicPtReadingHelper *const readingHelper, + const int *const wordCodePoints, const int codePointCount, const int probability, + const bool isNotAWord, const bool isBlacklisted, const int timestamp, + bool *const outAddedNewUnigram); + + // Add a bigram relation from word0Pos to word1Pos. + bool addBigramWords(const int word0Pos, const int word1Pos, const int probability, + const int timestamp, bool *const outAddedNewBigram); + + // Remove a bigram relation from word0Pos to word1Pos. + bool removeBigramWords(const int word0Pos, const int word1Pos); + + // Add a shortcut target. + bool addShortcutTarget(const int wordPos, const int *const targetCodePoints, + const int targetCodePointCount, const int shortcutProbability); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPtUpdatingHelper); + + static const int CHILDREN_POSITION_FIELD_SIZE; + + BufferWithExtendableBuffer *const mBuffer; + const PtNodeReader *const mPtNodeReader; + PtNodeWriter *const mPtNodeWriter; + + bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints, + const int nodeCodePointCount, const bool isNotAWord, const bool isBlacklisted, + const int probability, const int timestamp, int *const forwardLinkFieldPos); + + bool setPtNodeProbability(const PtNodeParams *const originalPtNodeParams, const bool isNotAWord, + const bool isBlacklisted, const int probability, const int timestamp, + bool *const outAddedNewUnigram); + + bool createChildrenPtNodeArrayAndAChildPtNode(const PtNodeParams *const parentPtNodeParams, + const bool isNotAWord, const bool isBlacklisted, const int probability, + const int timestamp, const int *const codePoints, const int codePointCount); + + bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints, + const int nodeCodePointCount, const bool isNotAWord, const bool isBlacklisted, + const int probability, const int timestamp); + + bool reallocatePtNodeAndAddNewPtNodes( + const PtNodeParams *const reallocatingPtNodeParams, const int overlappingCodePointCount, + const bool isNotAWord, const bool isBlacklisted, const int probabilityOfNewPtNode, + const int timestamp, const int *const newNodeCodePoints, + const int newNodeCodePointCount); + + const PtNodeParams getUpdatedPtNodeParams(const PtNodeParams *const originalPtNodeParams, + const bool isNotAWord, const bool isBlacklisted, const bool isTerminal, + const int parentPos, const int codePointCount, + const int *const codePoints, const int probability) const; + + const PtNodeParams getPtNodeParamsForNewPtNode(const bool isNotAWord, const bool isBlacklisted, + const bool isTerminal, const int parentPos, + const int codePointCount, const int *const codePoints, const int probability) const; +}; +} // namespace latinime +#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_UPDATING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.cpp index 30ff10cd6..ebbdc2ea2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include <cstddef> #include <cstdlib> @@ -24,19 +24,18 @@ namespace latinime { -const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F; -const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF; -const int DynamicPatriciaTrieWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1; -const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2; -const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000; -const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_FIELD_SIZE = 3; -const int DynamicPatriciaTrieWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF; -const int DynamicPatriciaTrieWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF; -const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000; -const int DynamicPatriciaTrieWritingUtils::PROBABILITY_FIELD_SIZE = 1; -const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; +const size_t DynamicPtWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F; +const size_t DynamicPtWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF; +const int DynamicPtWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1; +const int DynamicPtWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2; +const int DynamicPtWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000; +const int DynamicPtWritingUtils::DICT_OFFSET_FIELD_SIZE = 3; +const int DynamicPtWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF; +const int DynamicPtWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF; +const int DynamicPtWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000; +const int DynamicPtWritingUtils::NODE_FLAG_FIELD_SIZE = 1; -/* static */ bool DynamicPatriciaTrieWritingUtils::writeEmptyDictionary( +/* static */ bool DynamicPtWritingUtils::writeEmptyDictionary( BufferWithExtendableBuffer *const buffer, const int rootPos) { int writingPos = rootPos; if (!writePtNodeArraySizeAndAdvancePosition(buffer, 0 /* arraySize */, &writingPos)) { @@ -46,13 +45,13 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; &writingPos); } -/* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writeForwardLinkPositionAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, int *const forwardLinkFieldPos) { return writeDictOffset(buffer, forwardLinkPos, (*forwardLinkFieldPos), forwardLinkFieldPos); } -/* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writePtNodeArraySizeAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const size_t arraySize, int *const arraySizeFieldPos) { // Currently, all array size field to be created has LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE to @@ -74,20 +73,20 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; } } -/* static */ bool DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writeFlagsAndAdvancePosition( BufferWithExtendableBuffer *const buffer, - const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) { + const DynamicPtReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) { return buffer->writeUintAndAdvancePosition(nodeFlags, NODE_FLAG_FIELD_SIZE, nodeFlagsFieldPos); } // Note that parentOffset is offset from node's head position. -/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int parentPos, const int basePos, int *const parentPosFieldPos) { return writeDictOffset(buffer, parentPos, basePos, parentPosFieldPos); } -/* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writeCodePointsAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int *const codePoints, const int codePointCount, int *const codePointFieldPos) { if (codePointCount <= 0) { @@ -101,34 +100,20 @@ const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; hasMultipleCodePoints, codePointFieldPos); } -/* static */ bool DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition( - BufferWithExtendableBuffer *const buffer, const int probability, - int *const probabilityFieldPos) { - if (probability < 0 || probability > MAX_PROBABILITY) { - AKLOGI("probability cannot be written because the probability is invalid: %d", - probability); - ASSERT(false); - return false; - } - return buffer->writeUintAndAdvancePosition(probability, PROBABILITY_FIELD_SIZE, - probabilityFieldPos); -} - -/* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( +/* static */ bool DynamicPtWritingUtils::writeChildrenPositionAndAdvancePosition( BufferWithExtendableBuffer *const buffer, const int childrenPosition, int *const childrenPositionFieldPos) { return writeDictOffset(buffer, childrenPosition, (*childrenPositionFieldPos), childrenPositionFieldPos); } -/* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset( - BufferWithExtendableBuffer *const buffer, const int targetPos, const int basePos, - int *const offsetFieldPos) { +/* static */ bool DynamicPtWritingUtils::writeDictOffset(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; + offset = DynamicPtReadingUtils::DICT_OFFSET_INVALID; } else if (offset == 0) { - offset = DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET; + offset = DynamicPtReadingUtils::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", diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h index af76bc6b5..362fbd1cc 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h @@ -14,19 +14,19 @@ * limitations under the License. */ -#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H -#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H +#ifndef LATINIME_DYNAMIC_PT_WRITING_UTILS_H +#define LATINIME_DYNAMIC_PT_WRITING_UTILS_H #include <cstddef> #include "defines.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" namespace latinime { class BufferWithExtendableBuffer; -class DynamicPatriciaTrieWritingUtils { +class DynamicPtWritingUtils { public: static const int NODE_FLAG_FIELD_SIZE; @@ -39,8 +39,15 @@ class DynamicPatriciaTrieWritingUtils { static bool writePtNodeArraySizeAndAdvancePosition(BufferWithExtendableBuffer *const buffer, const size_t arraySize, int *const arraySizeFieldPos); + static bool writeFlags(BufferWithExtendableBuffer *const buffer, + const DynamicPtReadingUtils::NodeFlags nodeFlags, + const int nodeFlagsFieldPos) { + int writingPos = nodeFlagsFieldPos; + return writeFlagsAndAdvancePosition(buffer, nodeFlags, &writingPos); + } + static bool writeFlagsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, - const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, + const DynamicPtReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos); static bool writeParentPosOffsetAndAdvancePosition(BufferWithExtendableBuffer *const buffer, @@ -49,14 +56,11 @@ class DynamicPatriciaTrieWritingUtils { static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, const int *const codePoints, const int codePointCount, int *const codePointFieldPos); - static bool writeProbabilityAndAdvancePosition(BufferWithExtendableBuffer *const buffer, - const int probability, int *const probabilityFieldPos); - static bool writeChildrenPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer, const int childrenPosition, int *const childrenPositionFieldPos); private: - DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingUtils); + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPtWritingUtils); static const size_t MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD; static const size_t MAX_PTNODE_ARRAY_SIZE; @@ -67,10 +71,9 @@ 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 PROBABILITY_FIELD_SIZE; 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 */ +#endif /* LATINIME_DYNAMIC_PT_WRITING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h new file mode 100644 index 000000000..84731eb17 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h @@ -0,0 +1,229 @@ +/* + * 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_PT_NODE_PARAMS_H +#define LATINIME_PT_NODE_PARAMS_H + +#include <cstring> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" + +namespace latinime { + +// This class has information of a PtNode. This class is immutable. +class PtNodeParams { + public: + // Invalid PtNode. + PtNodeParams() : mHeadPos(NOT_A_DICT_POS), mFlags(0), mParentPos(NOT_A_DICT_POS), + mCodePointCount(0), mCodePoints(), mTerminalIdFieldPos(NOT_A_DICT_POS), + mTerminalId(Ver4DictConstants::NOT_A_TERMINAL_ID), mProbabilityFieldPos(NOT_A_DICT_POS), + mProbability(NOT_A_PROBABILITY), mChildrenPosFieldPos(NOT_A_DICT_POS), + mChildrenPos(NOT_A_DICT_POS), mBigramLinkedNodePos(NOT_A_DICT_POS), + mShortcutPos(NOT_A_DICT_POS), mBigramPos(NOT_A_DICT_POS), + mSiblingPos(NOT_A_DICT_POS) {} + + PtNodeParams(const PtNodeParams& ptNodeParams) + : mHeadPos(ptNodeParams.mHeadPos), mFlags(ptNodeParams.mFlags), + mParentPos(ptNodeParams.mParentPos), mCodePointCount(ptNodeParams.mCodePointCount), + mCodePoints(), mTerminalIdFieldPos(ptNodeParams.mTerminalIdFieldPos), + mTerminalId(ptNodeParams.mTerminalId), + mProbabilityFieldPos(ptNodeParams.mProbabilityFieldPos), + mProbability(ptNodeParams.mProbability), + mChildrenPosFieldPos(ptNodeParams.mChildrenPosFieldPos), + mChildrenPos(ptNodeParams.mChildrenPos), + mBigramLinkedNodePos(ptNodeParams.mBigramLinkedNodePos), + mShortcutPos(ptNodeParams.mShortcutPos), mBigramPos(ptNodeParams.mBigramPos), + mSiblingPos(ptNodeParams.mSiblingPos) { + memcpy(mCodePoints, ptNodeParams.getCodePoints(), sizeof(int) * mCodePointCount); + } + + // PtNode with a terminal id. + PtNodeParams(const int headPos, const PatriciaTrieReadingUtils::NodeFlags flags, + const int parentPos, const int codePointCount, const int *const codePoints, + const int terminalIdFieldPos, const int terminalId, const int probability, + const int childrenPosFieldPos, const int childrenPos, const int siblingPos) + : mHeadPos(headPos), mFlags(flags), mParentPos(parentPos), + mCodePointCount(codePointCount), mCodePoints(), + mTerminalIdFieldPos(terminalIdFieldPos), mTerminalId(terminalId), + mProbabilityFieldPos(NOT_A_DICT_POS), mProbability(probability), + mChildrenPosFieldPos(childrenPosFieldPos), mChildrenPos(childrenPos), + mBigramLinkedNodePos(NOT_A_DICT_POS), mShortcutPos(terminalId), + mBigramPos(terminalId), mSiblingPos(siblingPos) { + memcpy(mCodePoints, codePoints, sizeof(int) * mCodePointCount); + } + + // Construct new params by updating existing PtNode params. + PtNodeParams(const PtNodeParams *const ptNodeParams, + const PatriciaTrieReadingUtils::NodeFlags flags, const int parentPos, + const int codePointCount, const int *const codePoints, const int probability) + : mHeadPos(ptNodeParams->getHeadPos()), mFlags(flags), mParentPos(parentPos), + mCodePointCount(codePointCount), mCodePoints(), + mTerminalIdFieldPos(ptNodeParams->getTerminalIdFieldPos()), + mTerminalId(ptNodeParams->getTerminalId()), + mProbabilityFieldPos(ptNodeParams->getProbabilityFieldPos()), + mProbability(probability), + mChildrenPosFieldPos(ptNodeParams->getChildrenPosFieldPos()), + mChildrenPos(ptNodeParams->getChildrenPos()), + mBigramLinkedNodePos(ptNodeParams->getBigramLinkedNodePos()), + mShortcutPos(ptNodeParams->getShortcutPos()), + mBigramPos(ptNodeParams->getBigramsPos()), + mSiblingPos(ptNodeParams->getSiblingNodePos()) { + memcpy(mCodePoints, codePoints, sizeof(int) * mCodePointCount); + } + + PtNodeParams(const PatriciaTrieReadingUtils::NodeFlags flags, const int parentPos, + const int codePointCount, const int *const codePoints, const int probability) + : mHeadPos(NOT_A_DICT_POS), mFlags(flags), mParentPos(parentPos), + mCodePointCount(codePointCount), mCodePoints(), + mTerminalIdFieldPos(NOT_A_DICT_POS), + mTerminalId(Ver4DictConstants::NOT_A_TERMINAL_ID), + mProbabilityFieldPos(NOT_A_DICT_POS), mProbability(probability), + mChildrenPosFieldPos(NOT_A_DICT_POS), mChildrenPos(NOT_A_DICT_POS), + mBigramLinkedNodePos(NOT_A_DICT_POS), mShortcutPos(NOT_A_DICT_POS), + mBigramPos(NOT_A_DICT_POS), mSiblingPos(NOT_A_DICT_POS) { + memcpy(mCodePoints, codePoints, sizeof(int) * mCodePointCount); + } + + AK_FORCE_INLINE bool isValid() const { + return mCodePointCount > 0; + } + + // Head position of the PtNode + AK_FORCE_INLINE int getHeadPos() const { + return mHeadPos; + } + + // Flags + AK_FORCE_INLINE bool isDeleted() const { + return DynamicPtReadingUtils::isDeleted(mFlags); + } + + AK_FORCE_INLINE bool willBecomeNonTerminal() const { + return DynamicPtReadingUtils::willBecomeNonTerminal(mFlags); + } + + AK_FORCE_INLINE bool hasChildren() const { + return mChildrenPos != NOT_A_DICT_POS; + } + + AK_FORCE_INLINE bool isTerminal() const { + return PatriciaTrieReadingUtils::isTerminal(mFlags); + } + + AK_FORCE_INLINE bool isBlacklisted() const { + return PatriciaTrieReadingUtils::isBlacklisted(mFlags); + } + + AK_FORCE_INLINE bool isNotAWord() const { + return PatriciaTrieReadingUtils::isNotAWord(mFlags); + } + + AK_FORCE_INLINE bool hasBigrams() const { + return PatriciaTrieReadingUtils::hasBigrams(mFlags); + } + + AK_FORCE_INLINE bool hasShortcutTargets() const { + return PatriciaTrieReadingUtils::hasShortcutTargets(mFlags); + } + + // Parent node position + AK_FORCE_INLINE int getParentPos() const { + return mParentPos; + } + + // Number of code points + AK_FORCE_INLINE uint8_t getCodePointCount() const { + return mCodePointCount; + } + + AK_FORCE_INLINE const int *getCodePoints() const { + return mCodePoints; + } + + // Probability + AK_FORCE_INLINE int getTerminalIdFieldPos() const { + return mTerminalIdFieldPos; + } + + AK_FORCE_INLINE int getTerminalId() const { + return mTerminalId; + } + + // Probability + AK_FORCE_INLINE int getProbabilityFieldPos() const { + return mProbabilityFieldPos; + } + + AK_FORCE_INLINE int getProbability() const { + return mProbability; + } + + // Children PtNode array position + AK_FORCE_INLINE int getChildrenPosFieldPos() const { + return mChildrenPosFieldPos; + } + + AK_FORCE_INLINE int getChildrenPos() const { + return mChildrenPos; + } + + // Bigram linked node position. + AK_FORCE_INLINE int getBigramLinkedNodePos() const { + return mBigramLinkedNodePos; + } + + // Shortcutlist position + AK_FORCE_INLINE int getShortcutPos() const { + return mShortcutPos; + } + + // Bigrams position + AK_FORCE_INLINE int getBigramsPos() const { + return mBigramPos; + } + + // Sibling node position + AK_FORCE_INLINE int getSiblingNodePos() const { + return mSiblingPos; + } + + private: + // This class have a public copy constructor to be used as a return value. + + // Disallowing the assignment operator. + PtNodeParams &operator=(PtNodeParams &ptNodeParams); + + const int mHeadPos; + const PatriciaTrieReadingUtils::NodeFlags mFlags; + const int mParentPos; + const uint8_t mCodePointCount; + int mCodePoints[MAX_WORD_LENGTH]; + const int mTerminalIdFieldPos; + const int mTerminalId; + const int mProbabilityFieldPos; + const int mProbability; + const int mChildrenPosFieldPos; + const int mChildrenPos; + const int mBigramLinkedNodePos; + const int mShortcutPos; + const int mBigramPos; + const int mSiblingPos; +}; +} // namespace latinime +#endif /* LATINIME_PT_NODE_PARAMS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h new file mode 100644 index 000000000..c6b2a8bed --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_PT_NODE_READER_H +#define LATINIME_PT_NODE_READER_H + +#include "defines.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" + +namespace latinime { + +// Interface class used to read PtNode information. +class PtNodeReader { + public: + virtual ~PtNodeReader() {} + virtual const PtNodeParams fetchNodeInfoInBufferFromPtNodePos(const int ptNodePos) const = 0; + + protected: + PtNodeReader() {}; + + private: + DISALLOW_COPY_AND_ASSIGN(PtNodeReader); +}; +} // namespace latinime +#endif /* LATINIME_PT_NODE_READER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h new file mode 100644 index 000000000..84dd6870e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h @@ -0,0 +1,95 @@ +/* + * 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_PT_NODE_WRITER_H +#define LATINIME_PT_NODE_WRITER_H + +#include "defines.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "utils/hash_map_compat.h" + +namespace latinime { + +// Interface class used to write PtNode information. +class PtNodeWriter { + 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); + }; + + virtual ~PtNodeWriter() {} + + virtual bool markPtNodeAsDeleted(const PtNodeParams *const toBeUpdatedPtNodeParams) = 0; + + virtual bool markPtNodeAsMoved(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int movedPos, const int bigramLinkedNodePos) = 0; + + virtual bool markPtNodeAsWillBecomeNonTerminal( + const PtNodeParams *const toBeUpdatedPtNodeParams) = 0; + + virtual bool updatePtNodeProbability(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int probability, const int timestamp) = 0; + + virtual bool updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + const PtNodeParams *const toBeUpdatedPtNodeParams, + bool *const outNeedsToKeepPtNode) = 0; + + virtual bool updateChildrenPosition(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newChildrenPosition) = 0; + + virtual bool writePtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + int *const ptNodeWritingPos) = 0; + + virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + const int timestamp, int *const ptNodeWritingPos) = 0; + + virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, + bool *const outAddedNewBigram) = 0; + + virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam) = 0; + + virtual bool updateAllBigramEntriesAndDeleteUselessEntries( + const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount) = 0; + + virtual bool updateAllPositionFields(const PtNodeParams *const toBeUpdatedPtNodeParams, + const DictPositionRelocationMap *const dictPositionRelocationMap, + int *const outBigramEntryCount) = 0; + + virtual bool addShortcutTarget(const PtNodeParams *const ptNodeParams, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability) = 0; + + protected: + PtNodeWriter() {}; + + private: + DISALLOW_COPY_AND_ASSIGN(PtNodeWriter); +}; +} // namespace latinime +#endif /* LATINIME_PT_NODE_WRITER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp index 8a84bd261..960c1b936 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp @@ -15,22 +15,22 @@ */ -#include "suggest/policyimpl/dictionary/patricia_trie_policy.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" #include "defines.h" #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" -#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" namespace latinime { -void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, +void PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode, DicNodeVector *const childDicNodes) const { if (!dicNode->hasChildren()) { return; } - int nextPos = dicNode->getChildrenPos(); + int nextPos = dicNode->getChildrenPtNodeArrayPos(); if (nextPos < 0 || nextPos >= mDictBufferSize) { AKLOGE("Children PtNode array position is invalid. pos: %d, dict size: %d", nextPos, mDictBufferSize); @@ -52,14 +52,14 @@ void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode, // This retrieves code points and the probability of the word by its terminal position. // Due to the fact that words are ordered in the dictionary in a strict breadth-first order, -// it is possible to check for this with advantageous complexity. For each node, we search +// it is possible to check for this with advantageous complexity. For each PtNode array, we search // for PtNodes with children and compare the children position with the position we look for. // When we shoot the position we look for, it means the word we look for is in the children // of the previous PtNode. The only tricky part is the fact that if we arrive at the end of a // PtNode array with the last PtNode's children position still less than what we are searching for, // we must descend the last PtNode's children (for example, if the word we are searching for starts // 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). +// than the position we look for, and we have to descend the z PtNode). /* Parameters : * 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) @@ -74,9 +74,9 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( int pos = getRootPosition(); int wordPos = 0; // One iteration of the outer loop iterates through PtNode arrays. As stated above, we will - // only traverse nodes that are actually a part of the terminal we are searching, so each time - // we enter this loop we are one depth level further than last time. - // The only reason we count nodes is because we want to reduce the probability of infinite + // only traverse PtNodes that are actually a part of the terminal we are searching, so each + // time we enter this loop we are one depth level further than last time. + // The only reason we count PtNodes is because we want to reduce the probability of infinite // looping in case there is a bug. Since we know there is an upper bound to the depth we are // supposed to traverse, it does not hurt to count iterations. for (int loopCount = maxCodePointCount; loopCount > 0; --loopCount) { @@ -140,8 +140,9 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( found = true; } else if (1 >= ptNodeCount) { // However if we are on the LAST PtNode of this array, and we have NOT shot the - // position we should descend THIS node. So we trick the lastCandidatePtNodePos - // so that we will descend this PtNode, not the previous one. + // position we should descend THIS PtNode. So we trick the + // lastCandidatePtNodePos so that we will descend this PtNode, not the previous + // one. lastCandidatePtNodePos = startPos; found = true; } else { @@ -149,7 +150,7 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( found = false; } } else { - // Even if we don't have children here, we could still be on the last PtNode of / + // Even if we don't have children here, we could still be on the last PtNode of // this array. If this is the case, we should descend the last PtNode that had // children, and their position is already in lastCandidatePtNodePos. found = (1 >= ptNodeCount); @@ -230,9 +231,9 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( return 0; } -// This function gets the position of the terminal node of the exact matching word in the +// This function gets the position of the terminal PtNode of the exact matching word in the // dictionary. If no match is found, it returns NOT_A_DICT_POS. -int PatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const inWord, +int PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const { int pos = getRootPosition(); int wordPos = 0; diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index 0f8662aea..2adafd22b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -24,6 +24,7 @@ #include "suggest/policyimpl/dictionary/bigram/bigram_list_policy.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" #include "suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" namespace latinime { @@ -33,28 +34,26 @@ class DicNodeVector; class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: - PatriciaTriePolicy(const MmappedBuffer *const buffer) - : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()), - mDictRoot(mBuffer->getBuffer() + mHeaderPolicy.getSize()), - mDictBufferSize(mBuffer->getBufferSize() - mHeaderPolicy.getSize()), + PatriciaTriePolicy(const MmappedBuffer::MmappedBufferPtr &mmappedBuffer) + : mMmappedBuffer(mmappedBuffer), + mHeaderPolicy(mMmappedBuffer.get()->getBuffer(), FormatUtils::VERSION_2), + mDictRoot(mMmappedBuffer.get()->getBuffer() + mHeaderPolicy.getSize()), + mDictBufferSize(mMmappedBuffer.get()->getBufferSize() + - mHeaderPolicy.getSize()), mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot) {} - ~PatriciaTriePolicy() { - delete mBuffer; - } - AK_FORCE_INLINE int getRootPosition() const { return 0; } - void createAndGetAllChildNodes(const DicNode *const dicNode, + void createAndGetAllChildDicNodes(const DicNode *const dicNode, DicNodeVector *const childDicNodes) const; int getCodePointsAndProbabilityAndReturnCodePointCount( const int terminalNodePos, const int maxCodePointCount, int *const outCodePoints, int *const outUnigramProbability) const; - int getTerminalNodePositionOfWord(const int *const inWord, + int getTerminalPtNodePositionOfWord(const int *const inWord, const int length, const bool forceLowerCaseSearch) const; int getProbability(const int unigramProbability, const int bigramProbability) const; @@ -77,14 +76,17 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return &mShortcutListPolicy; } - bool addUnigramWord(const int *const word, const int length, const int probability) { + bool addUnigramWord(const int *const word, const int length, const int probability, + const int *const shortcutTargetCodePoints, const int shortcutLength, + const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, + const int timestamp) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); return false; } bool addBigramWords(const int *const word0, const int length0, const int *const word1, - const int length1, const int probability) { + const int length1, const int probability, const int timestamp) { // This method should not be called for non-updatable dictionary. AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); return false; @@ -113,7 +115,7 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { return false; } - void getProperty(const char *const query, char *const outResult, + void getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength) { // getProperty is not supported for this class. if (maxResultLength > 0) { @@ -121,10 +123,16 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { } } + const UnigramProperty getUnigramProperty(const int *const codePoints, + const int codePointCount) const { + // getUnigramProperty is not supported. + return UnigramProperty(); + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTriePolicy); - const MmappedBuffer *const mBuffer; + const MmappedBuffer::MmappedBufferPtr mMmappedBuffer; const HeaderPolicy mHeaderPolicy; const uint8_t *const mDictRoot; const int mDictBufferSize; diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.cpp index 7df55815f..82b3593c8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" #include "defines.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h index 8420ee95a..b28f58336 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h @@ -23,6 +23,7 @@ namespace latinime { +// TODO: Move to pt_common class PatriciaTrieReadingUtils { public: typedef uint8_t NodeFlags; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp new file mode 100644 index 000000000..cb9d450ec --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp @@ -0,0 +1,202 @@ +/* + * 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/structure/v4/content/bigram_dict_content.h" + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition( + int *const bigramEntryPos) const { + const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer(); + const int bigramFlags = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, bigramEntryPos); + const bool hasNext = (bigramFlags & Ver4DictConstants::BIGRAM_HAS_NEXT_MASK) != 0; + int probability = NOT_A_PROBABILITY; + int timestamp = NOT_A_TIMESTAMP; + int level = 0; + int count = 0; + if (mHasHistoricalInfo) { + probability = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos); + timestamp = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos); + level = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, bigramEntryPos); + count = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos); + } else { + probability = bigramFlags & Ver4DictConstants::BIGRAM_PROBABILITY_MASK; + } + const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos); + const int targetTerminalId = + (encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ? + Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId; + if (mHasHistoricalInfo) { + const HistoricalInfo historicalInfo(timestamp, level, count); + return BigramEntry(hasNext, probability, &historicalInfo, targetTerminalId); + } else { + return BigramEntry(hasNext, probability, targetTerminalId); + } +} + +bool BigramDictContent::writeBigramEntryAndAdvancePosition( + const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) { + BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer(); + const int bigramFlags = createAndGetBigramFlags( + mHasHistoricalInfo ? 0 : bigramEntryToWrite->getProbability(), + bigramEntryToWrite->hasNext()); + if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags, + Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags); + return false; + } + if (mHasHistoricalInfo) { + if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(), + Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos, + bigramEntryToWrite->getProbability()); + return false; + } + const HistoricalInfo *const historicalInfo = bigramEntryToWrite->getHistoricalInfo(); + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram timestamps. pos: %d, timestamp: %d", *entryWritingPos, + historicalInfo->getTimeStamp()); + return false; + } + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getLevel(), + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram level. pos: %d, level: %d", *entryWritingPos, + historicalInfo->getLevel()); + return false; + } + if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getCount(), + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram count. pos: %d, count: %d", *entryWritingPos, + historicalInfo->getCount()); + return false; + } + } + const int targetTerminalIdToWrite = + (bigramEntryToWrite->getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) ? + Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID : + bigramEntryToWrite->getTargetTerminalId(); + if (!bigramListBuffer->writeUintAndAdvancePosition(targetTerminalIdToWrite, + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, entryWritingPos)) { + AKLOGE("Cannot write bigram target terminal id. pos: %d, target terminal id: %d", + *entryWritingPos, bigramEntryToWrite->getTargetTerminalId()); + return false; + } + return true; +} + +bool BigramDictContent::copyBigramList(const int bigramListPos, const int toPos) { + int readingPos = bigramListPos; + int writingPos = toPos; + bool hasNext = true; + while (hasNext) { + const BigramEntry bigramEntry = getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!writeBigramEntryAndAdvancePosition(&bigramEntry, &writingPos)) { + AKLOGE("Cannot write bigram entry to copy. pos: %d", writingPos); + return false; + } + } + return true; +} + +bool BigramDictContent::runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const BigramDictContent *const originalBigramDictContent, + int *const outBigramEntryCount) { + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const int originalBigramListPos = + originalBigramDictContent->getBigramListHeadPos(it->first); + if (originalBigramListPos == NOT_A_DICT_POS) { + // This terminal does not have a bigram list. + continue; + } + const int bigramListPos = getContentBuffer()->getTailPosition(); + int bigramEntryCount = 0; + // Copy bigram list with GC from original content. + if (!runGCBigramList(originalBigramListPos, originalBigramDictContent, bigramListPos, + terminalIdMap, &bigramEntryCount)) { + AKLOGE("Cannot complete GC for the bigram list. original pos: %d, pos: %d", + originalBigramListPos, bigramListPos); + return false; + } + if (bigramEntryCount == 0) { + // All bigram entries are useless. This terminal does not have a bigram list. + continue; + } + *outBigramEntryCount += bigramEntryCount; + // Set bigram list position to the lookup table. + if (!getUpdatableAddressLookupTable()->set(it->second, bigramListPos)) { + AKLOGE("Cannot set bigram list position. terminal id: %d, pos: %d", + it->second, bigramListPos); + return false; + } + } + return true; +} + +// Returns whether GC for the bigram list was succeeded or not. +bool BigramDictContent::runGCBigramList(const int bigramListPos, + const BigramDictContent *const sourceBigramDictContent, const int toPos, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + int *const outEntrycount) { + bool hasNext = true; + int readingPos = bigramListPos; + int writingPos = toPos; + int lastEntryPos = NOT_A_DICT_POS; + while (hasNext) { + const BigramEntry originalBigramEntry = + sourceBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = originalBigramEntry.hasNext(); + if (originalBigramEntry.getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) { + continue; + } + TerminalPositionLookupTable::TerminalIdMap::const_iterator it = + terminalIdMap->find(originalBigramEntry.getTargetTerminalId()); + if (it == terminalIdMap->end()) { + // Target word has been removed. + continue; + } + lastEntryPos = hasNext ? writingPos : NOT_A_DICT_POS; + const BigramEntry updatedBigramEntry = + originalBigramEntry.updateTargetTerminalIdAndGetEntry(it->second); + if (!writeBigramEntryAndAdvancePosition(&updatedBigramEntry, &writingPos)) { + AKLOGE("Cannot write bigram entry to run GC. pos: %d", writingPos); + return false; + } + *outEntrycount += 1; + } + if (lastEntryPos != NOT_A_DICT_POS) { + // Update has next flag in the last written entry. + const BigramEntry bigramEntry = getBigramEntry(lastEntryPos).updateHasNextAndGetEntry( + false /* hasNext */); + if (!writeBigramEntry(&bigramEntry, lastEntryPos)) { + AKLOGE("Cannot write bigram entry to set hasNext flag after GC. pos: %d", writingPos); + return false; + } + } + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h new file mode 100644 index 000000000..ba2a05209 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h @@ -0,0 +1,102 @@ +/* + * 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_BIGRAM_DICT_CONTENT_H +#define LATINIME_BIGRAM_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" + +namespace latinime { + +class BigramDictContent : public SparseTableDictContent { + public: + BigramDictContent(const char *const dictPath, const bool hasHistoricalInfo, + const bool isUpdatable) + : SparseTableDictContent(dictPath, + Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_FILE_EXTENSION, isUpdatable, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE), + mHasHistoricalInfo(hasHistoricalInfo) {} + + BigramDictContent(const bool hasHistoricalInfo) + : SparseTableDictContent(Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE), + mHasHistoricalInfo(hasHistoricalInfo) {} + + const BigramEntry getBigramEntry(const int bigramEntryPos) const { + int readingPos = bigramEntryPos; + return getBigramEntryAndAdvancePosition(&readingPos); + } + + const BigramEntry getBigramEntryAndAdvancePosition(int *const bigramEntryPos) const; + + // Returns head position of bigram list for a PtNode specified by terminalId. + int getBigramListHeadPos(const int terminalId) const { + const SparseTable *const addressLookupTable = getAddressLookupTable(); + if (!addressLookupTable->contains(terminalId)) { + return NOT_A_DICT_POS; + } + return addressLookupTable->get(terminalId); + } + + bool writeBigramEntry(const BigramEntry *const bigramEntryToWrite, const int entryWritingPos) { + int writingPos = entryWritingPos; + return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos); + } + + bool writeBigramEntryAndAdvancePosition(const BigramEntry *const bigramEntryToWrite, + int *const entryWritingPos); + + bool createNewBigramList(const int terminalId) { + const int bigramListPos = getContentBuffer()->getTailPosition(); + return getUpdatableAddressLookupTable()->set(terminalId, bigramListPos); + } + + bool copyBigramList(const int bigramListPos, const int toPos); + + bool flushToFile(const char *const dictPath) const { + return flush(dictPath, Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::BIGRAM_FILE_EXTENSION); + } + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const BigramDictContent *const originalBigramDictContent, + int *const outBigramEntryCount); + + private: + DISALLOW_COPY_AND_ASSIGN(BigramDictContent); + + int createAndGetBigramFlags(const int probability, const bool hasNext) const { + return (probability & Ver4DictConstants::BIGRAM_PROBABILITY_MASK) + | (hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0); + } + + bool runGCBigramList(const int bigramListPos, + const BigramDictContent *const sourceBigramDictContent, const int toPos, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + int *const outEntryCount); + + bool mHasHistoricalInfo; +}; +} // namespace latinime +#endif /* LATINIME_BIGRAM_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h new file mode 100644 index 000000000..2b0cbd93b --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h @@ -0,0 +1,99 @@ +/* + * 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_BIGRAM_ENTRY_H +#define LATINIME_BIGRAM_ENTRY_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/historical_info.h" + +namespace latinime { + +class BigramEntry { + public: + BigramEntry(const BigramEntry& bigramEntry) + : mHasNext(bigramEntry.mHasNext), mProbability(bigramEntry.mProbability), + mHistoricalInfo(), mTargetTerminalId(bigramEntry.mTargetTerminalId) {} + + // Entry with historical information. + BigramEntry(const bool hasNext, const int probability, const int targetTerminalId) + : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(), + mTargetTerminalId(targetTerminalId) {} + + // Entry with historical information. + BigramEntry(const bool hasNext, const int probability, + const HistoricalInfo *const historicalInfo, const int targetTerminalId) + : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(*historicalInfo), + mTargetTerminalId(targetTerminalId) {} + + const BigramEntry getInvalidatedEntry() const { + return updateTargetTerminalIdAndGetEntry(Ver4DictConstants::NOT_A_TERMINAL_ID); + } + + const BigramEntry updateHasNextAndGetEntry(const bool hasNext) const { + return BigramEntry(hasNext, mProbability, &mHistoricalInfo, mTargetTerminalId); + } + + const BigramEntry updateTargetTerminalIdAndGetEntry(const int newTargetTerminalId) const { + return BigramEntry(mHasNext, mProbability, &mHistoricalInfo, newTargetTerminalId); + } + + const BigramEntry updateProbabilityAndGetEntry(const int probability) const { + return BigramEntry(mHasNext, probability, &mHistoricalInfo, mTargetTerminalId); + } + + const BigramEntry updateHistoricalInfoAndGetEntry( + const HistoricalInfo *const historicalInfo) const { + return BigramEntry(mHasNext, mProbability, historicalInfo, mTargetTerminalId); + } + + bool isValid() const { + return mTargetTerminalId != Ver4DictConstants::NOT_A_TERMINAL_ID; + } + + bool hasNext() const { + return mHasNext; + } + + int getProbability() const { + return mProbability; + } + + bool hasHistoricalInfo() const { + return mHistoricalInfo.isValid(); + } + + const HistoricalInfo *getHistoricalInfo() const { + return &mHistoricalInfo; + } + + int getTargetTerminalId() const { + return mTargetTerminalId; + } + + private: + // Copy constructor is public to use this class as a type of return value. + DISALLOW_DEFAULT_CONSTRUCTOR(BigramEntry); + DISALLOW_ASSIGNMENT_OPERATOR(BigramEntry); + + const bool mHasNext; + const int mProbability; + const HistoricalInfo mHistoricalInfo; + const int mTargetTerminalId; +}; +} // namespace latinime +#endif /* LATINIME_BIGRAM_ENTRY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dict_content.h new file mode 100644 index 000000000..0c2f47073 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dict_content.h @@ -0,0 +1,36 @@ +/* + * 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_DICT_CONTENT_H +#define LATINIME_DICT_CONTENT_H + +#include "defines.h" + +namespace latinime { + +class DictContent { + public: + virtual ~DictContent() {} + virtual bool isValid() const = 0; + + protected: + DictContent() {} + + private: + DISALLOW_COPY_AND_ASSIGN(DictContent); +}; +} // namespace latinime +#endif /* LATINIME_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp new file mode 100644 index 000000000..3b7c70efd --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp @@ -0,0 +1,160 @@ +/* + * 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/structure/v4/content/probability_dict_content.h" + +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +const ProbabilityEntry ProbabilityDictContent::getProbabilityEntry(const int terminalId) const { + if (terminalId < 0 || terminalId >= mSize) { + // This method can be called with invalid terminal id during GC. + return ProbabilityEntry(0 /* flags */, NOT_A_PROBABILITY); + } + const BufferWithExtendableBuffer *const buffer = getBuffer(); + int entryPos = getEntryPos(terminalId); + const int flags = buffer->readUintAndAdvancePosition( + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &entryPos); + const int probability = buffer->readUintAndAdvancePosition( + Ver4DictConstants::PROBABILITY_SIZE, &entryPos); + if (mHasHistoricalInfo) { + const int timestamp = buffer->readUintAndAdvancePosition( + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &entryPos); + const int level = buffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos); + const int count = buffer->readUintAndAdvancePosition( + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos); + const HistoricalInfo historicalInfo(timestamp, level, count); + return ProbabilityEntry(flags, probability, &historicalInfo); + } else { + return ProbabilityEntry(flags, probability); + } +} + +bool ProbabilityDictContent::setProbabilityEntry(const int terminalId, + const ProbabilityEntry *const probabilityEntry) { + if (terminalId < 0) { + return false; + } + const int entryPos = getEntryPos(terminalId); + if (terminalId >= mSize) { + ProbabilityEntry dummyEntry; + // Write new entry. + int writingPos = getBuffer()->getTailPosition(); + while (writingPos <= entryPos) { + // Fulfilling with dummy entries until writingPos. + if (!writeEntry(&dummyEntry, writingPos)) { + AKLOGE("Cannot write dummy entry. pos: %d, mSize: %d", writingPos, mSize); + return false; + } + writingPos += getEntrySize(); + mSize++; + } + } + return writeEntry(probabilityEntry, entryPos); +} + +bool ProbabilityDictContent::flushToFile(const char *const dictPath) const { + if (getEntryPos(mSize) < getBuffer()->getTailPosition()) { + ProbabilityDictContent probabilityDictContentToWrite(mHasHistoricalInfo); + for (int i = 0; i < mSize; ++i) { + const ProbabilityEntry probabilityEntry = getProbabilityEntry(i); + if (!probabilityDictContentToWrite.setProbabilityEntry(i, &probabilityEntry)) { + AKLOGE("Cannot set probability entry in flushToFile. terminalId: %d", i); + return false; + } + } + return probabilityDictContentToWrite.flush(dictPath, + Ver4DictConstants::FREQ_FILE_EXTENSION); + } else { + return flush(dictPath, Ver4DictConstants::FREQ_FILE_EXTENSION); + } +} + +bool ProbabilityDictContent::runGC( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ProbabilityDictContent *const originalProbabilityDictContent) { + mSize = 0; + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const ProbabilityEntry probabilityEntry = + originalProbabilityDictContent->getProbabilityEntry(it->first); + if (!setProbabilityEntry(it->second, &probabilityEntry)) { + AKLOGE("Cannot set probability entry in runGC. terminalId: %d", it->second); + return false; + } + mSize++; + } + return true; +} + +int ProbabilityDictContent::getEntrySize() const { + if (mHasHistoricalInfo) { + return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE + + Ver4DictConstants::PROBABILITY_SIZE + + Ver4DictConstants::TIME_STAMP_FIELD_SIZE + + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE; + } else { + return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE + + Ver4DictConstants::PROBABILITY_SIZE; + } +} + +int ProbabilityDictContent::getEntryPos(const int terminalId) const { + return terminalId * getEntrySize(); +} + +bool ProbabilityDictContent::writeEntry(const ProbabilityEntry *const probabilityEntry, + const int entryPos) { + BufferWithExtendableBuffer *const bufferToWrite = getWritableBuffer(); + int writingPos = entryPos; + if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getFlags(), + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &writingPos)) { + AKLOGE("Cannot write flags in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getProbability(), + Ver4DictConstants::PROBABILITY_SIZE, &writingPos)) { + AKLOGE("Cannot write probability in probability dict content. pos: %d", writingPos); + return false; + } + if (mHasHistoricalInfo) { + const HistoricalInfo *const historicalInfo = probabilityEntry->getHistoricalInfo(); + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write timestamp in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getLevel(), + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write level in probability dict content. pos: %d", writingPos); + return false; + } + if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getCount(), + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &writingPos)) { + AKLOGE("Cannot write count in probability dict content. pos: %d", writingPos); + return false; + } + } + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h new file mode 100644 index 000000000..b065bc954 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h @@ -0,0 +1,63 @@ +/* + * 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_PROBABILITY_DICT_CONTENT_H +#define LATINIME_PROBABILITY_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +class ProbabilityEntry; + +class ProbabilityDictContent : public SingleDictContent { + public: + ProbabilityDictContent(const char *const dictPath, const bool hasHistoricalInfo, + const bool isUpdatable) + : SingleDictContent(dictPath, Ver4DictConstants::FREQ_FILE_EXTENSION, isUpdatable), + mHasHistoricalInfo(hasHistoricalInfo), + mSize(getBuffer()->getTailPosition() / getEntrySize()) {} + + ProbabilityDictContent(const bool hasHistoricalInfo) + : mHasHistoricalInfo(hasHistoricalInfo), mSize(0) {} + + const ProbabilityEntry getProbabilityEntry(const int terminalId) const; + + bool setProbabilityEntry(const int terminalId, const ProbabilityEntry *const probabilityEntry); + + bool flushToFile(const char *const dictPath) const; + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ProbabilityDictContent *const originalProbabilityDictContent); + + private: + DISALLOW_COPY_AND_ASSIGN(ProbabilityDictContent); + + int getEntrySize() const; + + int getEntryPos(const int terminalId) const; + + bool writeEntry(const ProbabilityEntry *const probabilityEntry, const int entryPos); + + bool mHasHistoricalInfo; + int mSize; +}; +} // namespace latinime +#endif /* LATINIME_PROBABILITY_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h new file mode 100644 index 000000000..36ba82be1 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_PROBABILITY_ENTRY_H +#define LATINIME_PROBABILITY_ENTRY_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/historical_info.h" + +namespace latinime { + +class ProbabilityEntry { + public: + ProbabilityEntry(const ProbabilityEntry &probabilityEntry) + : mFlags(probabilityEntry.mFlags), mProbability(probabilityEntry.mProbability), + mHistoricalInfo(probabilityEntry.mHistoricalInfo) {} + + // Dummy entry + ProbabilityEntry() + : mFlags(0), mProbability(NOT_A_PROBABILITY), mHistoricalInfo() {} + + // Entry without historical information + ProbabilityEntry(const int flags, const int probability) + : mFlags(flags), mProbability(probability), mHistoricalInfo() {} + + // Entry with historical information. + ProbabilityEntry(const int flags, const int probability, + const HistoricalInfo *const historicalInfo) + : mFlags(flags), mProbability(probability), mHistoricalInfo(*historicalInfo) {} + + const ProbabilityEntry createEntryWithUpdatedProbability(const int probability) const { + return ProbabilityEntry(mFlags, probability, &mHistoricalInfo); + } + + const ProbabilityEntry createEntryWithUpdatedHistoricalInfo( + const HistoricalInfo *const historicalInfo) const { + return ProbabilityEntry(mFlags, mProbability, historicalInfo); + } + + bool hasHistoricalInfo() const { + return mHistoricalInfo.isValid(); + } + + int getFlags() const { + return mFlags; + } + + int getProbability() const { + return mProbability; + } + + const HistoricalInfo *getHistoricalInfo() const { + return &mHistoricalInfo; + } + + private: + // Copy constructor is public to use this class as a type of return value. + DISALLOW_ASSIGNMENT_OPERATOR(ProbabilityEntry); + + const int mFlags; + const int mProbability; + const HistoricalInfo mHistoricalInfo; +}; +} // namespace latinime +#endif /* LATINIME_PROBABILITY_ENTRY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp new file mode 100644 index 000000000..29972a4e8 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.cpp @@ -0,0 +1,175 @@ +/* + * 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/structure/v4/content/shortcut_dict_content.h" + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +void ShortcutDictContent::getShortcutEntryAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoint, int *const outCodePointCount, int *const outProbability, + bool *const outhasNext, int *const shortcutEntryPos) const { + const BufferWithExtendableBuffer *const shortcutListBuffer = getContentBuffer(); + const int shortcutFlags = shortcutListBuffer->readUintAndAdvancePosition( + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); + if (outProbability) { + *outProbability = shortcutFlags & Ver4DictConstants::SHORTCUT_PROBABILITY_MASK; + } + if (outhasNext) { + *outhasNext = shortcutFlags & Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK; + } + if (outCodePoint && outCodePointCount) { + shortcutListBuffer->readCodePointsAndAdvancePosition( + maxCodePointCount, outCodePoint, outCodePointCount, shortcutEntryPos); + } +} + +int ShortcutDictContent::getShortcutListHeadPos(const int terminalId) const { + const SparseTable *const addressLookupTable = getAddressLookupTable(); + if (!addressLookupTable->contains(terminalId)) { + return NOT_A_DICT_POS; + } + return addressLookupTable->get(terminalId); +} + +bool ShortcutDictContent::flushToFile(const char *const dictPath) const { + return flush(dictPath, Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_FILE_EXTENSION); +} + +bool ShortcutDictContent::runGC( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ShortcutDictContent *const originalShortcutDictContent) { + for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); + it != terminalIdMap->end(); ++it) { + const int originalShortcutListPos = + originalShortcutDictContent->getShortcutListHeadPos(it->first); + if (originalShortcutListPos == NOT_A_DICT_POS) { + continue; + } + const int shortcutListPos = getContentBuffer()->getTailPosition(); + // Copy shortcut list from original content. + if (!copyShortcutListFromDictContent(originalShortcutListPos, originalShortcutDictContent, + shortcutListPos)) { + AKLOGE("Cannot copy shortcut list during GC. original pos: %d, pos: %d", + originalShortcutListPos, shortcutListPos); + return false; + } + // Set shortcut list position to the lookup table. + if (!getUpdatableAddressLookupTable()->set(it->second, shortcutListPos)) { + AKLOGE("Cannot set shortcut list position. terminal id: %d, pos: %d", + it->second, shortcutListPos); + return false; + } + } + return true; +} + +bool ShortcutDictContent::createNewShortcutList(const int terminalId) { + const int shortcutListListPos = getContentBuffer()->getTailPosition(); + return getUpdatableAddressLookupTable()->set(terminalId, shortcutListListPos); +} + +bool ShortcutDictContent::copyShortcutList(const int shortcutListPos, const int toPos) { + return copyShortcutListFromDictContent(shortcutListPos, this, toPos); +} + +bool ShortcutDictContent::copyShortcutListFromDictContent(const int shortcutListPos, + const ShortcutDictContent *const sourceShortcutDictContent, const int toPos) { + bool hasNext = true; + int readingPos = shortcutListPos; + int writingPos = toPos; + int codePoints[MAX_WORD_LENGTH]; + while (hasNext) { + int probability = 0; + int codePointCount = 0; + sourceShortcutDictContent->getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, + codePoints, &codePointCount, &probability, &hasNext, &readingPos); + if (!writeShortcutEntryAndAdvancePosition(codePoints, codePointCount, probability, + hasNext, &writingPos)) { + AKLOGE("Cannot write shortcut entry to copy. pos: %d", writingPos); + return false; + } + } + return true; +} + +bool ShortcutDictContent::setProbability(const int probability, const int shortcutEntryPos) { + BufferWithExtendableBuffer *const shortcutListBuffer = getWritableContentBuffer(); + const int shortcutFlags = shortcutListBuffer->readUint( + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); + const bool hasNext = shortcutFlags & Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK; + const int shortcutFlagsToWrite = createAndGetShortcutFlags(probability, hasNext); + return shortcutListBuffer->writeUint(shortcutFlagsToWrite, + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos); +} + +bool ShortcutDictContent::writeShortcutEntryAndAdvancePosition(const int *const codePoint, + const int codePointCount, const int probability, const bool hasNext, + int *const shortcutEntryPos) { + BufferWithExtendableBuffer *const shortcutListBuffer = getWritableContentBuffer(); + const int shortcutFlags = createAndGetShortcutFlags(probability, hasNext); + if (!shortcutListBuffer->writeUintAndAdvancePosition(shortcutFlags, + Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE, shortcutEntryPos)) { + AKLOGE("Cannot write shortcut flags. flags; %x, pos: %d", shortcutFlags, *shortcutEntryPos); + return false; + } + if (!shortcutListBuffer->writeCodePointsAndAdvancePosition(codePoint, codePointCount, + true /* writesTerminator */, shortcutEntryPos)) { + AKLOGE("Cannot write shortcut target code points. pos: %d", *shortcutEntryPos); + return false; + } + return true; +} + +// Find a shortcut entry that has specified target and return its position. +int ShortcutDictContent::findShortcutEntryAndGetPos(const int shortcutListPos, + const int *const targetCodePointsToFind, const int codePointCount) const { + bool hasNext = true; + int readingPos = shortcutListPos; + int targetCodePoints[MAX_WORD_LENGTH]; + while (hasNext) { + const int entryPos = readingPos; + int probability = 0; + int targetCodePointCount = 0; + getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, targetCodePoints, &targetCodePointCount, + &probability, &hasNext, &readingPos); + if (targetCodePointCount != codePointCount) { + continue; + } + bool matched = true; + for (int i = 0; i < codePointCount; ++i) { + if (targetCodePointsToFind[i] != targetCodePoints[i]) { + matched = false; + break; + } + } + if (matched) { + return entryPos; + } + } + return NOT_A_DICT_POS; +} + +int ShortcutDictContent::createAndGetShortcutFlags(const int probability, + const bool hasNext) const { + return (probability & Ver4DictConstants::SHORTCUT_PROBABILITY_MASK) + | (hasNext ? Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK : 0); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h new file mode 100644 index 000000000..eaafc27bc --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_SHORTCUT_DICT_CONTENT_H +#define LATINIME_SHORTCUT_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" + +namespace latinime { + +class ShortcutDictContent : public SparseTableDictContent { + public: + ShortcutDictContent(const char *const dictPath, const bool isUpdatable) + : SparseTableDictContent(dictPath, + Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION, + Ver4DictConstants::SHORTCUT_FILE_EXTENSION, isUpdatable, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {} + + ShortcutDictContent() + : SparseTableDictContent(Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, + Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE) {} + + void getShortcutEntry(const int maxCodePointCount, int *const outCodePoint, + int *const outCodePointCount, int *const outProbability, bool *const outhasNext, + const int shortcutEntryPos) { + int readingPos = shortcutEntryPos; + return getShortcutEntryAndAdvancePosition(maxCodePointCount, outCodePoint, + outCodePointCount, outProbability, outhasNext, &readingPos); + } + + void getShortcutEntryAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoint, int *const outCodePointCount, int *const outProbability, + bool *const outhasNext, int *const shortcutEntryPos) const; + + // Returns head position of shortcut list for a PtNode specified by terminalId. + int getShortcutListHeadPos(const int terminalId) const; + + bool flushToFile(const char *const dictPath) const; + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const ShortcutDictContent *const originalShortcutDictContent); + + bool createNewShortcutList(const int terminalId); + + bool copyShortcutList(const int shortcutListPos, const int toPos); + + bool setProbability(const int probability, const int shortcutEntryPos); + + bool writeShortcutEntry(const int *const codePoint, const int codePointCount, + const int probability, const bool hasNext, const int shortcutEntryPos) { + int writingPos = shortcutEntryPos; + return writeShortcutEntryAndAdvancePosition(codePoint, codePointCount, probability, + hasNext, &writingPos); + } + + bool writeShortcutEntryAndAdvancePosition(const int *const codePoint, + const int codePointCount, const int probability, const bool hasNext, + int *const shortcutEntryPos); + + int findShortcutEntryAndGetPos(const int shortcutListPos, + const int *const targetCodePointsToFind, const int codePointCount) const; + + private: + DISALLOW_COPY_AND_ASSIGN(ShortcutDictContent); + + bool copyShortcutListFromDictContent(const int shortcutListPos, + const ShortcutDictContent *const sourceShortcutDictContent, const int toPos); + + int createAndGetShortcutFlags(const int probability, const bool hasNext) const; +}; +} // namespace latinime +#endif /* LATINIME_SHORTCUT_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h new file mode 100644 index 000000000..9064b7e72 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h @@ -0,0 +1,75 @@ +/* + * 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_SINGLE_DICT_CONTENT_H +#define LATINIME_SINGLE_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +namespace latinime { + +class SingleDictContent : public DictContent { + public: + SingleDictContent(const char *const dictPath, const char *const contentFileName, + const bool isUpdatable) + : mMmappedBuffer(MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), + mExpandableContentBuffer(mMmappedBuffer.get() ? mMmappedBuffer.get()->getBuffer() : 0, + mMmappedBuffer.get() ? mMmappedBuffer.get()->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mIsValid(mMmappedBuffer.get() != 0) {} + + SingleDictContent() + : mMmappedBuffer(0), mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mIsValid(true) {} + + virtual ~SingleDictContent() {} + + virtual bool isValid() const { + return mIsValid; + } + + bool isNearSizeLimit() const { + return mExpandableContentBuffer.isNearSizeLimit(); + } + + protected: + BufferWithExtendableBuffer *getWritableBuffer() { + return &mExpandableContentBuffer; + } + + const BufferWithExtendableBuffer *getBuffer() const { + return &mExpandableContentBuffer; + } + + bool flush(const char *const dictPath, const char *const contentFileNameSuffix) const { + return DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + contentFileNameSuffix, &mExpandableContentBuffer); + } + + private: + DISALLOW_COPY_AND_ASSIGN(SingleDictContent); + + const MmappedBuffer::MmappedBufferPtr mMmappedBuffer; + BufferWithExtendableBuffer mExpandableContentBuffer; + const bool mIsValid; +}; +} // namespace latinime +#endif /* LATINIME_SINGLE_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp new file mode 100644 index 000000000..63c6ea3a4 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h" + +namespace latinime { + +bool SparseTableDictContent::flush(const char *const dictPath, + const char *const lookupTableFileNameSuffix, const char *const addressTableFileNameSuffix, + const char *const contentFileNameSuffix) const { + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, lookupTableFileNameSuffix, + &mExpandableLookupTableBuffer)){ + return false; + } + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, addressTableFileNameSuffix, + &mExpandableAddressTableBuffer)) { + return false; + } + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, contentFileNameSuffix, + &mExpandableContentBuffer)) { + return false; + } + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h new file mode 100644 index 000000000..a82e3f50a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_SPARSE_TABLE_DICT_CONTENT_H +#define LATINIME_SPARSE_TABLE_DICT_CONTENT_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" +#include "suggest/policyimpl/dictionary/utils/sparse_table.h" + +namespace latinime { + +// TODO: Support multiple contents. +class SparseTableDictContent : public DictContent { + public: + AK_FORCE_INLINE SparseTableDictContent(const char *const dictPath, + const char *const lookupTableFileName, const char *const addressTableFileName, + const char *const contentFileName, const bool isUpdatable, + const int sparseTableBlockSize, const int sparseTableDataSize) + : mLookupTableBuffer( + MmappedBuffer::openBuffer(dictPath, lookupTableFileName, isUpdatable)), + mAddressTableBuffer( + MmappedBuffer::openBuffer(dictPath, addressTableFileName, isUpdatable)), + mContentBuffer(MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), + mExpandableLookupTableBuffer( + mLookupTableBuffer.get() ? mLookupTableBuffer.get()->getBuffer() : 0, + mLookupTableBuffer.get() ? mLookupTableBuffer.get()->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableAddressTableBuffer( + mAddressTableBuffer.get() ? mAddressTableBuffer.get()->getBuffer() : 0, + mAddressTableBuffer.get() ? mAddressTableBuffer.get()->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableContentBuffer(mContentBuffer.get() ? mContentBuffer.get()->getBuffer() : 0, + mContentBuffer.get() ? mContentBuffer.get()->getBufferSize() : 0, + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, + sparseTableBlockSize, sparseTableDataSize), + mIsValid(mLookupTableBuffer.get() != 0 && mAddressTableBuffer.get() != 0 + && mContentBuffer.get() != 0) {} + + SparseTableDictContent(const int sparseTableBlockSize, const int sparseTableDataSize) + : mLookupTableBuffer(0), mAddressTableBuffer(0), mContentBuffer(0), + mExpandableLookupTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableAddressTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, + sparseTableBlockSize, sparseTableDataSize), mIsValid(true) {} + + virtual ~SparseTableDictContent() {} + + virtual bool isValid() const { + return mIsValid; + } + + bool isNearSizeLimit() const { + return mExpandableLookupTableBuffer.isNearSizeLimit() + || mExpandableAddressTableBuffer.isNearSizeLimit() + || mExpandableContentBuffer.isNearSizeLimit(); + } + + protected: + SparseTable *getUpdatableAddressLookupTable() { + return &mAddressLookupTable; + } + + const SparseTable *getAddressLookupTable() const { + return &mAddressLookupTable; + } + + BufferWithExtendableBuffer *getWritableContentBuffer() { + return &mExpandableContentBuffer; + } + + const BufferWithExtendableBuffer *getContentBuffer() const { + return &mExpandableContentBuffer; + } + + bool flush(const char *const dictDirPath, const char *const lookupTableFileName, + const char *const addressTableFileName, const char *const contentFileName) const; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SparseTableDictContent); + + const MmappedBuffer::MmappedBufferPtr mLookupTableBuffer; + const MmappedBuffer::MmappedBufferPtr mAddressTableBuffer; + const MmappedBuffer::MmappedBufferPtr mContentBuffer; + BufferWithExtendableBuffer mExpandableLookupTableBuffer; + BufferWithExtendableBuffer mExpandableAddressTableBuffer; + BufferWithExtendableBuffer mExpandableContentBuffer; + SparseTable mAddressLookupTable; + const bool mIsValid; +}; +} // namespace latinime +#endif /* LATINIME_SPARSE_TABLE_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp new file mode 100644 index 000000000..0b17a009d --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.cpp @@ -0,0 +1,100 @@ +/* + * 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/structure/v4/content/terminal_position_lookup_table.h" + +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +int TerminalPositionLookupTable::getTerminalPtNodePosition(const int terminalId) const { + if (terminalId < 0 || terminalId >= mSize) { + return NOT_A_DICT_POS; + } + const int terminalPos = getBuffer()->readUint( + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(terminalId)); + return (terminalPos == Ver4DictConstants::NOT_A_TERMINAL_ADDRESS) ? + NOT_A_DICT_POS : terminalPos; +} + +bool TerminalPositionLookupTable::setTerminalPtNodePosition( + const int terminalId, const int terminalPtNodePos) { + if (terminalId < 0) { + return NOT_A_DICT_POS; + } + while (terminalId >= mSize) { + // Write new entry. + if (!getWritableBuffer()->writeUint(Ver4DictConstants::NOT_A_TERMINAL_ADDRESS, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(mSize))) { + return false; + } + mSize++; + } + const int terminalPos = (terminalPtNodePos != NOT_A_DICT_POS) ? + terminalPtNodePos : Ver4DictConstants::NOT_A_TERMINAL_ADDRESS; + return getWritableBuffer()->writeUint(terminalPos, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(terminalId)); +} + +bool TerminalPositionLookupTable::flushToFile(const char *const dictPath) const { + // If the used buffer size is smaller than the actual buffer size, regenerate the lookup + // table and write the new table to the file. + if (getEntryPos(mSize) < getBuffer()->getTailPosition()) { + TerminalPositionLookupTable lookupTableToWrite; + for (int i = 0; i < mSize; ++i) { + const int terminalPtNodePosition = getTerminalPtNodePosition(i); + if (!lookupTableToWrite.setTerminalPtNodePosition(i, terminalPtNodePosition)) { + AKLOGE("Cannot set terminal position to lookupTableToWrite." + " terminalId: %d, position: %d", i, terminalPtNodePosition); + return false; + } + } + return lookupTableToWrite.flush(dictPath, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } else { + // We can simply use this lookup table because the buffer size has not been + // changed. + return flush(dictPath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } +} + +bool TerminalPositionLookupTable::runGCTerminalIds(TerminalIdMap *const terminalIdMap) { + int removedEntryCount = 0; + int nextNewTerminalId = 0; + for (int i = 0; i < mSize; ++i) { + const int terminalPos = getBuffer()->readUint( + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, getEntryPos(i)); + if (terminalPos == Ver4DictConstants::NOT_A_TERMINAL_ADDRESS) { + // This entry is a garbage. + removedEntryCount++; + } else { + // Give a new terminal id to the entry. + if (!getWritableBuffer()->writeUint(terminalPos, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE, + getEntryPos(nextNewTerminalId))) { + return false; + } + // Memorize the mapping to the old terminal id to the new terminal id. + terminalIdMap->insert(TerminalIdMap::value_type(i, nextNewTerminalId)); + nextNewTerminalId++; + } + } + mSize = nextNewTerminalId; + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h new file mode 100644 index 000000000..f73e22754 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h @@ -0,0 +1,61 @@ +/* + * 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_TERMINAL_POSITION_LOOKUP_TABLE_H +#define LATINIME_TERMINAL_POSITION_LOOKUP_TABLE_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "utils/hash_map_compat.h" + +namespace latinime { + +class TerminalPositionLookupTable : public SingleDictContent { + public: + typedef hash_map_compat<int, int> TerminalIdMap; + + TerminalPositionLookupTable(const char *const dictPath, const bool isUpdatable) + : SingleDictContent(dictPath, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION, isUpdatable), + mSize(getBuffer()->getTailPosition() + / Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE) {} + + TerminalPositionLookupTable() : mSize(0) {} + + int getTerminalPtNodePosition(const int terminalId) const; + + bool setTerminalPtNodePosition(const int terminalId, const int terminalPtNodePos); + + int getNextTerminalId() const { + return mSize; + } + + bool flushToFile(const char *const dictPath) const; + + bool runGCTerminalIds(TerminalIdMap *const terminalIdMap); + + private: + DISALLOW_COPY_AND_ASSIGN(TerminalPositionLookupTable); + + int getEntryPos(const int terminalId) const { + return terminalId * Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE; + } + + int mSize; +}; +} // namespace latinime +#endif // LATINIME_TERMINAL_POSITION_LOOKUP_TABLE_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp new file mode 100644 index 000000000..918c02ba2 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp @@ -0,0 +1,138 @@ +/* + * 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/structure/v4/ver4_dict_buffers.h" + +#include <cerrno> +#include <cstring> +#include <sys/stat.h> +#include <sys/types.h> + +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" + +namespace latinime { + +/* static */ Ver4DictBuffers::Ver4DictBuffersPtr Ver4DictBuffers::openVer4DictBuffers( + const char *const dictPath, const MmappedBuffer::MmappedBufferPtr &headerBuffer) { + const bool isUpdatable = headerBuffer.get() ? headerBuffer.get()->isUpdatable() : false; + // TODO: take only dictDirPath, and open both header and trie files in the constructor below + return Ver4DictBuffersPtr(new Ver4DictBuffers(dictPath, headerBuffer, isUpdatable)); +} + +bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const { + // Create temporary directory. + const int tmpDirPathBufSize = FileUtils::getFilePathWithSuffixBufSize(dictDirPath, + DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); + char tmpDirPath[tmpDirPathBufSize]; + FileUtils::getFilePathWithSuffix(dictDirPath, + DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE, tmpDirPathBufSize, + tmpDirPath); + if (FileUtils::existsDir(tmpDirPath)) { + if (!FileUtils::removeDirAndFiles(tmpDirPath)) { + AKLOGE("Existing directory %s cannot be removed.", tmpDirPath); + ASSERT(false); + return false; + } + } + if (mkdir(tmpDirPath, S_IRWXU) == -1) { + AKLOGE("Cannot create directory: %s. errno: %d.", tmpDirPath, errno); + return false; + } + // Get dictionary base path. + const int dictNameBufSize = strlen(dictDirPath) + 1 /* terminator */; + char dictName[dictNameBufSize]; + FileUtils::getBasename(dictDirPath, dictNameBufSize, dictName); + const int dictPathBufSize = FileUtils::getFilePathBufSize(tmpDirPath, dictName); + char dictPath[dictPathBufSize]; + FileUtils::getFilePath(tmpDirPath, dictName, dictPathBufSize, dictPath); + + // Write header file. + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + Ver4DictConstants::HEADER_FILE_EXTENSION, headerBuffer)) { + AKLOGE("Dictionary header file %s%s cannot be written.", tmpDirPath, + Ver4DictConstants::HEADER_FILE_EXTENSION); + return false; + } + // Write trie file. + if (!DictFileWritingUtils::flushBufferToFileWithSuffix(dictPath, + Ver4DictConstants::TRIE_FILE_EXTENSION, &mExpandableTrieBuffer)) { + AKLOGE("Dictionary trie file %s%s cannot be written.", tmpDirPath, + Ver4DictConstants::TRIE_FILE_EXTENSION); + return false; + } + // Write dictionary contents. + if (!mTerminalPositionLookupTable.flushToFile(dictPath)) { + AKLOGE("Terminal position lookup table cannot be written. %s", tmpDirPath); + return false; + } + if (!mProbabilityDictContent.flushToFile(dictPath)) { + AKLOGE("Probability dict content cannot be written. %s", tmpDirPath); + return false; + } + if (!mBigramDictContent.flushToFile(dictPath)) { + AKLOGE("Bigram dict content cannot be written. %s", tmpDirPath); + return false; + } + if (!mShortcutDictContent.flushToFile(dictPath)) { + AKLOGE("Shortcut dict content cannot be written. %s", tmpDirPath); + return false; + } + // Remove existing dictionary. + if (!FileUtils::removeDirAndFiles(dictDirPath)) { + AKLOGE("Existing directory %s cannot be removed.", dictDirPath); + ASSERT(false); + return false; + } + // Rename temporary directory. + if (rename(tmpDirPath, dictDirPath) != 0) { + AKLOGE("%s cannot be renamed to %s", tmpDirPath, dictDirPath); + ASSERT(false); + return false; + } + return true; +} + +Ver4DictBuffers::Ver4DictBuffers(const char *const dictPath, + const MmappedBuffer::MmappedBufferPtr &headerBuffer, const bool isUpdatable) + : mHeaderBuffer(headerBuffer), + mDictBuffer(MmappedBuffer::openBuffer(dictPath, + Ver4DictConstants::TRIE_FILE_EXTENSION, isUpdatable)), + mHeaderPolicy(headerBuffer.get()->getBuffer(), FormatUtils::VERSION_4), + mExpandableHeaderBuffer(headerBuffer.get()->getBuffer(), mHeaderPolicy.getSize(), + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mExpandableTrieBuffer(mDictBuffer.get()->getBuffer(), + mDictBuffer.get()->getBufferSize(), + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), + mTerminalPositionLookupTable(dictPath, isUpdatable), + mProbabilityDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), + isUpdatable), + mBigramDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), + isUpdatable), + mShortcutDictContent(dictPath, isUpdatable), + mIsUpdatable(isUpdatable) {} + +Ver4DictBuffers::Ver4DictBuffers(const HeaderPolicy *const headerPolicy) + : mHeaderBuffer(0), mDictBuffer(0), mHeaderPolicy(), + mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mExpandableTrieBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), + mTerminalPositionLookupTable(), + mProbabilityDictContent(headerPolicy->hasHistoricalInfoOfWords()), + mBigramDictContent(headerPolicy->hasHistoricalInfoOfWords()), mShortcutDictContent(), + mIsUpdatable(true) {} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h new file mode 100644 index 000000000..a0c219e4d --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h @@ -0,0 +1,137 @@ +/* + * 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_VER4_DICT_BUFFER_H +#define LATINIME_VER4_DICT_BUFFER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +namespace latinime { + +class Ver4DictBuffers { + public: + typedef ExclusiveOwnershipPointer<Ver4DictBuffers> Ver4DictBuffersPtr; + + static Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath, + const MmappedBuffer::MmappedBufferPtr &headerBuffer); + + static AK_FORCE_INLINE Ver4DictBuffersPtr createVer4DictBuffers( + const HeaderPolicy *const headerPolicy) { + return Ver4DictBuffersPtr(new Ver4DictBuffers(headerPolicy)); + } + + AK_FORCE_INLINE bool isValid() const { + return mDictBuffer.get() != 0 && mHeaderPolicy.isValid() + && mProbabilityDictContent.isValid() && mTerminalPositionLookupTable.isValid() + && mBigramDictContent.isValid() && mShortcutDictContent.isValid(); + } + + AK_FORCE_INLINE bool isNearSizeLimit() const { + return mExpandableTrieBuffer.isNearSizeLimit() + || mTerminalPositionLookupTable.isNearSizeLimit() + || mProbabilityDictContent.isNearSizeLimit() + || mBigramDictContent.isNearSizeLimit() + || mShortcutDictContent.isNearSizeLimit(); + } + + AK_FORCE_INLINE const HeaderPolicy *getHeaderPolicy() const { + return &mHeaderPolicy; + } + + AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableHeaderBuffer() { + return &mExpandableHeaderBuffer; + } + + AK_FORCE_INLINE BufferWithExtendableBuffer *getWritableTrieBuffer() { + return &mExpandableTrieBuffer; + } + + AK_FORCE_INLINE const BufferWithExtendableBuffer *getTrieBuffer() const { + return &mExpandableTrieBuffer; + } + + AK_FORCE_INLINE TerminalPositionLookupTable *getMutableTerminalPositionLookupTable() { + return &mTerminalPositionLookupTable; + } + + AK_FORCE_INLINE const TerminalPositionLookupTable *getTerminalPositionLookupTable() const { + return &mTerminalPositionLookupTable; + } + + AK_FORCE_INLINE ProbabilityDictContent *getMutableProbabilityDictContent() { + return &mProbabilityDictContent; + } + + AK_FORCE_INLINE const ProbabilityDictContent *getProbabilityDictContent() const { + return &mProbabilityDictContent; + } + + AK_FORCE_INLINE BigramDictContent *getMutableBigramDictContent() { + return &mBigramDictContent; + } + + AK_FORCE_INLINE const BigramDictContent *getBigramDictContent() const { + return &mBigramDictContent; + } + + AK_FORCE_INLINE ShortcutDictContent *getMutableShortcutDictContent() { + return &mShortcutDictContent; + } + + AK_FORCE_INLINE const ShortcutDictContent *getShortcutDictContent() const { + return &mShortcutDictContent; + } + + AK_FORCE_INLINE bool isUpdatable() const { + return mIsUpdatable; + } + + bool flush(const char *const dictDirPath) const { + return flushHeaderAndDictBuffers(dictDirPath, &mExpandableHeaderBuffer); + } + + bool flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const; + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers); + + Ver4DictBuffers(const char *const dictDirPath, + const MmappedBuffer::MmappedBufferPtr &headerBuffer, const bool isUpdatable); + + Ver4DictBuffers(const HeaderPolicy *const headerPolicy); + + const MmappedBuffer::MmappedBufferPtr mHeaderBuffer; + const MmappedBuffer::MmappedBufferPtr mDictBuffer; + const HeaderPolicy mHeaderPolicy; + BufferWithExtendableBuffer mExpandableHeaderBuffer; + BufferWithExtendableBuffer mExpandableTrieBuffer; + TerminalPositionLookupTable mTerminalPositionLookupTable; + ProbabilityDictContent mProbabilityDictContent; + BigramDictContent mBigramDictContent; + ShortcutDictContent mShortcutDictContent; + const int mIsUpdatable; +}; +} // namespace latinime +#endif /* LATINIME_VER4_DICT_BUFFER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp new file mode 100644 index 000000000..34fecc25f --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp @@ -0,0 +1,71 @@ +/* + * 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/structure/v4/ver4_dict_constants.h" + +namespace latinime { + +// These values MUST match the definitions in FormatSpec.java. +const char *const Ver4DictConstants::TRIE_FILE_EXTENSION = ".trie"; +const char *const Ver4DictConstants::HEADER_FILE_EXTENSION = ".header"; +const char *const Ver4DictConstants::FREQ_FILE_EXTENSION = ".freq"; +// tat = Terminal Address Table +const char *const Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat"; +const char *const Ver4DictConstants::BIGRAM_FILE_EXTENSION = ".bigram_freq"; +const char *const Ver4DictConstants::BIGRAM_LOOKUP_TABLE_FILE_EXTENSION = ".bigram_lookup"; +const char *const Ver4DictConstants::BIGRAM_CONTENT_TABLE_FILE_EXTENSION = ".bigram_index_freq"; +const char *const Ver4DictConstants::SHORTCUT_FILE_EXTENSION = ".shortcut_shortcut"; +const char *const Ver4DictConstants::SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION = ".shortcut_lookup"; +const char *const Ver4DictConstants::SHORTCUT_CONTENT_TABLE_FILE_EXTENSION = + ".shortcut_index_shortcut"; + +// Version 4 dictionary size is implicitly limited to 8MB due to 3-byte offsets. +// TODO: Make MAX_DICTIONARY_SIZE 8MB. +const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024; +// Extended region size, which is not GCed region size in dict file + additional buffer size, is +// limited to 1MB to prevent from inefficient traversing. +const int Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE = 1 * 1024 * 1024; + +const int Ver4DictConstants::NOT_A_TERMINAL_ID = -1; +const int Ver4DictConstants::PROBABILITY_SIZE = 1; +const int Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE = 1; +const int Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3; +const int Ver4DictConstants::NOT_A_TERMINAL_ADDRESS = 0; +const int Ver4DictConstants::TERMINAL_ID_FIELD_SIZE = 4; +const int Ver4DictConstants::TIME_STAMP_FIELD_SIZE = 4; +const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1; +const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1; + +const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4; +const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE = 4; +const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 16; +const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE = 4; + +const int Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE = 3; +// Unsigned int max value of BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE-byte is used for representing +// invalid terminal ID in bigram lists. +const int Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID = + (1 << (BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE * 8)) - 1; +const int Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE = 1; +const int Ver4DictConstants::BIGRAM_PROBABILITY_MASK = 0x0F; +const int Ver4DictConstants::BIGRAM_HAS_NEXT_MASK = 0x80; +const int Ver4DictConstants::BIGRAM_LARGE_PROBABILITY_FIELD_SIZE = 1; + +const int Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE = 1; +const int Ver4DictConstants::SHORTCUT_PROBABILITY_MASK = 0x0F; +const int Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK = 0x80; + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h new file mode 100644 index 000000000..d6d22c5c1 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_VER4_DICT_CONSTANTS_H +#define LATINIME_VER4_DICT_CONSTANTS_H + +#include "defines.h" + +namespace latinime { + +// TODO: Create PtConstants under the pt_common and move some constant values there. +// Note that there are corresponding definitions in FormatSpec.java. +class Ver4DictConstants { + public: + static const char *const TRIE_FILE_EXTENSION; + static const char *const HEADER_FILE_EXTENSION; + static const char *const FREQ_FILE_EXTENSION; + static const char *const TERMINAL_ADDRESS_TABLE_FILE_EXTENSION; + static const char *const BIGRAM_FILE_EXTENSION; + static const char *const BIGRAM_LOOKUP_TABLE_FILE_EXTENSION; + static const char *const BIGRAM_CONTENT_TABLE_FILE_EXTENSION; + static const char *const SHORTCUT_FILE_EXTENSION; + static const char *const SHORTCUT_LOOKUP_TABLE_FILE_EXTENSION; + static const char *const SHORTCUT_CONTENT_TABLE_FILE_EXTENSION; + + static const int MAX_DICTIONARY_SIZE; + static const int MAX_DICT_EXTENDED_REGION_SIZE; + + static const int NOT_A_TERMINAL_ID; + static const int PROBABILITY_SIZE; + static const int FLAGS_IN_PROBABILITY_FILE_SIZE; + static const int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE; + static const int NOT_A_TERMINAL_ADDRESS; + static const int TERMINAL_ID_FIELD_SIZE; + static const int TIME_STAMP_FIELD_SIZE; + static const int WORD_LEVEL_FIELD_SIZE; + static const int WORD_COUNT_FIELD_SIZE; + + static const int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE; + static const int BIGRAM_ADDRESS_TABLE_DATA_SIZE; + static const int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE; + static const int SHORTCUT_ADDRESS_TABLE_DATA_SIZE; + + static const int BIGRAM_FLAGS_FIELD_SIZE; + static const int BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE; + static const int INVALID_BIGRAM_TARGET_TERMINAL_ID; + static const int BIGRAM_PROBABILITY_MASK; + static const int BIGRAM_HAS_NEXT_MASK; + // Used when bigram list has time stamp. + static const int BIGRAM_LARGE_PROBABILITY_FIELD_SIZE; + + static const int SHORTCUT_FLAGS_FIELD_SIZE; + static const int SHORTCUT_PROBABILITY_MASK; + static const int SHORTCUT_HAS_NEXT_MASK; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4DictConstants); +}; +} // namespace latinime +#endif /* LATINIME_VER4_DICT_CONSTANTS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp new file mode 100644 index 000000000..17fc9483b --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp @@ -0,0 +1,98 @@ +/* + * 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/structure/v4/ver4_patricia_trie_node_reader.h" + +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { + +const PtNodeParams Ver4PatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProcessMovedPtNode( + const int ptNodePos, const int siblingNodePos) const { + if (ptNodePos < 0 || ptNodePos >= mBuffer->getTailPosition()) { + // Reading invalid position because of bug or broken dictionary. + AKLOGE("Fetching PtNode info from invalid dictionary position: %d, dictionary size: %d", + ptNodePos, mBuffer->getTailPosition()); + ASSERT(false); + return PtNodeParams(); + } + const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodePos); + const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer); + int pos = ptNodePos; + const int headPos = ptNodePos; + if (usesAdditionalBuffer) { + pos -= mBuffer->getOriginalBufferSize(); + } + const PatriciaTrieReadingUtils::NodeFlags flags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const int parentPosOffset = + DynamicPtReadingUtils::getParentPtNodePosOffsetAndAdvancePosition( + dictBuf, &pos); + const int parentPos = + DynamicPtReadingUtils::getParentPtNodePos(parentPosOffset, headPos); + int codePoints[MAX_WORD_LENGTH]; + const int codePonitCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition( + dictBuf, flags, MAX_WORD_LENGTH, codePoints, &pos); + int terminalIdFieldPos = NOT_A_DICT_POS; + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + int probability = NOT_A_PROBABILITY; + if (PatriciaTrieReadingUtils::isTerminal(flags)) { + terminalIdFieldPos = pos; + if (usesAdditionalBuffer) { + terminalIdFieldPos += mBuffer->getOriginalBufferSize(); + } + terminalId = Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition(dictBuf, &pos); + const ProbabilityEntry probabilityEntry = + mProbabilityDictContent->getProbabilityEntry(terminalId); + if (probabilityEntry.hasHistoricalInfo()) { + probability = ForgettingCurveUtils::decodeProbability( + probabilityEntry.getHistoricalInfo()); + } else { + probability = probabilityEntry.getProbability(); + } + } + int childrenPosFieldPos = pos; + if (usesAdditionalBuffer) { + childrenPosFieldPos += mBuffer->getOriginalBufferSize(); + } + int childrenPos = DynamicPtReadingUtils::readChildrenPositionAndAdvancePosition( + dictBuf, &pos); + if (usesAdditionalBuffer && childrenPos != NOT_A_DICT_POS) { + childrenPos += mBuffer->getOriginalBufferSize(); + } + if (usesAdditionalBuffer) { + pos += mBuffer->getOriginalBufferSize(); + } + // Sibling position is the tail position of original PtNode. + int newSiblingNodePos = (siblingNodePos == NOT_A_DICT_POS) ? pos : siblingNodePos; + // Read destination node if the read node is a moved node. + if (DynamicPtReadingUtils::isMoved(flags)) { + // The destination position is stored at the same place as the parent position. + return fetchPtNodeInfoFromBufferAndProcessMovedPtNode(parentPos, newSiblingNodePos); + } else { + return PtNodeParams(headPos, flags, parentPos, codePonitCount, codePoints, + terminalIdFieldPos, terminalId, probability, childrenPosFieldPos, childrenPos, + newSiblingNodePos); + } +} + +} diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h new file mode 100644 index 000000000..9d932457c --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h @@ -0,0 +1,58 @@ +/* + * 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_VER4_PATRICIA_TRIE_NODE_READER_H +#define LATINIME_VER4_PATRICIA_TRIE_NODE_READER_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_reader.h" + +namespace latinime { + +class BufferWithExtendableBuffer; +class ProbabilityDictContent; + +/* + * This class is used for helping to read nodes of ver4 patricia trie. This class handles moved + * node and reads node attributes including probability form probabilityBuffer. + */ +class Ver4PatriciaTrieNodeReader : public PtNodeReader { + public: + Ver4PatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer, + const ProbabilityDictContent *const probabilityDictContent) + : mBuffer(buffer), mProbabilityDictContent(probabilityDictContent) {} + + ~Ver4PatriciaTrieNodeReader() {} + + virtual const PtNodeParams fetchNodeInfoInBufferFromPtNodePos(const int ptNodePos) const { + return fetchPtNodeInfoFromBufferAndProcessMovedPtNode(ptNodePos, + NOT_A_DICT_POS /* siblingNodePos */); + } + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeReader); + + const BufferWithExtendableBuffer *const mBuffer; + const ProbabilityDictContent *const mProbabilityDictContent; + + const PtNodeParams fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos, + const int siblingNodePos) const; +}; +} // namespace latinime +#endif /* LATINIME_VER4_PATRICIA_TRIE_NODE_READER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp new file mode 100644 index 000000000..32576cf0a --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -0,0 +1,411 @@ +/* + * 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/structure/v4/ver4_patricia_trie_node_writer.h" + +#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_reading_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { + +const int Ver4PatriciaTrieNodeWriter::CHILDREN_POSITION_FIELD_SIZE = 3; + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsDeleted( + const PtNodeParams *const toBeUpdatedPtNodeParams) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */, + true /* isDeleted */, false /* willBecomeNonTerminal */); + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + // Update flags. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos)) { + return false; + } + if (toBeUpdatedPtNodeParams->isTerminal()) { + // The PtNode is a terminal. Delete entry from the terminal position lookup table. + return mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + toBeUpdatedPtNodeParams->getTerminalId(), NOT_A_DICT_POS /* ptNodePos */); + } else { + return true; + } +} + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsMoved( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const int movedPos, const int bigramLinkedNodePos) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */, + false /* isDeleted */, false /* willBecomeNonTerminal */); + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + // Update flags. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos)) { + return false; + } + // Update moved position, which is stored in the parent offset field. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition( + mTrieBuffer, movedPos, toBeUpdatedPtNodeParams->getHeadPos(), &writingPos)) { + return false; + } + if (toBeUpdatedPtNodeParams->hasChildren()) { + // Update children's parent position. + mReadingHelper.initWithPtNodeArrayPos(toBeUpdatedPtNodeParams->getChildrenPos()); + while (!mReadingHelper.isEnd()) { + const PtNodeParams childPtNodeParams(mReadingHelper.getPtNodeParams()); + int parentOffsetFieldPos = childPtNodeParams.getHeadPos() + + DynamicPtWritingUtils::NODE_FLAG_FIELD_SIZE; + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition( + mTrieBuffer, bigramLinkedNodePos, childPtNodeParams.getHeadPos(), + &parentOffsetFieldPos)) { + // Parent offset cannot be written because of a bug or a broken dictionary; thus, + // we give up to update dictionary. + return false; + } + mReadingHelper.readNextSiblingNode(childPtNodeParams); + } + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::markPtNodeAsWillBecomeNonTerminal( + const PtNodeParams *const toBeUpdatedPtNodeParams) { + int pos = toBeUpdatedPtNodeParams->getHeadPos(); + const bool usesAdditionalBuffer = mTrieBuffer->isInAdditionalBuffer(pos); + const uint8_t *const dictBuf = mTrieBuffer->getBuffer(usesAdditionalBuffer); + if (usesAdditionalBuffer) { + pos -= mTrieBuffer->getOriginalBufferSize(); + } + // Read original flags + const PatriciaTrieReadingUtils::NodeFlags originalFlags = + PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos); + const PatriciaTrieReadingUtils::NodeFlags updatedFlags = + DynamicPtReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */, + false /* isDeleted */, true /* willBecomeNonTerminal */); + if (!mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + toBeUpdatedPtNodeParams->getTerminalId(), NOT_A_DICT_POS /* ptNodePos */)) { + AKLOGE("Cannot update terminal position lookup table. terminal id: %d", + toBeUpdatedPtNodeParams->getTerminalId()); + return false; + } + // Update flags. + int writingPos = toBeUpdatedPtNodeParams->getHeadPos(); + return DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, updatedFlags, + &writingPos); +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbability( + const PtNodeParams *const toBeUpdatedPtNodeParams, const int newProbability, + const int timestamp) { + if (!toBeUpdatedPtNodeParams->isTerminal()) { + return false; + } + const ProbabilityEntry originalProbabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId()); + const ProbabilityEntry probabilityEntry = createUpdatedEntryFrom(&originalProbabilityEntry, + newProbability, timestamp); + return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry); +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + const PtNodeParams *const toBeUpdatedPtNodeParams, bool *const outNeedsToKeepPtNode) { + if (!toBeUpdatedPtNodeParams->isTerminal()) { + AKLOGE("updatePtNodeProbabilityAndGetNeedsToSaveForGC is called for non-terminal PtNode."); + return false; + } + const ProbabilityEntry originalProbabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId()); + if (originalProbabilityEntry.hasHistoricalInfo()) { + const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( + originalProbabilityEntry.getHistoricalInfo()); + const ProbabilityEntry probabilityEntry = + originalProbabilityEntry.createEntryWithUpdatedHistoricalInfo(&historicalInfo); + if (!mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry)) { + AKLOGE("Cannot write updated probability entry. terminalId: %d", + toBeUpdatedPtNodeParams->getTerminalId()); + return false; + } + const bool isValid = ForgettingCurveUtils::needsToKeep(&historicalInfo); + if (!isValid) { + if (!markPtNodeAsWillBecomeNonTerminal(toBeUpdatedPtNodeParams)) { + AKLOGE("Cannot mark PtNode as willBecomeNonTerminal."); + return false; + } + } + *outNeedsToKeepPtNode = isValid; + } else { + // No need to update probability. + *outNeedsToKeepPtNode = true; + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::updateChildrenPosition( + const PtNodeParams *const toBeUpdatedPtNodeParams, const int newChildrenPosition) { + int childrenPosFieldPos = toBeUpdatedPtNodeParams->getChildrenPosFieldPos(); + return DynamicPtWritingUtils::writeChildrenPositionAndAdvancePosition(mTrieBuffer, + newChildrenPosition, &childrenPosFieldPos); +} + +bool Ver4PatriciaTrieNodeWriter::updateTerminalId(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newTerminalId) { + return mTrieBuffer->writeUint(newTerminalId, Ver4DictConstants::TERMINAL_ID_FIELD_SIZE, + toBeUpdatedPtNodeParams->getTerminalIdFieldPos()); +} + +bool Ver4PatriciaTrieNodeWriter::writePtNodeAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const ptNodeWritingPos) { + return writePtNodeAndGetTerminalIdAndAdvancePosition(ptNodeParams, 0 /* outTerminalId */, + ptNodeWritingPos); +} + + +bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( + const PtNodeParams *const ptNodeParams, const int timestamp, int *const ptNodeWritingPos) { + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + if (!writePtNodeAndGetTerminalIdAndAdvancePosition(ptNodeParams, &terminalId, + ptNodeWritingPos)) { + return false; + } + // Write probability. + ProbabilityEntry newProbabilityEntry; + const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( + &newProbabilityEntry, ptNodeParams->getProbability(), timestamp); + return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry(terminalId, + &probabilityEntryToWrite); +} + +bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry( + const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, + bool *const outAddedNewBigram) { + if (!mBigramPolicy->addNewEntry(sourcePtNodeParams->getTerminalId(), + targetPtNodeParam->getTerminalId(), probability, timestamp, outAddedNewBigram)) { + AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", + sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); + return false; + } + if (!sourcePtNodeParams->hasBigrams()) { + // Update has bigrams flag. + return updatePtNodeFlags(sourcePtNodeParams->getHeadPos(), + sourcePtNodeParams->isBlacklisted(), sourcePtNodeParams->isNotAWord(), + sourcePtNodeParams->isTerminal(), sourcePtNodeParams->hasShortcutTargets(), + true /* hasBigrams */, + sourcePtNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::removeBigramEntry( + const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam) { + return mBigramPolicy->removeEntry(sourcePtNodeParams->getTerminalId(), + targetPtNodeParam->getTerminalId()); +} + +bool Ver4PatriciaTrieNodeWriter::updateAllBigramEntriesAndDeleteUselessEntries( + const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount) { + return mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries( + sourcePtNodeParams->getTerminalId(), outBigramEntryCount); +} + +bool Ver4PatriciaTrieNodeWriter::updateAllPositionFields( + const PtNodeParams *const toBeUpdatedPtNodeParams, + const DictPositionRelocationMap *const dictPositionRelocationMap, + int *const outBigramEntryCount) { + int parentPos = toBeUpdatedPtNodeParams->getParentPos(); + if (parentPos != NOT_A_DICT_POS) { + PtNodeWriter::PtNodePositionRelocationMap::const_iterator it = + dictPositionRelocationMap->mPtNodePositionRelocationMap.find(parentPos); + if (it != dictPositionRelocationMap->mPtNodePositionRelocationMap.end()) { + parentPos = it->second; + } + } + int writingPos = toBeUpdatedPtNodeParams->getHeadPos() + + DynamicPtWritingUtils::NODE_FLAG_FIELD_SIZE; + // Write updated parent offset. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition(mTrieBuffer, + parentPos, toBeUpdatedPtNodeParams->getHeadPos(), &writingPos)) { + return false; + } + + // Updates children position. + int childrenPos = toBeUpdatedPtNodeParams->getChildrenPos(); + if (childrenPos != NOT_A_DICT_POS) { + PtNodeWriter::PtNodeArrayPositionRelocationMap::const_iterator it = + dictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.find(childrenPos); + if (it != dictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.end()) { + childrenPos = it->second; + } + } + if (!updateChildrenPosition(toBeUpdatedPtNodeParams, childrenPos)) { + return false; + } + + // Counts bigram entries. + if (outBigramEntryCount) { + *outBigramEntryCount = mBigramPolicy->getBigramEntryConut( + toBeUpdatedPtNodeParams->getTerminalId()); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::addShortcutTarget(const PtNodeParams *const ptNodeParams, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability) { + if (!mShortcutPolicy->addNewShortcut(ptNodeParams->getTerminalId(), + targetCodePoints, targetCodePointCount, shortcutProbability)) { + AKLOGE("Cannot add new shortuct entry. terminalId: %d", ptNodeParams->getTerminalId()); + return false; + } + if (!ptNodeParams->hasShortcutTargets()) { + // Update has shortcut targets flag. + return updatePtNodeFlags(ptNodeParams->getHeadPos(), + ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), + ptNodeParams->isTerminal(), true /* hasShortcutTargets */, + ptNodeParams->hasBigrams(), + ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); + } + return true; +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeHasBigramsAndShortcutTargetsFlags( + const PtNodeParams *const ptNodeParams) { + const bool hasBigrams = mBuffers->getBigramDictContent()->getBigramListHeadPos( + ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; + const bool hasShortcutTargets = mBuffers->getShortcutDictContent()->getShortcutListHeadPos( + ptNodeParams->getTerminalId()) != NOT_A_DICT_POS; + return updatePtNodeFlags(ptNodeParams->getHeadPos(), ptNodeParams->isBlacklisted(), + ptNodeParams->isNotAWord(), ptNodeParams->isTerminal(), hasShortcutTargets, + hasBigrams, ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); +} + +bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const outTerminalId, + int *const ptNodeWritingPos) { + const int nodePos = *ptNodeWritingPos; + // Write dummy flags. The Node flags are updated with appropriate flags at the last step of the + // PtNode writing. + if (!DynamicPtWritingUtils::writeFlagsAndAdvancePosition(mTrieBuffer, + 0 /* nodeFlags */, ptNodeWritingPos)) { + return false; + } + // Calculate a parent offset and write the offset. + if (!DynamicPtWritingUtils::writeParentPosOffsetAndAdvancePosition(mTrieBuffer, + ptNodeParams->getParentPos(), nodePos, ptNodeWritingPos)) { + return false; + } + // Write code points + if (!DynamicPtWritingUtils::writeCodePointsAndAdvancePosition(mTrieBuffer, + ptNodeParams->getCodePoints(), ptNodeParams->getCodePointCount(), ptNodeWritingPos)) { + return false; + } + int terminalId = Ver4DictConstants::NOT_A_TERMINAL_ID; + if (!ptNodeParams->willBecomeNonTerminal()) { + if (ptNodeParams->getTerminalId() != Ver4DictConstants::NOT_A_TERMINAL_ID) { + terminalId = ptNodeParams->getTerminalId(); + } else if (ptNodeParams->isTerminal()) { + // Write terminal information using a new terminal id. + // Get a new unused terminal id. + terminalId = mBuffers->getTerminalPositionLookupTable()->getNextTerminalId(); + } + } + const int isTerminal = terminalId != Ver4DictConstants::NOT_A_TERMINAL_ID; + if (isTerminal) { + // Update the lookup table. + if (!mBuffers->getMutableTerminalPositionLookupTable()->setTerminalPtNodePosition( + terminalId, nodePos)) { + return false; + } + // Write terminal Id. + if (!mTrieBuffer->writeUintAndAdvancePosition(terminalId, + Ver4DictConstants::TERMINAL_ID_FIELD_SIZE, ptNodeWritingPos)) { + return false; + } + if (outTerminalId) { + *outTerminalId = terminalId; + } + } + // Write children position + if (!DynamicPtWritingUtils::writeChildrenPositionAndAdvancePosition(mTrieBuffer, + ptNodeParams->getChildrenPos(), ptNodeWritingPos)) { + return false; + } + return updatePtNodeFlags(nodePos, ptNodeParams->isBlacklisted(), ptNodeParams->isNotAWord(), + isTerminal, ptNodeParams->hasShortcutTargets(), ptNodeParams->hasBigrams(), + ptNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); +} + +const ProbabilityEntry Ver4PatriciaTrieNodeWriter::createUpdatedEntryFrom( + const ProbabilityEntry *const originalProbabilityEntry, const int newProbability, + const int timestamp) const { + // TODO: Consolidate historical info and probability. + if (mBuffers->getHeaderPolicy()->hasHistoricalInfoOfWords()) { + const HistoricalInfo updatedHistoricalInfo = + ForgettingCurveUtils::createUpdatedHistoricalInfo( + originalProbabilityEntry->getHistoricalInfo(), newProbability, timestamp); + return originalProbabilityEntry->createEntryWithUpdatedHistoricalInfo( + &updatedHistoricalInfo); + } else { + return originalProbabilityEntry->createEntryWithUpdatedProbability(newProbability); + } +} + +bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, + const bool isBlacklisted, const bool isNotAWord, const bool isTerminal, + const bool hasShortcutTargets, const bool hasBigrams, const bool hasMultipleChars) { + // Create node flags and write them. + PatriciaTrieReadingUtils::NodeFlags nodeFlags = + PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord, isTerminal, + hasShortcutTargets, hasBigrams, hasMultipleChars, + CHILDREN_POSITION_FIELD_SIZE); + if (!DynamicPtWritingUtils::writeFlags(mTrieBuffer, nodeFlags, ptNodePos)) { + AKLOGE("Cannot write PtNode flags. flags: %x, pos: %d", nodeFlags, ptNodePos); + return false; + } + return true; +} + +} diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h new file mode 100644 index 000000000..69576d8e5 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_VER4_PATRICIA_TRIE_NODE_WRITER_H +#define LATINIME_VER4_PATRICIA_TRIE_NODE_WRITER_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" + +namespace latinime { + +class BufferWithExtendableBuffer; +class Ver4BigramListPolicy; +class Ver4DictBuffers; +class Ver4ShortcutListPolicy; + +/* + * This class is used for helping to writes nodes of ver4 patricia trie. + */ +class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { + public: + Ver4PatriciaTrieNodeWriter(BufferWithExtendableBuffer *const trieBuffer, + Ver4DictBuffers *const buffers, const Ver4PatriciaTrieNodeReader *const ptNodeReader, + Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy) + : mTrieBuffer(trieBuffer), mBuffers(buffers), mPtNodeReader(ptNodeReader), + mReadingHelper(mTrieBuffer, mPtNodeReader), + mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy) {} + + virtual ~Ver4PatriciaTrieNodeWriter() {} + + virtual bool markPtNodeAsDeleted(const PtNodeParams *const toBeUpdatedPtNodeParams); + + virtual bool markPtNodeAsMoved(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int movedPos, const int bigramLinkedNodePos); + + virtual bool markPtNodeAsWillBecomeNonTerminal( + const PtNodeParams *const toBeUpdatedPtNodeParams); + + virtual bool updatePtNodeProbability(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newProbability, const int timestamp); + + virtual bool updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( + const PtNodeParams *const toBeUpdatedPtNodeParams, bool *const outNeedsToKeepPtNode); + + virtual bool updateChildrenPosition(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newChildrenPosition); + + bool updateTerminalId(const PtNodeParams *const toBeUpdatedPtNodeParams, + const int newTerminalId); + + virtual bool writePtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + int *const ptNodeWritingPos); + + virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, + const int timestamp, int *const ptNodeWritingPos); + + virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam, const int probability, const int timestamp, + bool *const outAddedNewBigram); + + virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, + const PtNodeParams *const targetPtNodeParam); + + virtual bool updateAllBigramEntriesAndDeleteUselessEntries( + const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount); + + virtual bool updateAllPositionFields(const PtNodeParams *const toBeUpdatedPtNodeParams, + const DictPositionRelocationMap *const dictPositionRelocationMap, + int *const outBigramEntryCount); + + virtual bool addShortcutTarget(const PtNodeParams *const ptNodeParams, + const int *const targetCodePoints, const int targetCodePointCount, + const int shortcutProbability); + + bool updatePtNodeHasBigramsAndShortcutTargetsFlags(const PtNodeParams *const ptNodeParams); + + private: + DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeWriter); + + bool writePtNodeAndGetTerminalIdAndAdvancePosition( + const PtNodeParams *const ptNodeParams, int *const outTerminalId, + int *const ptNodeWritingPos); + + // Create updated probability entry using given probability and timestamp. In addition to the + // probability, this method updates historical information if needed. + const ProbabilityEntry createUpdatedEntryFrom( + const ProbabilityEntry *const originalProbabilityEntry, const int newProbability, + const int timestamp) const; + + bool updatePtNodeFlags(const int ptNodePos, const bool isBlacklisted, const bool isNotAWord, + const bool isTerminal, const bool hasShortcutTargets, const bool hasBigrams, + const bool hasMultipleChars); + + static const int CHILDREN_POSITION_FIELD_SIZE; + + BufferWithExtendableBuffer *const mTrieBuffer; + Ver4DictBuffers *const mBuffers; + const Ver4PatriciaTrieNodeReader *const mPtNodeReader; + DynamicPtReadingHelper mReadingHelper; + Ver4BigramListPolicy *const mBigramPolicy; + Ver4ShortcutListPolicy *const mShortcutPolicy; +}; +} // namespace latinime +#endif /* LATINIME_VER4_PATRICIA_TRIE_NODE_WRITER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp new file mode 100644 index 000000000..96bb8128e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -0,0 +1,352 @@ +/* + * 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/structure/v4/ver4_patricia_trie_policy.h" + +#include <vector> + +#include "suggest/core/dicnode/dic_node.h" +#include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/unigram_property.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" +#include "suggest/policyimpl/dictionary/utils/probability_utils.h" + +namespace latinime { + +// Note that there are corresponding definitions in Java side in BinaryDictionaryTests and +// BinaryDictionaryDecayingTests. +const char *const Ver4PatriciaTriePolicy::UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::BIGRAM_COUNT_QUERY = "BIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; +const char *const Ver4PatriciaTriePolicy::SET_CURRENT_TIME_FOR_TESTING_QUERY_FORMAT = + "SET_CURRENT_TIME_FOR_TESTING:%d"; +const char *const Ver4PatriciaTriePolicy::GET_CURRENT_TIME_QUERY = "GET_CURRENT_TIME"; +const char *const Ver4PatriciaTriePolicy::QUIT_TIMEKEEPER_TEST_MODE_QUERY = + "QUIT_TIMEKEEPER_TEST_MODE"; +const int Ver4PatriciaTriePolicy::MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS = 1024; +const int Ver4PatriciaTriePolicy::MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS = + Ver4DictConstants::MAX_DICTIONARY_SIZE - MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; + +void Ver4PatriciaTriePolicy::createAndGetAllChildDicNodes(const DicNode *const dicNode, + DicNodeVector *const childDicNodes) const { + if (!dicNode->hasChildren()) { + return; + } + DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader); + readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPtNodeArrayPos()); + while (!readingHelper.isEnd()) { + const PtNodeParams ptNodeParams = readingHelper.getPtNodeParams(); + if (!ptNodeParams.isValid()) { + break; + } + bool isTerminal = ptNodeParams.isTerminal() && !ptNodeParams.isDeleted(); + if (isTerminal && mHeaderPolicy->isDecayingDict()) { + // A DecayingDict may have a terminal PtNode that has a terminal DicNode whose + // probability is NOT_A_PROBABILITY. In such case, we don't want to treat it as a + // valid terminal DicNode. + isTerminal = ptNodeParams.getProbability() != NOT_A_PROBABILITY; + } + childDicNodes->pushLeavingChild(dicNode, ptNodeParams.getHeadPos(), + ptNodeParams.getChildrenPos(), ptNodeParams.getProbability(), isTerminal, + ptNodeParams.hasChildren(), + ptNodeParams.isBlacklisted() + || ptNodeParams.isNotAWord() /* isBlacklistedOrNotAWord */, + ptNodeParams.getCodePointCount(), ptNodeParams.getCodePoints()); + readingHelper.readNextSiblingNode(ptNodeParams); + } +} + +int Ver4PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( + const int ptNodePos, const int maxCodePointCount, int *const outCodePoints, + int *const outUnigramProbability) const { + DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader); + readingHelper.initWithPtNodePos(ptNodePos); + return readingHelper.getCodePointsAndProbabilityAndReturnCodePointCount( + maxCodePointCount, outCodePoints, outUnigramProbability); +} + +int Ver4PatriciaTriePolicy::getTerminalPtNodePositionOfWord(const int *const inWord, + const int length, const bool forceLowerCaseSearch) const { + DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); + return readingHelper.getTerminalPtNodePositionOfWord(inWord, length, forceLowerCaseSearch); +} + +int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability, + const int bigramProbability) const { + if (mHeaderPolicy->isDecayingDict()) { + // Both probabilities are encoded. Decode them and get probability. + return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability); + } else { + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (bigramProbability == NOT_A_PROBABILITY) { + return ProbabilityUtils::backoff(unigramProbability); + } else { + // bigramProbability is a bigram probability delta. + return ProbabilityUtils::computeProbabilityForBigram(unigramProbability, + bigramProbability); + } + } +} + +int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_PROBABILITY; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { + return NOT_A_PROBABILITY; + } + return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY); +} + +int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_DICT_POS; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted()) { + return NOT_A_DICT_POS; + } + return mBuffers.get()->getShortcutDictContent()->getShortcutListHeadPos( + ptNodeParams.getTerminalId()); +} + +int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { + if (ptNodePos == NOT_A_DICT_POS) { + return NOT_A_DICT_POS; + } + const PtNodeParams ptNodeParams(mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos)); + if (ptNodeParams.isDeleted()) { + return NOT_A_DICT_POS; + } + return mBuffers.get()->getBigramDictContent()->getBigramListHeadPos( + ptNodeParams.getTerminalId()); +} + +bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int length, + const int probability, const int *const shortcutTargetCodePoints, const int shortcutLength, + const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, + const int timestamp) { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + DynamicPtReadingHelper readingHelper(mDictBuffer, &mNodeReader); + readingHelper.initWithPtNodeArrayPos(getRootPosition()); + bool addedNewUnigram = false; + if (mUpdatingHelper.addUnigramWord(&readingHelper, word, length, probability, isNotAWord, + isBlacklisted, timestamp, &addedNewUnigram)) { + if (addedNewUnigram) { + mUnigramCount++; + } + if (shortcutLength > 0) { + // Add shortcut target. + const int wordPos = getTerminalPtNodePositionOfWord(word, length, + false /* forceLowerCaseSearch */); + if (wordPos == NOT_A_DICT_POS) { + AKLOGE("Cannot find terminal PtNode position to add shortcut target."); + return false; + } + if (!mUpdatingHelper.addShortcutTarget(wordPos, shortcutTargetCodePoints, + shortcutLength, shortcutProbability)) { + AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %d, probability: %d", + wordPos, shortcutLength, shortcutProbability); + return false; + } + } + return true; + } else { + return false; + } +} + +bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int length0, + const int *const word1, const int length1, const int probability, + const int timestamp) { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0, + false /* forceLowerCaseSearch */); + if (word0Pos == NOT_A_DICT_POS) { + return false; + } + const int word1Pos = getTerminalPtNodePositionOfWord(word1, length1, + false /* forceLowerCaseSearch */); + if (word1Pos == NOT_A_DICT_POS) { + return false; + } + bool addedNewBigram = false; + if (mUpdatingHelper.addBigramWords(word0Pos, word1Pos, probability, timestamp, + &addedNewBigram)) { + if (addedNewBigram) { + mBigramCount++; + } + return true; + } else { + return false; + } +} + +bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int length0, + const int *const word1, const int length1) { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); + return false; + } + if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS) { + AKLOGE("The dictionary is too large to dynamically update. Dictionary size: %d", + mDictBuffer->getTailPosition()); + return false; + } + const int word0Pos = getTerminalPtNodePositionOfWord(word0, length0, + false /* forceLowerCaseSearch */); + if (word0Pos == NOT_A_DICT_POS) { + return false; + } + const int word1Pos = getTerminalPtNodePositionOfWord(word1, length1, + false /* forceLowerCaseSearch */); + if (word1Pos == NOT_A_DICT_POS) { + return false; + } + if (mUpdatingHelper.removeBigramWords(word0Pos, word1Pos)) { + mBigramCount--; + return true; + } else { + return false; + } +} + +void Ver4PatriciaTriePolicy::flush(const char *const filePath) { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); + return; + } + mWritingHelper.writeToDictFile(filePath, mUnigramCount, mBigramCount); +} + +void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); + return; + } + mWritingHelper.writeToDictFileWithGC(getRootPosition(), filePath); +} + +bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary."); + return false; + } + if (mBuffers.get()->isNearSizeLimit()) { + // Additional buffer size is near the limit. + return true; + } else if (mHeaderPolicy->getExtendedRegionSize() + mDictBuffer->getUsedAdditionalBufferSize() + > Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE) { + // Total extended region size of the trie exceeds the limit. + return true; + } else if (mDictBuffer->getTailPosition() >= MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS + && mDictBuffer->getUsedAdditionalBufferSize() > 0) { + // Needs to reduce dictionary size. + return true; + } else if (mHeaderPolicy->isDecayingDict()) { + return ForgettingCurveUtils::needsToDecay(mindsBlockByGC, mUnigramCount, mBigramCount, + mHeaderPolicy); + } + return false; +} + +void Ver4PatriciaTriePolicy::getProperty(const char *const query, const int queryLength, + char *const outResult, const int maxResultLength) { + const int compareLength = queryLength + 1 /* terminator */; + int timestamp = NOT_A_TIMESTAMP; + if (strncmp(query, UNIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", mUnigramCount); + } else if (strncmp(query, BIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", mBigramCount); + } else if (strncmp(query, MAX_UNIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", + mHeaderPolicy->isDecayingDict() ? ForgettingCurveUtils::MAX_UNIGRAM_COUNT : + static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); + } else if (strncmp(query, MAX_BIGRAM_COUNT_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", + mHeaderPolicy->isDecayingDict() ? ForgettingCurveUtils::MAX_BIGRAM_COUNT : + static_cast<int>(Ver4DictConstants::MAX_DICTIONARY_SIZE)); + } else if (sscanf(query, SET_CURRENT_TIME_FOR_TESTING_QUERY_FORMAT, ×tamp) == 1) { + TimeKeeper::startTestModeWithForceCurrentTime(timestamp); + } else if (strncmp(query, GET_CURRENT_TIME_QUERY, compareLength) == 0) { + snprintf(outResult, maxResultLength, "%d", TimeKeeper::peekCurrentTime()); + } else if (strncmp(query, QUIT_TIMEKEEPER_TEST_MODE_QUERY, compareLength) == 0) { + TimeKeeper::stopTestMode(); + } +} + +const UnigramProperty Ver4PatriciaTriePolicy::getUnigramProperty(const int *const codePoints, + const int codePointCount) const { + const int ptNodePos = getTerminalPtNodePositionOfWord(codePoints, codePointCount, + false /* forceLowerCaseSearch */); + if (ptNodePos == NOT_A_DICT_POS) { + AKLOGE("fetchUnigramProperty is called for invalid word."); + return UnigramProperty(); + } + const PtNodeParams ptNodeParams = mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos); + const ProbabilityEntry probabilityEntry = + mBuffers.get()->getProbabilityDictContent()->getProbabilityEntry( + ptNodeParams.getTerminalId()); + const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); + // Fetch shortcut information. + std::vector<std::vector<int> > shortcutTargets; + std::vector<int> shortcutProbabilities; + int shortcutPos = getShortcutPositionOfPtNode(ptNodePos); + if (shortcutPos != NOT_A_DICT_POS) { + int shortcutTarget[MAX_WORD_LENGTH]; + const ShortcutDictContent *const shortcutDictContent = + mBuffers.get()->getShortcutDictContent(); + bool hasNext = true; + while (hasNext) { + int shortcutTargetLength = 0; + int shortcutProbability = NOT_A_PROBABILITY; + shortcutDictContent->getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, shortcutTarget, + &shortcutTargetLength, &shortcutProbability, &hasNext, &shortcutPos); + std::vector<int> target(shortcutTarget, shortcutTarget + shortcutTargetLength); + shortcutTargets.push_back(target); + shortcutProbabilities.push_back(shortcutProbability); + } + } + return UnigramProperty(ptNodeParams.getCodePoints(), ptNodeParams.getCodePointCount(), + ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.hasBigrams(), + ptNodeParams.hasShortcutTargets(), ptNodeParams.getProbability(), + historicalInfo->getTimeStamp(), historicalInfo->getLevel(), + historicalInfo->getCount(), &shortcutTargets, &shortcutProbabilities); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h new file mode 100644 index 000000000..8187b7a39 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_VER4_PATRICIA_TRIE_POLICY_H +#define LATINIME_VER4_PATRICIA_TRIE_POLICY_H + +#include "defines.h" +#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +class DicNode; +class DicNodeVector; + +// TODO: Implement. +class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { + public: + Ver4PatriciaTriePolicy(const Ver4DictBuffers::Ver4DictBuffersPtr &buffers) + : mBuffers(buffers), mHeaderPolicy(mBuffers.get()->getHeaderPolicy()), + mDictBuffer(mBuffers.get()->getWritableTrieBuffer()), + mBigramPolicy(mBuffers.get()->getMutableBigramDictContent(), + mBuffers.get()->getTerminalPositionLookupTable(), mHeaderPolicy), + mShortcutPolicy(mBuffers.get()->getMutableShortcutDictContent(), + mBuffers.get()->getTerminalPositionLookupTable()), + mNodeReader(mDictBuffer, mBuffers.get()->getProbabilityDictContent()), + mNodeWriter(mDictBuffer, mBuffers.get(), &mNodeReader, &mBigramPolicy, + &mShortcutPolicy), + mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter), + mWritingHelper(mBuffers.get()), + mUnigramCount(mHeaderPolicy->getUnigramCount()), + mBigramCount(mHeaderPolicy->getBigramCount()) {}; + + AK_FORCE_INLINE int getRootPosition() const { + return 0; + } + + void createAndGetAllChildDicNodes(const DicNode *const dicNode, + DicNodeVector *const childDicNodes) const; + + int getCodePointsAndProbabilityAndReturnCodePointCount( + const int terminalPtNodePos, const int maxCodePointCount, int *const outCodePoints, + int *const outUnigramProbability) const; + + int getTerminalPtNodePositionOfWord(const int *const inWord, + const int length, const bool forceLowerCaseSearch) const; + + int getProbability(const int unigramProbability, const int bigramProbability) const; + + int getUnigramProbabilityOfPtNode(const int ptNodePos) const; + + int getShortcutPositionOfPtNode(const int ptNodePos) const; + + int getBigramsPositionOfPtNode(const int ptNodePos) const; + + const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { + return mHeaderPolicy; + } + + const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const { + return &mBigramPolicy; + } + + const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const { + return &mShortcutPolicy; + } + + bool addUnigramWord(const int *const word, const int length, const int probability, + const int *const shortcutTargetCodePoints, const int shortcutLength, + const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, + const int timestamp); + + bool addBigramWords(const int *const word0, const int length0, const int *const word1, + const int length1, const int probability, const int timestamp); + + bool removeBigramWords(const int *const word0, const int length0, const int *const word1, + const int length1); + + void flush(const char *const filePath); + + void flushWithGC(const char *const filePath); + + bool needsToRunGC(const bool mindsBlockByGC) const; + + void getProperty(const char *const query, const int queryLength, char *const outResult, + const int maxResultLength); + + const UnigramProperty getUnigramProperty(const int *const codePoints, + const int codePointCount) const; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTriePolicy); + + static const char *const UNIGRAM_COUNT_QUERY; + static const char *const BIGRAM_COUNT_QUERY; + static const char *const MAX_UNIGRAM_COUNT_QUERY; + static const char *const MAX_BIGRAM_COUNT_QUERY; + static const char *const SET_CURRENT_TIME_FOR_TESTING_QUERY_FORMAT; + static const char *const GET_CURRENT_TIME_QUERY; + static const char *const QUIT_TIMEKEEPER_TEST_MODE_QUERY; + // When the dictionary size is near the maximum size, we have to refuse dynamic operations to + // prevent the dictionary from overflowing. + static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; + static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS; + + Ver4DictBuffers::Ver4DictBuffersPtr mBuffers; + const HeaderPolicy *const mHeaderPolicy; + BufferWithExtendableBuffer *const mDictBuffer; + Ver4BigramListPolicy mBigramPolicy; + Ver4ShortcutListPolicy mShortcutPolicy; + Ver4PatriciaTrieNodeReader mNodeReader; + Ver4PatriciaTrieNodeWriter mNodeWriter; + DynamicPtUpdatingHelper mUpdatingHelper; + Ver4PatriciaTrieWritingHelper mWritingHelper; + int mUnigramCount; + int mBigramCount; +}; +} // namespace latinime +#endif // LATINIME_VER4_PATRICIA_TRIE_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.cpp new file mode 100644 index 000000000..254022db4 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.cpp @@ -0,0 +1,28 @@ +/* + * 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/structure/v4/ver4_patricia_trie_reading_utils.h" + +#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" + +namespace latinime { + +/* static */ int Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition( + const uint8_t *const buffer, int *pos) { + return ByteArrayUtils::readUint32AndAdvancePosition(buffer, pos); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h new file mode 100644 index 000000000..e418c4933 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h @@ -0,0 +1,37 @@ +/* + * 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_VER4_PATRICIA_TRIE_READING_UTILS_H +#define LATINIME_VER4_PATRICIA_TRIE_READING_UTILS_H + +#include <stdint.h> + +#include "defines.h" + +namespace latinime { + +class BufferWithExtendableBuffer; + +class Ver4PatriciaTrieReadingUtils { + public: + static int getTerminalIdAndAdvancePosition(const uint8_t *const buffer, + int *const pos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTrieReadingUtils); +}; +} // namespace latinime +#endif /* LATINIME_VER4_PATRICIA_TRIE_READING_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp new file mode 100644 index 000000000..43227635c --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -0,0 +1,285 @@ +/* + * 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/structure/v4/ver4_patricia_trie_writing_helper.h" + +#include <cstring> +#include <queue> + +#include "suggest/policyimpl/dictionary/bigram/ver4_bigram_list_policy.h" +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/shortcut/ver4_shortcut_list_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + +namespace latinime { + +void Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const dictDirPath, + const int unigramCount, const int bigramCount) const { + const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); + BufferWithExtendableBuffer headerBuffer( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + + mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize(); + if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */, + false /* updatesLastDecayedTime */, unigramCount, bigramCount, extendedRegionSize)) { + AKLOGE("Cannot write header structure to buffer. updatesLastUpdatedTime: %d, " + "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, " + "extendedRegionSize: %d", false, false, unigramCount, bigramCount, + extendedRegionSize); + return; + } + mBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); +} + +void Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeArrayPos, + const char *const dictDirPath) { + const HeaderPolicy *const headerPolicy = mBuffers->getHeaderPolicy(); + Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( + Ver4DictBuffers::createVer4DictBuffers(headerPolicy)); + int unigramCount = 0; + int bigramCount = 0; + if (!runGC(rootPtNodeArrayPos, headerPolicy, dictBuffers.get(), &unigramCount, &bigramCount)) { + return; + } + BufferWithExtendableBuffer headerBuffer( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, true /* updatesLastUpdatedTime */, + true /* updatesLastDecayedTime */, unigramCount, bigramCount, + 0 /* extendedRegionSize */)) { + return; + } + dictBuffers.get()->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); +} + +bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, + const HeaderPolicy *const headerPolicy, Ver4DictBuffers *const buffersToWrite, + int *const outUnigramCount, int *const outBigramCount) { + Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(), + mBuffers->getProbabilityDictContent()); + Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(), + mBuffers->getTerminalPositionLookupTable(), headerPolicy); + Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(), + mBuffers->getTerminalPositionLookupTable()); + Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(), + mBuffers, &ptNodeReader, &bigramPolicy, &shortcutPolicy); + + DynamicPtReadingHelper readingHelper(mBuffers->getTrieBuffer(), &ptNodeReader); + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners + ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted( + &ptNodeWriter); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) { + return false; + } + const int unigramCount = traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted + .getValidUnigramCount(); + if (headerPolicy->isDecayingDict() + && unigramCount > ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC) { + if (!truncateUnigrams(&ptNodeReader, &ptNodeWriter, + ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC)) { + AKLOGE("Cannot remove unigrams. current: %d, max: %d", unigramCount, + ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC); + return false; + } + } + + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners::TraversePolicyToUpdateBigramProbability + traversePolicyToUpdateBigramProbability(&ptNodeWriter); + if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateBigramProbability)) { + return false; + } + const int bigramCount = traversePolicyToUpdateBigramProbability.getValidBigramEntryCount(); + if (headerPolicy->isDecayingDict() + && bigramCount > ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC) { + if (!truncateBigrams(ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC)) { + AKLOGE("Cannot remove bigrams. current: %d, max: %d", bigramCount, + ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC); + return false; + } + } + + // Mapping from positions in mBuffer to positions in bufferToWrite. + PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap; + readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(), + buffersToWrite, &ptNodeReader, &bigramPolicy, &shortcutPolicy); + DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer + traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers, + buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap); + if (!readingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToPlaceAndWriteValidPtNodesToBuffer)) { + return false; + } + + // Create policy instances for the GCed dictionary. + Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(), + buffersToWrite->getProbabilityDictContent()); + Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(), + buffersToWrite->getTerminalPositionLookupTable(), headerPolicy); + Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(), + buffersToWrite->getTerminalPositionLookupTable()); + Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(), + buffersToWrite, &newPtNodeReader, &newBigramPolicy, &newShortcutPolicy); + // Re-assign terminal IDs for valid terminal PtNodes. + TerminalPositionLookupTable::TerminalIdMap terminalIdMap; + if(!buffersToWrite->getMutableTerminalPositionLookupTable()->runGCTerminalIds( + &terminalIdMap)) { + return false; + } + // Run GC for probability dict content. + if (!buffersToWrite->getMutableProbabilityDictContent()->runGC(&terminalIdMap, + mBuffers->getProbabilityDictContent())) { + return false; + } + // Run GC for bigram dict content. + if(!buffersToWrite->getMutableBigramDictContent()->runGC(&terminalIdMap, + mBuffers->getBigramDictContent(), outBigramCount)) { + return false; + } + // Run GC for shortcut dict content. + if(!buffersToWrite->getMutableShortcutDictContent()->runGC(&terminalIdMap, + mBuffers->getShortcutDictContent())) { + return false; + } + DynamicPtReadingHelper newDictReadingHelper(buffersToWrite->getTrieBuffer(), + &newPtNodeReader); + newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + DynamicPtGcEventListeners::TraversePolicyToUpdateAllPositionFields + traversePolicyToUpdateAllPositionFields(&newPtNodeWriter, &dictPositionRelocationMap); + if (!newDictReadingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner( + &traversePolicyToUpdateAllPositionFields)) { + return false; + } + newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos); + TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds(&newPtNodeWriter, &terminalIdMap); + if (!newDictReadingHelper.traverseAllPtNodesInPostorderDepthFirstManner( + &traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds)) { + return false; + } + *outUnigramCount = traversePolicyToUpdateAllPositionFields.getUnigramCount(); + return true; +} + +bool Ver4PatriciaTrieWritingHelper::truncateUnigrams( + const Ver4PatriciaTrieNodeReader *const ptNodeReader, + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, const int maxUnigramCount) { + const TerminalPositionLookupTable *const terminalPosLookupTable = + mBuffers->getTerminalPositionLookupTable(); + const int nextTerminalId = terminalPosLookupTable->getNextTerminalId(); + std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator> + priorityQueue; + for (int i = 0; i < nextTerminalId; ++i) { + const int terminalPos = terminalPosLookupTable->getTerminalPtNodePosition(i); + if (terminalPos == NOT_A_DICT_POS) { + continue; + } + const ProbabilityEntry probabilityEntry = + mBuffers->getProbabilityDictContent()->getProbabilityEntry(i); + const int probability = probabilityEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability(probabilityEntry.getHistoricalInfo()) : + probabilityEntry.getProbability(); + priorityQueue.push(DictProbability(terminalPos, probability, + probabilityEntry.getHistoricalInfo()->getTimeStamp())); + } + + // Delete unigrams. + while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) { + const int ptNodePos = priorityQueue.top().getDictPos(); + const PtNodeParams ptNodeParams = + ptNodeReader->fetchNodeInfoInBufferFromPtNodePos(ptNodePos); + if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) { + AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos); + return false; + } + priorityQueue.pop(); + } + return true; +} + +bool Ver4PatriciaTrieWritingHelper::truncateBigrams(const int maxBigramCount) { + const TerminalPositionLookupTable *const terminalPosLookupTable = + mBuffers->getTerminalPositionLookupTable(); + const int nextTerminalId = terminalPosLookupTable->getNextTerminalId(); + std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator> + priorityQueue; + BigramDictContent *const bigramDictContent = mBuffers->getMutableBigramDictContent(); + for (int i = 0; i < nextTerminalId; ++i) { + const int bigramListPos = bigramDictContent->getBigramListHeadPos(i); + if (bigramListPos == NOT_A_DICT_POS) { + continue; + } + bool hasNext = true; + int readingPos = bigramListPos; + while (hasNext) { + const int entryPos = readingPos; + const BigramEntry bigramEntry = + bigramDictContent->getBigramEntryAndAdvancePosition(&readingPos); + hasNext = bigramEntry.hasNext(); + if (!bigramEntry.isValid()) { + continue; + } + const int probability = bigramEntry.hasHistoricalInfo() ? + ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo()) : + bigramEntry.getProbability(); + priorityQueue.push(DictProbability(entryPos, probability, + bigramEntry.getHistoricalInfo()->getTimeStamp())); + } + } + + // Delete bigrams. + while (static_cast<int>(priorityQueue.size()) > maxBigramCount) { + const int entryPos = priorityQueue.top().getDictPos(); + const BigramEntry bigramEntry = bigramDictContent->getBigramEntry(entryPos); + const BigramEntry invalidatedBigramEntry = bigramEntry.getInvalidatedEntry(); + if (!bigramDictContent->writeBigramEntry(&invalidatedBigramEntry, entryPos)) { + AKLOGE("Cannot write bigram entry to remove. pos: %d", entryPos); + return false; + } + priorityQueue.pop(); + } + return true; +} + +bool Ver4PatriciaTrieWritingHelper::TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + ::onVisitingPtNode(const PtNodeParams *const ptNodeParams) { + if (!ptNodeParams->isTerminal()) { + return true; + } + TerminalPositionLookupTable::TerminalIdMap::const_iterator it = + mTerminalIdMap->find(ptNodeParams->getTerminalId()); + if (it == mTerminalIdMap->end()) { + AKLOGE("terminal Id %d is not in the terminal position map. map size: %zd", + ptNodeParams->getTerminalId(), mTerminalIdMap->size()); + return false; + } + if (!mPtNodeWriter->updateTerminalId(ptNodeParams, it->second)) { + AKLOGE("Cannot update terminal id. %d -> %d", it->first, it->second); + } + return mPtNodeWriter->updatePtNodeHasBigramsAndShortcutTargetsFlags(ptNodeParams); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h new file mode 100644 index 000000000..c3a155e0e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_VER4_PATRICIA_TRIE_WRITING_HELPER_H +#define LATINIME_VER4_PATRICIA_TRIE_WRITING_HELPER_H + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" + +namespace latinime { + +class HeaderPolicy; +class Ver4DictBuffers; +class Ver4PatriciaTrieNodeReader; +class Ver4PatriciaTrieNodeWriter; + +class Ver4PatriciaTrieWritingHelper { + public: + Ver4PatriciaTrieWritingHelper(Ver4DictBuffers *const buffers) + : mBuffers(buffers) {} + + void writeToDictFile(const char *const dictDirPath, const int unigramCount, + const int bigramCount) const; + + void writeToDictFileWithGC(const int rootPtNodeArrayPos, const char *const dictDirPath); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTrieWritingHelper); + + class TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds + : public DynamicPtReadingHelper::TraversingEventListener { + public: + TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds( + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap) + : mPtNodeWriter(ptNodeWriter), mTerminalIdMap(terminalIdMap) {} + + bool onAscend() { return true; } + + bool onDescend(const int ptNodeArrayPos) { return true; } + + bool onReadingPtNodeArrayTail() { return true; } + + bool onVisitingPtNode(const PtNodeParams *const ptNodeParams); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateAllPtNodeFlagsAndTerminalIds); + + Ver4PatriciaTrieNodeWriter *const mPtNodeWriter; + const TerminalPositionLookupTable::TerminalIdMap *const mTerminalIdMap; + }; + + // For truncateUnigrams() and truncateBigrams(). + class DictProbability { + public: + DictProbability(const int dictPos, const int probability, const int timestamp) + : mDictPos(dictPos), mProbability(probability), mTimestamp(timestamp) {} + + int getDictPos() const { + return mDictPos; + } + + int getProbability() const { + return mProbability; + } + + int getTimestamp() const { + return mTimestamp; + } + + private: + DISALLOW_DEFAULT_CONSTRUCTOR(DictProbability); + + int mDictPos; + int mProbability; + int mTimestamp; + }; + + // For truncateUnigrams() and truncateBigrams(). + class DictProbabilityComparator { + public: + bool operator()(const DictProbability &left, const DictProbability &right) { + if (left.getProbability() != right.getProbability()) { + return left.getProbability() > right.getProbability(); + } + if (left.getTimestamp() != right.getTimestamp()) { + return left.getTimestamp() < right.getTimestamp(); + } + return left.getDictPos() > right.getDictPos(); + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(DictProbabilityComparator); + }; + + bool runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, + Ver4DictBuffers *const buffersToWrite, int *const outUnigramCount, + int *const outBigramCount); + + bool truncateUnigrams(const Ver4PatriciaTrieNodeReader *const ptNodeReader, + Ver4PatriciaTrieNodeWriter *const ptNodeWriter, const int maxUnigramCount); + + bool truncateBigrams(const int maxBigramCount); + + Ver4DictBuffers *const mBuffers; +}; +} // namespace latinime + +#endif /* LATINIME_VER4_PATRICIA_TRIE_WRITING_HELPER_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 f692882f2..259dae4c6 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,11 +18,42 @@ namespace latinime { -const size_t BufferWithExtendableBuffer::MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 1024; +const size_t BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 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; +uint32_t BufferWithExtendableBuffer::readUint(const int size, const int pos) const { + const bool readingPosIsInAdditionalBuffer = isInAdditionalBuffer(pos); + const int posInBuffer = readingPosIsInAdditionalBuffer ? pos - mOriginalBufferSize : pos; + return ByteArrayUtils::readUint(getBuffer(readingPosIsInAdditionalBuffer), size, posInBuffer); +} + +uint32_t BufferWithExtendableBuffer::readUintAndAdvancePosition(const int size, + int *const pos) const { + const int value = readUint(size, *pos); + *pos += size; + return value; +} + +void BufferWithExtendableBuffer::readCodePointsAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoints, int *outCodePointCount, int *const pos) const { + const bool readingPosIsInAdditionalBuffer = isInAdditionalBuffer(*pos); + if (readingPosIsInAdditionalBuffer) { + *pos -= mOriginalBufferSize; + } + *outCodePointCount = ByteArrayUtils::readStringAndAdvancePosition( + getBuffer(readingPosIsInAdditionalBuffer), maxCodePointCount, outCodePoints, pos); + if (readingPosIsInAdditionalBuffer) { + *pos += mOriginalBufferSize; + } +} + +bool BufferWithExtendableBuffer::writeUint(const uint32_t data, const int size, const int pos) { + int writingPos = pos; + return writeUintAndAdvancePosition(data, size, &writingPos); +} + bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos) { if (!(size >= 1 && size <= 4)) { @@ -46,7 +77,7 @@ bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data } bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *const codePoints, - const int codePointCount, const bool writesTerminator ,int *const pos) { + const int codePointCount, const bool writesTerminator, int *const pos) { const size_t size = ByteArrayUtils::calculateRequiredByteCountToStoreCodePoints( codePoints, codePointCount, writesTerminator); if (!checkAndPrepareWriting(*pos, size)) { @@ -100,4 +131,21 @@ bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int return true; } +bool BufferWithExtendableBuffer::copy(const BufferWithExtendableBuffer *const sourceBuffer) { + int copyingPos = 0; + const int tailPos = sourceBuffer->getTailPosition(); + const int maxDataChunkSize = sizeof(uint32_t); + while (copyingPos < tailPos) { + const int remainingSize = tailPos - copyingPos; + const int copyingSize = (remainingSize >= maxDataChunkSize) ? + maxDataChunkSize : remainingSize; + const uint32_t data = sourceBuffer->readUint(copyingSize, copyingPos); + if (!writeUint(data, copyingSize, copyingPos)) { + return false; + } + copyingPos += copyingSize; + } + return true; +} + } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h index 9dc34823c..76be16518 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,12 +32,20 @@ namespace latinime { // raw pointer but provides several methods that handle boundary checking for writing data. class BufferWithExtendableBuffer { public: + static const size_t DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE; + BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize, - const int maxAdditionalBufferSize = MAX_ADDITIONAL_BUFFER_SIZE) + const int maxAdditionalBufferSize) : mOriginalBuffer(originalBuffer), mOriginalBufferSize(originalBufferSize), mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0), mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} + // Without original buffer. + BufferWithExtendableBuffer(const int maxAdditionalBufferSize) + : mOriginalBuffer(0), mOriginalBufferSize(0), + mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0), + mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} + AK_FORCE_INLINE int getTailPosition() const { return mOriginalBufferSize + mUsedAdditionalBufferSize; } @@ -63,6 +71,13 @@ class BufferWithExtendableBuffer { } } + uint32_t readUint(const int size, const int pos) const; + + uint32_t readUintAndAdvancePosition(const int size, int *const pos) const; + + void readCodePointsAndAdvancePosition(const int maxCodePointCount, + int *const outCodePoints, int *outCodePointCount, int *const pos) const; + AK_FORCE_INLINE int getOriginalBufferSize() const { return mOriginalBufferSize; } @@ -78,15 +93,18 @@ class BufferWithExtendableBuffer { * Writing is allowed for original buffer, already written region of additional buffer and the * tail of additional buffer. */ + bool writeUint(const uint32_t data, const int size, const int pos); + bool writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos); bool writeCodePointsAndAdvancePosition(const int *const codePoints, const int codePointCount, const bool writesTerminator, int *const pos); + bool copy(const BufferWithExtendableBuffer *const sourceBuffer); + private: DISALLOW_COPY_AND_ASSIGN(BufferWithExtendableBuffer); - 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; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h index 0c1576818..ebdd523e1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h @@ -114,6 +114,24 @@ class ByteArrayUtils { return buffer[(*pos)++]; } + static AK_FORCE_INLINE int readUint(const uint8_t *const buffer, + const int size, const int pos) { + // size must be in 1 to 4. + ASSERT(size >= 1 && size <= 4); + switch (size) { + case 1: + return ByteArrayUtils::readUint8(buffer, pos); + case 2: + return ByteArrayUtils::readUint16(buffer, pos); + case 3: + return ByteArrayUtils::readUint24(buffer, pos); + case 4: + return ByteArrayUtils::readUint32(buffer, pos); + default: + return 0; + } + } + /** * Code Point Reading * diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp index 994826fa8..442373b29 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp @@ -17,12 +17,14 @@ #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include <cstdio> -#include <cstring> #include "suggest/policyimpl/dictionary/header/header_policy.h" -#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" +#include "utils/time_keeper.h" namespace latinime { @@ -30,60 +32,81 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = /* static */ bool DictFileWritingUtils::createEmptyDictFile(const char *const filePath, const int dictVersion, const HeaderReadWriteUtils::AttributeMap *const attributeMap) { + TimeKeeper::setCurrentTime(); switch (dictVersion) { - case 3: - return createEmptyV3DictFile(filePath, attributeMap); + case FormatUtils::VERSION_4: + return createEmptyV4DictFile(filePath, attributeMap); default: - // Only version 3 dictionary is supported for now. + AKLOGE("Cannot create dictionary %s because format version %d is not supported.", + filePath, dictVersion); return false; } } -/* static */ bool DictFileWritingUtils::createEmptyV3DictFile(const char *const filePath, +/* static */ bool DictFileWritingUtils::createEmptyV4DictFile(const char *const dirPath, const HeaderReadWriteUtils::AttributeMap *const attributeMap) { - BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); - HeaderPolicy headerPolicy(FormatUtils::VERSION_3, attributeMap); - headerPolicy.writeHeaderToBuffer(&headerBuffer, true /* updatesLastUpdatedTime */, - true /* updatesLastDecayedTime */, 0 /* unigramCount */, 0 /* bigramCount */, - 0 /* extendedRegionSize */); - BufferWithExtendableBuffer bodyBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */); - if (!DynamicPatriciaTrieWritingUtils::writeEmptyDictionary(&bodyBuffer, 0 /* rootPos */)) { + HeaderPolicy headerPolicy(FormatUtils::VERSION_4, attributeMap); + Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = + Ver4DictBuffers::createVer4DictBuffers(&headerPolicy); + headerPolicy.writeHeaderToBuffer(dictBuffers.get()->getWritableHeaderBuffer(), + true /* updatesLastUpdatedTime */, true /* updatesLastDecayedTime */, + 0 /* unigramCount */, 0 /* bigramCount */, 0 /* extendedRegionSize */); + if (!DynamicPtWritingUtils::writeEmptyDictionary( + dictBuffers.get()->getWritableTrieBuffer(), 0 /* rootPos */)) { + AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); return false; } - return flushAllHeaderAndBodyToFile(filePath, &headerBuffer, &bodyBuffer); + return dictBuffers.get()->flush(dirPath); } /* static */ bool DictFileWritingUtils::flushAllHeaderAndBodyToFile(const char *const filePath, BufferWithExtendableBuffer *const dictHeader, BufferWithExtendableBuffer *const dictBody) { - const int tmpFileNameBufSize = strlen(filePath) - + strlen(TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE) + 1 /* terminator */; + const int tmpFileNameBufSize = FileUtils::getFilePathWithSuffixBufSize(filePath, + TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); // 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", filePath, - TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); - FILE *const file = fopen(tmpFileName, "wb"); - if (!file) { - AKLOGE("Dictionary file %s cannnot be opened.", tmpFileName); - ASSERT(false); + FileUtils::getFilePathWithSuffix(filePath, TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE, + tmpFileNameBufSize, tmpFileName); + if (!DictFileWritingUtils::flushBufferToFile(tmpFileName, dictHeader)) { + AKLOGE("Dictionary header cannot be written to %s.", tmpFileName); + return false; + } + if (!DictFileWritingUtils::flushBufferToFile(tmpFileName, dictBody)) { + AKLOGE("Dictionary structure cannot be written to %s.", tmpFileName); + return false; + } + if (rename(tmpFileName, filePath) != 0) { + AKLOGE("Dictionary file %s cannot be renamed to %s", tmpFileName, filePath);; return false; } - // Write the dictionary header. - if (!writeBufferToFile(file, dictHeader)) { - remove(tmpFileName); - AKLOGE("Dictionary header cannnot be written. size: %d", dictHeader->getTailPosition()); + return true; +} + +/* static */ bool DictFileWritingUtils::flushBufferToFileWithSuffix(const char *const basePath, + const char *const suffix, const BufferWithExtendableBuffer *const buffer) { + const int filePathBufSize = FileUtils::getFilePathWithSuffixBufSize(basePath, suffix); + char filePath[filePathBufSize]; + FileUtils::getFilePathWithSuffix(basePath, suffix, filePathBufSize, filePath); + return flushBufferToFile(filePath, buffer); +} + +/* static */ bool DictFileWritingUtils::flushBufferToFile(const char *const filePath, + const BufferWithExtendableBuffer *const buffer) { + FILE *const file = fopen(filePath, "wb"); + if (!file) { + AKLOGE("File %s cannot be opened.", filePath); ASSERT(false); return false; } - // Write the dictionary body. - if (!writeBufferToFile(file, dictBody)) { - remove(tmpFileName); - AKLOGE("Dictionary body cannnot be written. size: %d", dictBody->getTailPosition()); + if (!writeBufferToFile(file, buffer)) { + remove(filePath); + AKLOGE("Buffer cannot be written to the file %s. size: %d", filePath, + buffer->getTailPosition()); ASSERT(false); return false; } fclose(file); - rename(tmpFileName, filePath); return true; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h index bd4ac66fd..bdf9fd63c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h @@ -28,6 +28,8 @@ class BufferWithExtendableBuffer; class DictFileWritingUtils { public: + static const char *const TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE; + static bool createEmptyDictFile(const char *const filePath, const int dictVersion, const HeaderReadWriteUtils::AttributeMap *const attributeMap); @@ -35,14 +37,18 @@ class DictFileWritingUtils { BufferWithExtendableBuffer *const dictHeader, BufferWithExtendableBuffer *const dictBody); + static bool flushBufferToFileWithSuffix(const char *const basePath, const char *const suffix, + const BufferWithExtendableBuffer *const buffer); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictFileWritingUtils); - static const char *const TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE; - - static bool createEmptyV3DictFile(const char *const filePath, + static bool createEmptyV4DictFile(const char *const filePath, const HeaderReadWriteUtils::AttributeMap *const attributeMap); + static bool flushBufferToFile(const char *const filePath, + const BufferWithExtendableBuffer *const buffer); + static bool writeBufferToFile(FILE *const file, const BufferWithExtendableBuffer *const buffer); }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp new file mode 100644 index 000000000..1f25cfa1e --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/utils/file_utils.h" + +#include <cstdio> +#include <cstring> +#include <dirent.h> +#include <fcntl.h> +#include <libgen.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +namespace latinime { + +// Returns -1 on error. +/* static */ int FileUtils::getFileSize(const char *const filePath) { + const int fd = open(filePath, O_RDONLY); + if (fd == -1) { + return -1; + } + struct stat statBuf; + if (fstat(fd, &statBuf) != 0) { + close(fd); + return -1; + } + close(fd); + return static_cast<int>(statBuf.st_size); +} + +/* static */ bool FileUtils::existsDir(const char *const dirPath) { + DIR *const dir = opendir(dirPath); + if (dir == NULL) { + return false; + } + closedir(dir); + return true; +} + +// Remove a directory and all files in the directory. +/* static */ bool FileUtils::removeDirAndFiles(const char *const dirPath) { + DIR *const dir = opendir(dirPath); + if (dir == NULL) { + AKLOGE("Cannot open dir %s.", dirPath); + return true; + } + struct dirent *dirent; + while ((dirent = readdir(dir)) != NULL) { + if (dirent->d_type != DT_REG) { + continue; + } + const int filePathBufSize = getFilePathBufSize(dirPath, dirent->d_name); + char filePath[filePathBufSize]; + getFilePath(dirPath, dirent->d_name, filePathBufSize, filePath); + if (remove(filePath) != 0) { + AKLOGE("Cannot remove file %s.", filePath); + closedir(dir); + return false; + } + } + closedir(dir); + if (remove(dirPath) != 0) { + AKLOGE("Cannot remove directory %s.", dirPath); + return false; + } + return true; +} + +/* static */ int FileUtils::getFilePathWithSuffixBufSize(const char *const filePath, + const char *const suffix) { + return strlen(filePath) + strlen(suffix) + 1 /* terminator */; +} + +/* static */ void FileUtils::getFilePathWithSuffix(const char *const filePath, + const char *const suffix, const int filePathBufSize, char *const outFilePath) { + snprintf(outFilePath, filePathBufSize, "%s%s", filePath, suffix); +} + +/* static */ int FileUtils::getFilePathBufSize(const char *const dirPath, + const char *const fileName) { + return strlen(dirPath) + 1 /* '/' */ + strlen(fileName) + 1 /* terminator */; +} + +/* static */ void FileUtils::getFilePath(const char *const dirPath, const char *const fileName, + const int filePathBufSize, char *const outFilePath) { + snprintf(outFilePath, filePathBufSize, "%s/%s", dirPath, fileName); +} + +/* static */ bool FileUtils::getFilePathWithoutSuffix(const char *const filePath, + const char *const suffix, const int outDirPathBufSize, char *const outDirPath) { + const int filePathLength = strlen(filePath); + const int suffixLength = strlen(suffix); + if (filePathLength <= suffixLength) { + AKLOGE("File path length (%s:%d) is shorter that suffix length (%s:%d).", + filePath, filePathLength, suffix, suffixLength); + return false; + } + const int resultFilePathLength = filePathLength - suffixLength; + if (outDirPathBufSize <= resultFilePathLength) { + AKLOGE("outDirPathBufSize is too small. filePath: %s, suffix: %s, outDirPathBufSize: %d", + filePath, suffix, outDirPathBufSize); + return false; + } + if (strncmp(filePath + resultFilePathLength, suffix, suffixLength) != 0) { + AKLOGE("File Path %s does not have %s as a suffix", filePath, suffix); + return false; + } + snprintf(outDirPath, resultFilePathLength + 1 /* terminator */, "%s", filePath); + return true; +} + +/* static */ void FileUtils::getDirPath(const char *const filePath, const int outDirPathBufSize, + char *const outDirPath) { + for (int i = strlen(filePath) - 1; i >= 0; --i) { + if (filePath[i] == '/') { + if (i >= outDirPathBufSize) { + AKLOGE("outDirPathBufSize is too small. filePath: %s, outDirPathBufSize: %d", + filePath, outDirPathBufSize); + ASSERT(false); + return; + } + snprintf(outDirPath, i + 1 /* terminator */, "%s", filePath); + return; + } + } +} + +/* static */ void FileUtils::getBasename(const char *const filePath, + const int outNameBufSize, char *const outName) { + const int filePathBufSize = strlen(filePath) + 1 /* terminator */; + char filePathBuf[filePathBufSize]; + snprintf(filePathBuf, filePathBufSize, "%s", filePath); + const char *const baseName = basename(filePathBuf); + const int baseNameLength = strlen(baseName); + if (baseNameLength >= outNameBufSize) { + AKLOGE("outNameBufSize is too small. filePath: %s, outNameBufSize: %d", + filePath, outNameBufSize); + return; + } + snprintf(outName, baseNameLength + 1 /* terminator */, "%s", baseName); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h new file mode 100644 index 000000000..3e84a3038 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h @@ -0,0 +1,58 @@ +/* + * 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_FILE_UTILS_H +#define LATINIME_FILE_UTILS_H + +#include "defines.h" + +namespace latinime { + +class FileUtils { + public: + // Returns -1 on error. + static int getFileSize(const char *const filePath); + + static bool existsDir(const char *const dirPath); + + // Remove a directory and all files in the directory. + static bool removeDirAndFiles(const char *const dirPath); + + static int getFilePathWithSuffixBufSize(const char *const filePath, const char *const suffix); + + static void getFilePathWithSuffix(const char *const filePath, const char *const suffix, + const int filePathBufSize, char *const outFilePath); + + static int getFilePathBufSize(const char *const dirPath, const char *const fileName); + + static void getFilePath(const char *const dirPath, const char *const fileName, + const int filePathBufSize, char *const outFilePath); + + // Returns whether the filePath have the suffix. + static bool getFilePathWithoutSuffix(const char *const filePath, const char *const suffix, + const int dirPathBufSize, char *const outDirPath); + + static void getDirPath(const char *const filePath, const int dirPathBufSize, + char *const outDirPath); + + static void getBasename(const char *const filePath, const int outNameBufSize, + char *const outName); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtils); +}; +} // namespace latinime +#endif /* LATINIME_FILE_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp index 1632fd072..4050ad363 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ +#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" + #include <cmath> -#include <ctime> #include <stdlib.h> -#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" - #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" +#include "utils/time_keeper.h" namespace latinime { @@ -31,76 +31,86 @@ const int ForgettingCurveUtils::MAX_BIGRAM_COUNT = 12000; const int ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000; const int ForgettingCurveUtils::MAX_COMPUTED_PROBABILITY = 127; -const int ForgettingCurveUtils::MAX_ENCODED_PROBABILITY = 15; -const int ForgettingCurveUtils::MIN_VALID_ENCODED_PROBABILITY = 3; -const int ForgettingCurveUtils::ENCODED_PROBABILITY_STEP = 1; -// Currently, we try to decay each uni/bigram once every 2 hours. Accordingly, the expected -// duration of the decay is approximately 66hours. -const float ForgettingCurveUtils::MIN_PROBABILITY_TO_DECAY = 0.03f; const int ForgettingCurveUtils::DECAY_INTERVAL_SECONDS = 2 * 60 * 60; -const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable; -ForgettingCurveUtils::TimeKeeper ForgettingCurveUtils::sTimeKeeper; +const int ForgettingCurveUtils::MAX_LEVEL = 3; +const int ForgettingCurveUtils::MAX_COUNT = 3; +const int ForgettingCurveUtils::MIN_VALID_LEVEL = 1; +const int ForgettingCurveUtils::TIME_STEP_DURATION_IN_SECONDS = 6 * 60 * 60; +const int ForgettingCurveUtils::MAX_ELAPSED_TIME_STEP_COUNT = 15; +const int ForgettingCurveUtils::DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD = 14; -void ForgettingCurveUtils::TimeKeeper::setCurrentTime() { - mCurrentTime = time(0); -} +const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable; -/* static */ int ForgettingCurveUtils::getProbability(const int encodedUnigramProbability, - const int encodedBigramProbability) { - if (encodedUnigramProbability == NOT_A_PROBABILITY) { - return NOT_A_PROBABILITY; - } else if (encodedBigramProbability == NOT_A_PROBABILITY) { - return backoff(decodeProbability(encodedUnigramProbability)); +/* static */ const HistoricalInfo ForgettingCurveUtils::createUpdatedHistoricalInfo( + const HistoricalInfo *const originalHistoricalInfo, + const int newProbability, const int timestamp) { + if (newProbability != NOT_A_PROBABILITY && originalHistoricalInfo->getLevel() == 0) { + return HistoricalInfo(timestamp, MIN_VALID_LEVEL /* level */, 0 /* count */); + } else if (!originalHistoricalInfo->isValid()) { + // Initial information. + return HistoricalInfo(timestamp, 0 /* level */, 1 /* count */); } else { - const int unigramProbability = decodeProbability(encodedUnigramProbability); - const int bigramProbability = decodeProbability(encodedBigramProbability); - return min(max(unigramProbability, bigramProbability), MAX_COMPUTED_PROBABILITY); + const int updatedCount = originalHistoricalInfo->getCount() + 1; + if (updatedCount > MAX_COUNT) { + // The count exceeds the max value the level can be incremented. + if (originalHistoricalInfo->getLevel() >= MAX_LEVEL) { + // The level is already max. + return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel(), + originalHistoricalInfo->getCount()); + } else { + // Level up. + return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel() + 1, + 0 /* count */); + } + } else { + return HistoricalInfo(timestamp, originalHistoricalInfo->getLevel(), updatedCount); + } } } -// Caveat: Unlike getProbability(), this method doesn't assume special bigram probability encoding -// (i.e. unigram probability + bigram probability delta). -/* static */ int ForgettingCurveUtils::getUpdatedEncodedProbability( - const int originalEncodedProbability, const int newProbability) { - if (originalEncodedProbability == NOT_A_PROBABILITY) { - // The bigram relation is not in this dictionary. - if (newProbability == NOT_A_PROBABILITY) { - // The bigram target is not in other dictionaries. - return 0; - } else { - return MIN_VALID_ENCODED_PROBABILITY; - } +/* static */ int ForgettingCurveUtils::decodeProbability( + const HistoricalInfo *const historicalInfo) { + const int elapsedTimeStepCount = getElapsedTimeStepCount(historicalInfo->getTimeStamp()); + return sProbabilityTable.getProbability(historicalInfo->getLevel(), + min(max(elapsedTimeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT)); +} + +/* static */ int ForgettingCurveUtils::getProbability(const int unigramProbability, + const int bigramProbability) { + if (unigramProbability == NOT_A_PROBABILITY) { + return NOT_A_PROBABILITY; + } else if (bigramProbability == NOT_A_PROBABILITY) { + return min(backoff(unigramProbability), MAX_COMPUTED_PROBABILITY); } else { - if (newProbability != NOT_A_PROBABILITY - && originalEncodedProbability < MIN_VALID_ENCODED_PROBABILITY) { - return MIN_VALID_ENCODED_PROBABILITY; - } - return min(originalEncodedProbability + ENCODED_PROBABILITY_STEP, MAX_ENCODED_PROBABILITY); + return min(max(unigramProbability, bigramProbability), MAX_COMPUTED_PROBABILITY); } } -/* static */ int ForgettingCurveUtils::isValidEncodedProbability(const int encodedProbability) { - return encodedProbability >= MIN_VALID_ENCODED_PROBABILITY; +/* static */ bool ForgettingCurveUtils::needsToKeep(const HistoricalInfo *const historicalInfo) { + return historicalInfo->getLevel() > 0 + || getElapsedTimeStepCount(historicalInfo->getTimeStamp()) + < DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD; } -/* static */ int ForgettingCurveUtils::getEncodedProbabilityToSave(const int encodedProbability, - const DictionaryHeaderStructurePolicy *const headerPolicy) { - const int elapsedTime = sTimeKeeper.peekCurrentTime() - headerPolicy->getLastDecayedTime(); - const int decayIterationCount = max(elapsedTime / DECAY_INTERVAL_SECONDS, 1); - int currentEncodedProbability = max(min(encodedProbability, MAX_ENCODED_PROBABILITY), 0); - // TODO: Implement the decay in more proper way. - for (int i = 0; i < decayIterationCount; ++i) { - const float currentRate = static_cast<float>(currentEncodedProbability) - / static_cast<float>(MAX_ENCODED_PROBABILITY); - const float thresholdToDecay = (1.0f - MIN_PROBABILITY_TO_DECAY) * currentRate; - const float randValue = static_cast<float>(rand()) / static_cast<float>(RAND_MAX); - if (thresholdToDecay < randValue) { - currentEncodedProbability = max(currentEncodedProbability - ENCODED_PROBABILITY_STEP, - 0); - } +/* static */ const HistoricalInfo ForgettingCurveUtils::createHistoricalInfoToSave( + const HistoricalInfo *const originalHistoricalInfo) { + if (originalHistoricalInfo->getTimeStamp() == NOT_A_TIMESTAMP) { + return HistoricalInfo(); } - return currentEncodedProbability; + const int elapsedTimeStep = getElapsedTimeStepCount(originalHistoricalInfo->getTimeStamp()); + if (elapsedTimeStep <= MAX_ELAPSED_TIME_STEP_COUNT) { + // No need to update historical info. + return *originalHistoricalInfo; + } + // Level down. + const int maxLevelDownAmonut = elapsedTimeStep / (MAX_ELAPSED_TIME_STEP_COUNT + 1); + const int levelDownAmount = (maxLevelDownAmonut >= originalHistoricalInfo->getLevel()) ? + originalHistoricalInfo->getLevel() : maxLevelDownAmonut; + const int adjustedTimestamp = originalHistoricalInfo->getTimeStamp() + + levelDownAmount * (MAX_ELAPSED_TIME_STEP_COUNT + 1) * TIME_STEP_DURATION_IN_SECONDS; + return HistoricalInfo(adjustedTimestamp, + originalHistoricalInfo->getLevel() - levelDownAmount, 0 /* count */); } /* static */ bool ForgettingCurveUtils::needsToDecay(const bool mindsBlockByDecay, @@ -116,21 +126,14 @@ void ForgettingCurveUtils::TimeKeeper::setCurrentTime() { if (mindsBlockByDecay) { return false; } - if (headerPolicy->getLastDecayedTime() + DECAY_INTERVAL_SECONDS < time(0)) { + if (headerPolicy->getLastDecayedTime() + DECAY_INTERVAL_SECONDS + < TimeKeeper::peekCurrentTime()) { // Time to decay. return true; } return false; } -/* static */ int ForgettingCurveUtils::decodeProbability(const int encodedProbability) { - if (encodedProbability < MIN_VALID_ENCODED_PROBABILITY) { - return NOT_A_PROBABILITY; - } else { - return min(sProbabilityTable.getProbability(encodedProbability), MAX_ENCODED_PROBABILITY); - } -} - // See comments in ProbabilityUtils::backoff(). /* static */ int ForgettingCurveUtils::backoff(const int unigramProbability) { if (unigramProbability == NOT_A_PROBABILITY) { @@ -140,15 +143,29 @@ void ForgettingCurveUtils::TimeKeeper::setCurrentTime() { } } +/* static */ int ForgettingCurveUtils::getElapsedTimeStepCount(const int timestamp) { + return (TimeKeeper::peekCurrentTime() - timestamp) / TIME_STEP_DURATION_IN_SECONDS; +} + ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTable() { - // Table entry is as follows: - // 1, 1, 1, 2, 3, 5, 6, 9, 13, 18, 25, 34, 48, 66, 91, 127. - // Note that first MIN_VALID_ENCODED_PROBABILITY values are not used. - mTable.resize(MAX_ENCODED_PROBABILITY + 1); - for (int i = 0; i <= MAX_ENCODED_PROBABILITY; ++i) { - const int probability = static_cast<int>(powf(static_cast<float>(MAX_COMPUTED_PROBABILITY), - static_cast<float>(i) / static_cast<float>(MAX_ENCODED_PROBABILITY))); - mTable[i] = min(MAX_COMPUTED_PROBABILITY, max(0, probability)); + mTable.resize(MAX_LEVEL + 1); + for (int level = 0; level <= MAX_LEVEL; ++level) { + mTable[level].resize(MAX_ELAPSED_TIME_STEP_COUNT + 1); + const float initialProbability = + static_cast<float>(MAX_COMPUTED_PROBABILITY / (1 << (MAX_LEVEL - level))); + for (int timeStepCount = 0; timeStepCount <= MAX_ELAPSED_TIME_STEP_COUNT; ++timeStepCount) { + if (level == 0) { + mTable[level][timeStepCount] = NOT_A_PROBABILITY; + continue; + } + const int elapsedTime = timeStepCount * TIME_STEP_DURATION_IN_SECONDS; + const float probability = initialProbability + * powf(2.0f, -1.0f * static_cast<float>(elapsedTime) + / static_cast<float>(TIME_STEP_DURATION_IN_SECONDS + * (MAX_ELAPSED_TIME_STEP_COUNT + 1))); + mTable[level][timeStepCount] = + min(max(static_cast<int>(probability), 1), MAX_COMPUTED_PROBABILITY); + } } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h index 2ad423874..6ac8dc528 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h @@ -20,45 +20,33 @@ #include <vector> #include "defines.h" +#include "suggest/policyimpl/dictionary/utils/historical_info.h" namespace latinime { class DictionaryHeaderStructurePolicy; -// TODO: Check the elapsed time and decrease the probability depending on the time. Time field is -// required to introduced to each terminal PtNode and bigram entry. // TODO: Quit using bigram probability to indicate the delta. class ForgettingCurveUtils { public: - class TimeKeeper { - public: - TimeKeeper() : mCurrentTime(0) {} - void setCurrentTime(); - int peekCurrentTime() const { return mCurrentTime; }; - - private: - DISALLOW_COPY_AND_ASSIGN(TimeKeeper); - - int mCurrentTime; - }; - static const int MAX_UNIGRAM_COUNT; static const int MAX_UNIGRAM_COUNT_AFTER_GC; static const int MAX_BIGRAM_COUNT; static const int MAX_BIGRAM_COUNT_AFTER_GC; - static TimeKeeper sTimeKeeper; + static const HistoricalInfo createUpdatedHistoricalInfo( + const HistoricalInfo *const originalHistoricalInfo, const int newProbability, + const int timestamp); - static int getProbability(const int encodedUnigramProbability, - const int encodedBigramProbability); + static const HistoricalInfo createHistoricalInfoToSave( + const HistoricalInfo *const originalHistoricalInfo); - static int getUpdatedEncodedProbability(const int originalEncodedProbability, - const int newProbability); + static int decodeProbability(const HistoricalInfo *const historicalInfo); - static int isValidEncodedProbability(const int encodedProbability); + static int getProbability(const int encodedUnigramProbability, + const int encodedBigramProbability); - static int getEncodedProbabilityToSave(const int encodedProbability, - const DictionaryHeaderStructurePolicy *const headerPolicy); + static bool needsToKeep(const HistoricalInfo *const historicalInfo); static bool needsToDecay(const bool mindsBlockByDecay, const int unigramCount, const int bigramCount, const DictionaryHeaderStructurePolicy *const headerPolicy); @@ -70,31 +58,32 @@ class ForgettingCurveUtils { public: ProbabilityTable(); - int getProbability(const int encodedProbability) const { - if (encodedProbability < 0 || encodedProbability > static_cast<int>(mTable.size())) { - return NOT_A_PROBABILITY; - } - return mTable[encodedProbability]; + int getProbability(const int level, const int elapsedTimeStepCount) const { + return mTable[level][elapsedTimeStepCount]; } private: DISALLOW_COPY_AND_ASSIGN(ProbabilityTable); - std::vector<int> mTable; + std::vector<std::vector<int> > mTable; }; static const int MAX_COMPUTED_PROBABILITY; - static const int MAX_ENCODED_PROBABILITY; - static const int MIN_VALID_ENCODED_PROBABILITY; - static const int ENCODED_PROBABILITY_STEP; - static const float MIN_PROBABILITY_TO_DECAY; static const int DECAY_INTERVAL_SECONDS; - static const ProbabilityTable sProbabilityTable; + static const int MAX_LEVEL; + static const int MAX_COUNT; + static const int MIN_VALID_LEVEL; + static const int TIME_STEP_DURATION_IN_SECONDS; + static const int MAX_ELAPSED_TIME_STEP_COUNT; + static const int DISCARD_LEVEL_ZERO_ENTRY_TIME_STEP_COUNT_THRESHOLD; + static const int HALF_LIFE_TIME_IN_SECONDS; - static int decodeProbability(const int encodedProbability); + static const ProbabilityTable sProbabilityTable; static int backoff(const int unigramProbability); + + static int getElapsedTimeStepCount(const int timestamp); }; } // namespace latinime #endif /* LATINIME_FORGETTING_CURVE_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp index 1d77d5c27..cd3c403fa 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -41,10 +41,13 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; // Dictionary format version number (2 bytes) // Options (2 bytes) // Header size (4 bytes) : integer, big endian - if (ByteArrayUtils::readUint16(dict, 4) == 2) { + // Conceptually this converts the hardcoded value of the bytes in the file into + // the symbolic value we use in the code. But we want the constants to be the + // same so we use them for both here. + if (ByteArrayUtils::readUint16(dict, 4) == VERSION_2) { return VERSION_2; - } else if (ByteArrayUtils::readUint16(dict, 4) == 3) { - return VERSION_3; + } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4) { + return VERSION_4; } else { return UNKNOWN_VERSION; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h index 79ed0de29..eb2227d60 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -29,9 +29,10 @@ namespace latinime { class FormatUtils { public: enum FORMAT_VERSION { - VERSION_2, - VERSION_3, - UNKNOWN_VERSION + // These MUST have the same values as the relevant constants in FormatSpec.java. + VERSION_2 = 2, + VERSION_4 = 400, + UNKNOWN_VERSION = -1 }; // 32 bit magic number is stored at the beginning of the dictionary header to reject diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h b/native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h new file mode 100644 index 000000000..428ca8626 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/historical_info.h @@ -0,0 +1,58 @@ +/* + * 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_HISTORICAL_INFO_H +#define LATINIME_HISTORICAL_INFO_H + +#include "defines.h" + +namespace latinime { + +class HistoricalInfo { + public: + // Invalid historical info. + HistoricalInfo() + : mTimestamp(NOT_A_TIMESTAMP), mLevel(0), mCount(0) {} + + HistoricalInfo(const int timestamp, const int level, const int count) + : mTimestamp(timestamp), mLevel(level), mCount(count) {} + + bool isValid() const { + return mTimestamp != NOT_A_TIMESTAMP; + } + + int getTimeStamp() const { + return mTimestamp; + } + + int getLevel() const { + return mLevel; + } + + int getCount() const { + return mCount; + } + + private: + // Copy constructor is public to use this class as a type of return value. + DISALLOW_ASSIGNMENT_OPERATOR(HistoricalInfo); + + const int mTimestamp; + const int mLevel; + const int mCount; +}; +} // namespace latinime +#endif /* LATINIME_HISTORICAL_INFO_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp new file mode 100644 index 000000000..e88d6e0a9 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" + +#include <cerrno> +#include <climits> +#include <cstdio> +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "suggest/policyimpl/dictionary/utils/file_utils.h" + +namespace latinime { + +/* static */ MmappedBuffer::MmappedBufferPtr MmappedBuffer::openBuffer( + const char *const path, const int bufferOffset, const int bufferSize, + const bool isUpdatable) { + const int mmapFd = open(path, O_RDONLY); + if (mmapFd < 0) { + AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno); + return MmappedBufferPtr(0); + } + const int pagesize = sysconf(_SC_PAGESIZE); + const int offset = bufferOffset % pagesize; + int alignedOffset = bufferOffset - offset; + int alignedSize = bufferSize + offset; + const int protMode = isUpdatable ? PROT_READ | PROT_WRITE : PROT_READ; + void *const mmappedBuffer = mmap(0, alignedSize, protMode, MAP_PRIVATE, mmapFd, + alignedOffset); + if (mmappedBuffer == MAP_FAILED) { + AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); + close(mmapFd); + return MmappedBufferPtr(0); + } + uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset; + if (!buffer) { + AKLOGE("DICT: buffer is null"); + close(mmapFd); + return MmappedBufferPtr(0); + } + return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, + mmapFd, isUpdatable)); +} + +/* static */ MmappedBuffer::MmappedBufferPtr MmappedBuffer::openBuffer( + const char *const path, const bool isUpdatable) { + const int fileSize = FileUtils::getFileSize(path); + if (fileSize == -1) { + return MmappedBufferPtr(0); + } else if (fileSize == 0) { + return MmappedBufferPtr(new MmappedBuffer(isUpdatable)); + } else { + return openBuffer(path, 0 /* bufferOffset */, fileSize, isUpdatable); + } +} + +/* static */ MmappedBuffer::MmappedBufferPtr MmappedBuffer::openBuffer( + const char *const dirPath, const char *const fileName, const bool isUpdatable) { + const int filePathBufferSize = PATH_MAX + 1 /* terminator */; + char filePath[filePathBufferSize]; + const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath, + fileName); + if (filePathLength >= filePathBufferSize) { + return 0; + } + return openBuffer(filePath, isUpdatable); +} + +MmappedBuffer::~MmappedBuffer() { + if (mAlignedSize == 0) { + return; + } + int ret = munmap(mMmappedBuffer, mAlignedSize); + if (ret != 0) { + AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno); + } + ret = close(mMmapFd); + if (ret != 0) { + AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno); + } +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h index 6b69116eb..73a733b0c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h @@ -17,58 +17,27 @@ #ifndef LATINIME_MMAPPED_BUFFER_H #define LATINIME_MMAPPED_BUFFER_H -#include <cerrno> -#include <fcntl.h> #include <stdint.h> -#include <sys/mman.h> -#include <unistd.h> #include "defines.h" +#include "utils/exclusive_ownership_pointer.h" namespace latinime { class MmappedBuffer { public: - static MmappedBuffer* openBuffer(const char *const path, const int bufferOffset, - const int bufferSize, const bool isUpdatable) { - const int openMode = isUpdatable ? O_RDWR : O_RDONLY; - const int mmapFd = open(path, openMode); - if (mmapFd < 0) { - AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno); - return 0; - } - const int pagesize = getpagesize(); - const int offset = bufferOffset % pagesize; - int alignedOffset = bufferOffset - offset; - int alignedSize = bufferSize + offset; - const int protMode = isUpdatable ? PROT_READ | PROT_WRITE : PROT_READ; - void *const mmappedBuffer = mmap(0, alignedSize, protMode, MAP_PRIVATE, mmapFd, - alignedOffset); - if (mmappedBuffer == MAP_FAILED) { - AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); - close(mmapFd); - return 0; - } - uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset; - if (!buffer) { - AKLOGE("DICT: buffer is null"); - close(mmapFd); - return 0; - } - return new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, mmapFd, - isUpdatable); - } + typedef ExclusiveOwnershipPointer<MmappedBuffer> MmappedBufferPtr; - ~MmappedBuffer() { - int ret = munmap(mMmappedBuffer, mAlignedSize); - if (ret != 0) { - AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno); - } - ret = close(mMmapFd); - if (ret != 0) { - AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno); - } - } + static MmappedBufferPtr openBuffer(const char *const path, + const int bufferOffset, const int bufferSize, const bool isUpdatable); + + // Mmap entire file. + static MmappedBufferPtr openBuffer(const char *const path, const bool isUpdatable); + + static MmappedBufferPtr openBuffer(const char *const dirPath, const char *const fileName, + const bool isUpdatable); + + ~MmappedBuffer(); AK_FORCE_INLINE uint8_t *getBuffer() const { return mBuffer; @@ -89,6 +58,11 @@ class MmappedBuffer { : mBuffer(buffer), mBufferSize(bufferSize), mMmappedBuffer(mmappedBuffer), mAlignedSize(alignedSize), mMmapFd(mmapFd), mIsUpdatable(isUpdatable) {} + // Empty file. We have to handle an empty file as a valid part of a dictionary. + AK_FORCE_INLINE MmappedBuffer(const bool isUpdatable) + : mBuffer(0), mBufferSize(0), mMmappedBuffer(0), mAlignedSize(0), mMmapFd(0), + mIsUpdatable(isUpdatable) {} + DISALLOW_IMPLICIT_CONSTRUCTORS(MmappedBuffer); uint8_t *const mBuffer; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp new file mode 100644 index 000000000..4ad82f9f7 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/utils/sparse_table.h" + +namespace latinime { + +const int SparseTable::NOT_EXIST = -1; +const int SparseTable::INDEX_SIZE = 4; + +bool SparseTable::contains(const int id) const { + const int readingPos = getPosInIndexTable(id); + if (id < 0 || mIndexTableBuffer->getTailPosition() <= readingPos) { + return false; + } + const int index = mIndexTableBuffer->readUint(INDEX_SIZE, readingPos); + return index != NOT_EXIST; +} + +uint32_t SparseTable::get(const int id) const { + const int indexTableReadingPos = getPosInIndexTable(id); + const int index = mIndexTableBuffer->readUint(INDEX_SIZE, indexTableReadingPos); + const int contentTableReadingPos = getPosInContentTable(id, index); + return mContentTableBuffer->readUint(mDataSize, contentTableReadingPos); +} + +bool SparseTable::set(const int id, const uint32_t value) { + const int posInIndexTable = getPosInIndexTable(id); + // Extends the index table if needed. + if (mIndexTableBuffer->getTailPosition() < posInIndexTable) { + int tailPos = mIndexTableBuffer->getTailPosition(); + while(tailPos < posInIndexTable) { + if (!mIndexTableBuffer->writeUintAndAdvancePosition(NOT_EXIST, INDEX_SIZE, &tailPos)) { + AKLOGE("cannot extend index table. tailPos: %d to: %d", tailPos, posInIndexTable); + return false; + } + } + } + if (contains(id)) { + // The entry is already in the content table. + const int index = mIndexTableBuffer->readUint(INDEX_SIZE, posInIndexTable); + if (!mContentTableBuffer->writeUint(value, mDataSize, getPosInContentTable(id, index))) { + AKLOGE("cannot update value %d. pos: %d, tailPos: %d, mDataSize: %d", value, + getPosInContentTable(id, index), mContentTableBuffer->getTailPosition(), + mDataSize); + return false; + } + return true; + } + // The entry is not in the content table. + // Create new entry in the content table. + const int index = getIndexFromContentTablePos(mContentTableBuffer->getTailPosition()); + if (!mIndexTableBuffer->writeUint(index, INDEX_SIZE, posInIndexTable)) { + AKLOGE("cannot write index %d. pos %d", index, posInIndexTable); + return false; + } + // Write a new block that containing the entry to be set. + int writingPos = getPosInContentTable(0 /* id */, index); + for (int i = 0; i < mBlockSize; ++i) { + if (!mContentTableBuffer->writeUintAndAdvancePosition(NOT_A_DICT_POS, mDataSize, + &writingPos)) { + AKLOGE("cannot write content table to extend. writingPos: %d, tailPos: %d, " + "mDataSize: %d", writingPos, mContentTableBuffer->getTailPosition(), mDataSize); + return false; + } + } + return mContentTableBuffer->writeUint(value, mDataSize, getPosInContentTable(id, index)); +} + +int SparseTable::getIndexFromContentTablePos(const int contentTablePos) const { + return contentTablePos / mDataSize / mBlockSize; +} + +int SparseTable::getPosInIndexTable(const int id) const { + return (id / mBlockSize) * INDEX_SIZE; +} + +int SparseTable::getPosInContentTable(const int id, const int index) const { + const int offset = id % mBlockSize; + return (index * mBlockSize + offset) * mDataSize; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.h b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.h new file mode 100644 index 000000000..21c167506 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/sparse_table.h @@ -0,0 +1,61 @@ +/* + * 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_SPARSE_TABLE_H +#define LATINIME_SPARSE_TABLE_H + +#include <stdint.h> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +// Note that there is a corresponding implementation in SparseTable.java. +// TODO: Support multiple content buffers. +class SparseTable { + public: + SparseTable(BufferWithExtendableBuffer *const indexTableBuffer, + BufferWithExtendableBuffer *const contentTableBuffer, const int blockSize, + const int dataSize) + : mIndexTableBuffer(indexTableBuffer), mContentTableBuffer(contentTableBuffer), + mBlockSize(blockSize), mDataSize(dataSize) {} + + bool contains(const int id) const; + + uint32_t get(const int id) const; + + bool set(const int id, const uint32_t value); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SparseTable); + + int getIndexFromContentTablePos(const int contentTablePos) const; + + int getPosInIndexTable(const int id) const; + + int getPosInContentTable(const int id, const int index) const; + + static const int NOT_EXIST; + static const int INDEX_SIZE; + + BufferWithExtendableBuffer *const mIndexTableBuffer; + BufferWithExtendableBuffer *const mContentTableBuffer; + const int mBlockSize; + const int mDataSize; +}; +} // namespace latinime +#endif /* LATINIME_SPARSE_TABLE_H */ diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index 007c19e0a..fd0ac9eb6 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -81,7 +81,7 @@ class TypingTraversal : public Traversal { return false; } const int point0Index = dicNode->getInputIndex(0); - return dicNode->isTerminalWordNode() + return dicNode->isTerminalDicNode() && traverseSession->getProximityInfoState(0)-> hasSpaceProximity(point0Index); } @@ -96,7 +96,7 @@ class TypingTraversal : public Traversal { if (dicNode->isCompletion(inputSize)) { return false; } - if (!dicNode->isTerminalWordNode()) { + if (!dicNode->isTerminalDicNode()) { return false; } const int16_t pointIndex = dicNode->getInputIndex(0); diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp index 5b6b5e874..54f65c786 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp @@ -23,39 +23,64 @@ namespace latinime { const TypingWeighting TypingWeighting::sInstance; -ErrorType TypingWeighting::getErrorType(const CorrectionType correctionType, +ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType correctionType, const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const { switch (correctionType) { case CT_MATCH: if (isProximityDicNode(traverseSession, dicNode)) { - return ET_PROXIMITY_CORRECTION; + return ErrorTypeUtils::PROXIMITY_CORRECTION; + } else if (dicNode->isInDigraph()) { + return ErrorTypeUtils::MATCH_WITH_DIGRAPH; } else { - return ET_NOT_AN_ERROR; + // Compare the node code point with original primary code point on the keyboard. + const ProximityInfoState *const pInfoState = + traverseSession->getProximityInfoState(0); + const int primaryOriginalCodePoint = pInfoState->getPrimaryOriginalCodePointAt( + dicNode->getInputIndex(0)); + const int nodeCodePoint = dicNode->getNodeCodePoint(); + if (primaryOriginalCodePoint == nodeCodePoint) { + // Node code point is same as original code point on the keyboard. + return ErrorTypeUtils::NOT_AN_ERROR; + } else if (CharUtils::toLowerCase(primaryOriginalCodePoint) == + CharUtils::toLowerCase(nodeCodePoint)) { + // Only cases of the code points are different. + return ErrorTypeUtils::MATCH_WITH_CASE_ERROR; + } else if (CharUtils::toBaseCodePoint(primaryOriginalCodePoint) == + CharUtils::toBaseCodePoint(nodeCodePoint)) { + // Node code point is a variant of original code point. + return ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR; + } else { + // Node code point is a variant of original code point and the cases are also + // different. + return ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR + | ErrorTypeUtils::MATCH_WITH_CASE_ERROR; + } } + break; case CT_ADDITIONAL_PROXIMITY: - return ET_PROXIMITY_CORRECTION; + return ErrorTypeUtils::PROXIMITY_CORRECTION; case CT_OMISSION: if (parentDicNode->canBeIntentionalOmission()) { - return ET_INTENTIONAL_OMISSION; + return ErrorTypeUtils::INTENTIONAL_OMISSION; } else { - return ET_EDIT_CORRECTION; + return ErrorTypeUtils::EDIT_CORRECTION; } break; case CT_SUBSTITUTION: case CT_INSERTION: case CT_TERMINAL_INSERTION: case CT_TRANSPOSITION: - return ET_EDIT_CORRECTION; + return ErrorTypeUtils::EDIT_CORRECTION; case CT_NEW_WORD_SPACE_OMISSION: case CT_NEW_WORD_SPACE_SUBSTITUTION: - return ET_NEW_WORD; + return ErrorTypeUtils::NEW_WORD; case CT_TERMINAL: - return ET_NOT_AN_ERROR; + return ErrorTypeUtils::NOT_AN_ERROR; case CT_COMPLETION: - return ET_COMPLETION; + return ErrorTypeUtils::COMPLETION; default: - return ET_NOT_AN_ERROR; + return ErrorTypeUtils::NOT_AN_ERROR; } } } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 9f0a331e3..41314ef52 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -19,6 +19,7 @@ #include "defines.h" #include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dictionary/error_type_utils.h" #include "suggest/core/layout/touch_position_correction_utils.h" #include "suggest/core/policy/weighting.h" #include "suggest/core/session/dic_traverse_session.h" @@ -204,7 +205,7 @@ class TypingWeighting : public Weighting { return cost * traverseSession->getMultiWordCostMultiplier(); } - ErrorType getErrorType(const CorrectionType correctionType, + ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType, const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const; diff --git a/native/jni/src/utils/exclusive_ownership_pointer.h b/native/jni/src/utils/exclusive_ownership_pointer.h new file mode 100644 index 000000000..081802e8b --- /dev/null +++ b/native/jni/src/utils/exclusive_ownership_pointer.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_EXCLUSIVE_OWNERSHIP_POINTER_H +#define LATINIME_EXCLUSIVE_OWNERSHIP_POINTER_H + +#include "defines.h" + +namespace latinime { + +template<class T> +class ExclusiveOwnershipPointer { + public: + // This instance become an owner of the raw pointer. + AK_FORCE_INLINE ExclusiveOwnershipPointer(T *const rawPointer) + : mPointer(rawPointer), + mSharedOwnerPtr(new (ExclusiveOwnershipPointer<T> *)(this)) {} + + // Move the ownership. + AK_FORCE_INLINE ExclusiveOwnershipPointer(const ExclusiveOwnershipPointer<T> &pointer) + : mPointer(pointer.mPointer), mSharedOwnerPtr(pointer.mSharedOwnerPtr) { + transferOwnership(&pointer); + } + + AK_FORCE_INLINE ~ExclusiveOwnershipPointer() { + deletePointersIfHavingOwnership(); + } + + AK_FORCE_INLINE T *get() const { + return mPointer; + } + + private: + // This class allows to copy and ensures only one instance has the ownership of the + // managed pointer. + DISALLOW_DEFAULT_CONSTRUCTOR(ExclusiveOwnershipPointer); + DISALLOW_ASSIGNMENT_OPERATOR(ExclusiveOwnershipPointer); + + void transferOwnership(const ExclusiveOwnershipPointer<T> *const src) { + if (*mSharedOwnerPtr != src) { + AKLOGE("Failed to transfer the ownership because src is not the current owner." + "src: %p, owner: %p", src, *mSharedOwnerPtr); + ASSERT(false); + return; + } + // Transfer the ownership from src to this instance. + *mSharedOwnerPtr = this; + } + + void deletePointersIfHavingOwnership() { + if (mSharedOwnerPtr && *mSharedOwnerPtr == this) { + if (mPointer) { + if (DEBUG_DICT) { + AKLOGI("Releasing pointer: %p", mPointer); + } + delete mPointer; + } + delete mSharedOwnerPtr; + } + } + + T *mPointer; + // mSharedOwnerPtr points a shared memory space where the instance which has the ownership is + // stored. + ExclusiveOwnershipPointer<T> **mSharedOwnerPtr; +}; +} // namespace latinime +#endif /* LATINIME_EXCLUSIVE_OWNERSHIP_POINTER_H */ diff --git a/native/jni/src/utils/time_keeper.cpp b/native/jni/src/utils/time_keeper.cpp new file mode 100644 index 000000000..026284060 --- /dev/null +++ b/native/jni/src/utils/time_keeper.cpp @@ -0,0 +1,41 @@ +/* + * 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 "utils/time_keeper.h" + +#include <ctime> + +namespace latinime { + +int TimeKeeper::sCurrentTime; +bool TimeKeeper::sSetForTesting; + +/* static */ void TimeKeeper::setCurrentTime() { + if (!sSetForTesting) { + sCurrentTime = time(0); + } +} + +/* static */ void TimeKeeper::startTestModeWithForceCurrentTime(const int currentTime) { + sCurrentTime = currentTime; + sSetForTesting = true; +} + +/* static */ void TimeKeeper::stopTestMode() { + sSetForTesting = false; +} + +} // namespace latinime diff --git a/native/jni/src/utils/time_keeper.h b/native/jni/src/utils/time_keeper.h new file mode 100644 index 000000000..d066757e4 --- /dev/null +++ b/native/jni/src/utils/time_keeper.h @@ -0,0 +1,41 @@ +/* + * 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_TIME_KEEPER_H +#define LATINIME_TIME_KEEPER_H + +#include "defines.h" + +namespace latinime { + +class TimeKeeper { + public: + static void setCurrentTime(); + + static void startTestModeWithForceCurrentTime(const int currentTime); + + static void stopTestMode(); + + static int peekCurrentTime() { return sCurrentTime; }; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(TimeKeeper); + + static int sCurrentTime; + static bool sSetForTesting; +}; +} // namespace latinime +#endif /* LATINIME_TIME_KEEPER_H */ diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 09a63c590..4ca846be1 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.inputmethod.latin.tests"> - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17" /> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java index 2eb448c82..cbe2d5960 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java @@ -116,6 +116,16 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase { private static final String SURROGATE1 = PAIR1 + PAIR2; private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3; + public void testResolveNullText() { + assertNull("resolve null", KeySpecParser.resolveTextReference( + null, mTextsSet)); + } + + public void testResolveEmptyText() { + assertNull("resolve empty text", KeySpecParser.resolveTextReference( + "!text/empty_string", mTextsSet)); + } + public void testSplitZero() { assertTextArray("Empty string", ""); assertTextArray("Empty entry", ","); diff --git a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java index 279559cfe..7908b260e 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueueTests.java @@ -27,7 +27,7 @@ public class PointerTrackerQueueTests extends AndroidTestCase { public final int mId; public boolean mIsModifier; - public boolean mIsInSlidingKeyInput; + public boolean mIsInDraggingFinger; public long mPhantomUpEventTime = NOT_HAPPENED; public Element(int id) { @@ -40,8 +40,8 @@ public class PointerTrackerQueueTests extends AndroidTestCase { } @Override - public boolean isInSlidingKeyInput() { - return mIsInSlidingKeyInput; + public boolean isInDraggingFinger() { + return mIsInDraggingFinger; } @Override @@ -297,19 +297,19 @@ public class PointerTrackerQueueTests extends AndroidTestCase { assertEquals(Element.NOT_HAPPENED, mElement4.mPhantomUpEventTime); } - public void testIsAnyInSlidingKeyInput() { + public void testIsAnyInDraggingFinger() { Element.sPhantomUpCount = 0; - assertFalse(mQueue.isAnyInSlidingKeyInput()); + assertFalse(mQueue.isAnyInDraggingFinger()); mQueue.add(mElement1); mQueue.add(mElement2); mQueue.add(mElement3); mQueue.add(mElement4); - assertFalse(mQueue.isAnyInSlidingKeyInput()); + assertFalse(mQueue.isAnyInDraggingFinger()); - mElement3.mIsInSlidingKeyInput = true; - assertTrue(mQueue.isAnyInSlidingKeyInput()); + mElement3.mIsInDraggingFinger = true; + assertTrue(mQueue.isAnyInDraggingFinger()); assertEquals(0, Element.sPhantomUpCount); assertEquals(4, mQueue.size()); diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index cd5384ea4..e0276513a 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -22,6 +22,7 @@ import android.util.Pair; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.IOException; @@ -30,6 +31,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Random; +import java.util.concurrent.TimeUnit; @LargeTest public class BinaryDictionaryDecayingTests extends AndroidTestCase { @@ -37,61 +39,146 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private static final String TEST_LOCALE = "test"; // Note that these are corresponding definitions in native code in - // latinime::DynamicPatriciaTriePolicy. - private static final String SET_NEEDS_TO_DECAY_FOR_TESTING_KEY = - "SET_NEEDS_TO_DECAY_FOR_TESTING"; + // latinime::Ver4PatriciaTriePolicy. + private static final String SET_CURRENT_TIME_FOR_TESTING_QUERY = + "SET_CURRENT_TIME_FOR_TESTING"; + private static final String GET_CURRENT_TIME_QUERY = "GET_CURRENT_TIME"; + private static final String QUIT_TIMEKEEPER_TEST_MODE_QUERY = "QUIT_TIMEKEEPER_TEST_MODE"; private static final int DUMMY_PROBABILITY = 0; + private int mCurrentTime = 0; + @Override protected void setUp() throws Exception { super.setUp(); + mCurrentTime = 0; } @Override protected void tearDown() throws Exception { super.tearDown(); + try { + final File dictFile = + createEmptyDictionaryAndGetFile("TestBinaryDictionary", FormatSpec.VERSION4); + final BinaryDictionary binaryDictionary = + new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, + dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), + TEST_LOCALE, true /* isUpdatable */); + binaryDictionary.getPropertyForTests(QUIT_TIMEKEEPER_TEST_MODE_QUERY); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + } + + private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, + final int probability) { + binaryDictionary.addUnigramWord(word, probability, "" /* shortcutTarget */, + BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, + false /* isNotAWord */, false /* isBlacklisted */, + mCurrentTime /* timestamp */); + } + + private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, + final String word1, final int probability) { + binaryDictionary.addBigramWords(word0, word1, probability, + mCurrentTime /* timestamp */); } private void forcePassingShortTime(final BinaryDictionary binaryDictionary) { - // Entries having low probability would be suppressed once in 3 GCs. - final int count = 3; - for (int i = 0; i < count; i++) { - binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY); - binaryDictionary.flushWithGC(); - } + // 4 days. + final int timeToElapse = (int)TimeUnit.SECONDS.convert(4, TimeUnit.DAYS); + mCurrentTime += timeToElapse; + setCurrentTime(binaryDictionary, mCurrentTime); + binaryDictionary.flushWithGC(); } private void forcePassingLongTime(final BinaryDictionary binaryDictionary) { - // Currently, probabilities are decayed when GC is run. All entries that have never been - // typed in 128 GCs would be removed. - final int count = 128; - for (int i = 0; i < count; i++) { - binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY); - binaryDictionary.flushWithGC(); - } + // 60 days. + final int timeToElapse = (int)TimeUnit.SECONDS.convert(60, TimeUnit.DAYS); + mCurrentTime += timeToElapse; + setCurrentTime(binaryDictionary, mCurrentTime); + binaryDictionary.flushWithGC(); } - private File createEmptyDictionaryAndGetFile(final String filename) throws IOException { - final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION, + private File createEmptyDictionaryAndGetFile(final String dictId, + final int formatVersion) throws IOException { + if (formatVersion == FormatSpec.VERSION4) { + return createEmptyVer4DictionaryAndGetFile(dictId); + } else { + throw new IOException("Dictionary format version " + formatVersion + + " is not supported."); + } + } + private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { + final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); + FileUtils.deleteRecursively(file); Map<String, String> attributeMap = new HashMap<String, String>(); attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); + attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE, + FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); + final String headerFileName = file.getName() + FormatSpec.HEADER_FILE_EXTENSION; if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), - 3 /* dictVersion */, attributeMap)) { + FormatSpec.VERSION4, attributeMap)) { return file; } else { - throw new IOException("Empty dictionary cannot be created."); + throw new IOException("Empty dictionary " + file.getAbsolutePath() + " " + + headerFileName + " cannot be created."); } } + private static int getCurrentTime(final BinaryDictionary binaryDictionary) { + return Integer.parseInt(binaryDictionary.getPropertyForTests(GET_CURRENT_TIME_QUERY)); + } + + private static void setCurrentTime(final BinaryDictionary binaryDictionary, + final int currentTime) { + final String query = SET_CURRENT_TIME_FOR_TESTING_QUERY + ":" + currentTime; + binaryDictionary.getPropertyForTests(query); + } + + public void testControlCurrentTime() { + testControlCurrentTime(FormatSpec.VERSION4); + } + + private void testControlCurrentTime(final int formatVersion) { + final int TEST_COUNT = 1000; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + binaryDictionary.getPropertyForTests(QUIT_TIMEKEEPER_TEST_MODE_QUERY); + final int startTime = getCurrentTime(binaryDictionary); + for (int i = 0; i < TEST_COUNT; i++) { + final int currentTime = random.nextInt(Integer.MAX_VALUE); + setCurrentTime(binaryDictionary, currentTime); + assertEquals(currentTime, getCurrentTime(binaryDictionary)); + } + binaryDictionary.getPropertyForTests(QUIT_TIMEKEEPER_TEST_MODE_QUERY); + final int endTime = getCurrentTime(binaryDictionary); + final int MAX_ALLOWED_ELAPSED_TIME = 10; + assertTrue(startTime <= endTime && endTime <= startTime + MAX_ALLOWED_ELAPSED_TIME); + } + public void testAddValidAndInvalidWords() { + testAddValidAndInvalidWords(FormatSpec.VERSION4); + } + + private void testAddValidAndInvalidWords(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -99,36 +186,35 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY); + addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY); + addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY); + addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("a", Dictionary.NOT_A_PROBABILITY); + addUnigramWord(binaryDictionary, "a", Dictionary.NOT_A_PROBABILITY); assertTrue(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); assertTrue(binaryDictionary.isValidWord("b")); - final int unigramProbability = binaryDictionary.getFrequency("a"); - binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidBigram("a", "b")); - binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidBigram("a", "b")); - binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidBigram("a", "b")); - binaryDictionary.addBigramWords("a", "b", Dictionary.NOT_A_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", Dictionary.NOT_A_PROBABILITY); assertTrue(binaryDictionary.isValidBigram("a", "b")); - binaryDictionary.addUnigramWord("c", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "c", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "c", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "c", DUMMY_PROBABILITY); assertTrue(binaryDictionary.isValidBigram("a", "c")); // Add bigrams of not valid unigrams. - binaryDictionary.addBigramWords("x", "y", Dictionary.NOT_A_PROBABILITY); + addBigramWords(binaryDictionary, "x", "y", Dictionary.NOT_A_PROBABILITY); assertFalse(binaryDictionary.isValidBigram("x", "y")); - binaryDictionary.addBigramWords("x", "y", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "x", "y", DUMMY_PROBABILITY); assertFalse(binaryDictionary.isValidBigram("x", "y")); binaryDictionary.close(); @@ -136,9 +222,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testDecayingProbability() { + testDecayingProbability(FormatSpec.VERSION4); + } + + private void testDecayingProbability(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -146,39 +236,44 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); assertTrue(binaryDictionary.isValidWord("a")); forcePassingShortTime(binaryDictionary); assertFalse(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + assertTrue(binaryDictionary.isValidWord("a")); forcePassingShortTime(binaryDictionary); assertTrue(binaryDictionary.isValidWord("a")); forcePassingLongTime(binaryDictionary); assertFalse(binaryDictionary.isValidWord("a")); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); assertTrue(binaryDictionary.isValidBigram("a", "b")); forcePassingShortTime(binaryDictionary); assertFalse(binaryDictionary.isValidBigram("a", "b")); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("a", DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord("b", DUMMY_PROBABILITY); - binaryDictionary.addBigramWords("a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "a", DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, "b", DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, "a", "b", DUMMY_PROBABILITY); assertTrue(binaryDictionary.isValidBigram("a", "b")); forcePassingShortTime(binaryDictionary); assertTrue(binaryDictionary.isValidBigram("a", "b")); @@ -190,6 +285,10 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddManyUnigramsToDecayingDict() { + testAddManyUnigramsToDecayingDict(FormatSpec.VERSION4); + } + + private void testAddManyUnigramsToDecayingDict(final int formatVersion) { final int unigramCount = 30000; final int unigramTypedCount = 100000; final int codePointSetSize = 50; @@ -198,13 +297,14 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + setCurrentTime(binaryDictionary, mCurrentTime); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); final ArrayList<String> words = new ArrayList<String>(); @@ -218,14 +318,14 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); for (int i = 0; i < unigramTypedCount; i++) { final String word = words.get(random.nextInt(words.size())); - binaryDictionary.addUnigramWord(word, DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final int unigramCountBeforeGC = Integer.parseInt(binaryDictionary.getPropertyForTests( BinaryDictionary.UNIGRAM_COUNT_QUERY)); while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); + forcePassingShortTime(binaryDictionary); } final int unigramCountAfterGC = Integer.parseInt(binaryDictionary.getPropertyForTests( @@ -238,9 +338,75 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { BinaryDictionary.UNIGRAM_COUNT_QUERY)) > 0); assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests( BinaryDictionary.UNIGRAM_COUNT_QUERY)) <= maxUnigramCount); + forcePassingLongTime(binaryDictionary); + assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.UNIGRAM_COUNT_QUERY))); + } + + public void testOverflowUnigrams() { + testOverflowUnigrams(FormatSpec.VERSION4); + } + + private void testOverflowUnigrams(final int formatVersion) { + final int unigramCount = 20000; + final int eachUnigramTypedCount = 5; + final int strongUnigramTypedCount = 20; + final int weakUnigramTypedCount = 1; + final int codePointSetSize = 50; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + setCurrentTime(binaryDictionary, mCurrentTime); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + final String strong = "strong"; + final String weak = "weak"; + for (int j = 0; j < strongUnigramTypedCount; j++) { + addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY); + } + for (int j = 0; j < weakUnigramTypedCount; j++) { + addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY); + } + assertTrue(binaryDictionary.isValidWord(strong)); + assertTrue(binaryDictionary.isValidWord(weak)); + + for (int i = 0; i < unigramCount; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + for (int j = 0; j < eachUnigramTypedCount; j++) { + addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); + } + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + final int unigramCountBeforeGC = + Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.UNIGRAM_COUNT_QUERY)); + assertTrue(binaryDictionary.isValidWord(strong)); + assertTrue(binaryDictionary.isValidWord(weak)); + binaryDictionary.flushWithGC(); + final int unigramCountAfterGC = + Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.UNIGRAM_COUNT_QUERY)); + assertTrue(unigramCountBeforeGC > unigramCountAfterGC); + assertFalse(binaryDictionary.isValidWord(weak)); + assertTrue(binaryDictionary.isValidWord(strong)); + break; + } + } } public void testAddManyBigramsToDecayingDict() { + testAddManyBigramsToDecayingDict(FormatSpec.VERSION4); + } + + private void testAddManyBigramsToDecayingDict(final int formatVersion) { final int unigramCount = 5000; final int bigramCount = 30000; final int bigramTypedCount = 100000; @@ -250,13 +416,14 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + setCurrentTime(binaryDictionary, mCurrentTime); final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); final ArrayList<String> words = new ArrayList<String>(); @@ -282,16 +449,16 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { binaryDictionary.getPropertyForTests(BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); for (int i = 0; i < bigramTypedCount; ++i) { final Pair<String, String> bigram = bigrams.get(random.nextInt(bigrams.size())); - binaryDictionary.addUnigramWord(bigram.first, DUMMY_PROBABILITY); - binaryDictionary.addUnigramWord(bigram.second, DUMMY_PROBABILITY); - binaryDictionary.addBigramWords(bigram.first, bigram.second, DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, bigram.first, DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, bigram.second, DUMMY_PROBABILITY); + addBigramWords(binaryDictionary, bigram.first, bigram.second, DUMMY_PROBABILITY); if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { final int bigramCountBeforeGC = Integer.parseInt(binaryDictionary.getPropertyForTests( BinaryDictionary.BIGRAM_COUNT_QUERY)); while (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { - binaryDictionary.flushWithGC(); + forcePassingShortTime(binaryDictionary); } final int bigramCountAfterGC = Integer.parseInt(binaryDictionary.getPropertyForTests( @@ -304,5 +471,87 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { BinaryDictionary.BIGRAM_COUNT_QUERY)) > 0); assertTrue(Integer.parseInt(binaryDictionary.getPropertyForTests( BinaryDictionary.BIGRAM_COUNT_QUERY)) <= maxBigramCount); + forcePassingLongTime(binaryDictionary); + assertEquals(0, Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.BIGRAM_COUNT_QUERY))); + } + + public void testOverflowBigrams() { + testOverflowBigrams(FormatSpec.VERSION4); + } + + private void testOverflowBigrams(final int formatVersion) { + final int bigramCount = 20000; + final int unigramCount = 1000; + final int unigramTypedCount = 20; + final int eachBigramTypedCount = 5; + final int strongBigramTypedCount = 20; + final int weakBigramTypedCount = 1; + final int codePointSetSize = 50; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + setCurrentTime(binaryDictionary, mCurrentTime); + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + final ArrayList<String> words = new ArrayList<String>(); + for (int i = 0; i < unigramCount; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + words.add(word); + for (int j = 0; j < unigramTypedCount; j++) { + addUnigramWord(binaryDictionary, word, DUMMY_PROBABILITY); + } + } + final String strong = "strong"; + final String weak = "weak"; + final String target = "target"; + for (int j = 0; j < unigramTypedCount; j++) { + addUnigramWord(binaryDictionary, strong, DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, weak, DUMMY_PROBABILITY); + addUnigramWord(binaryDictionary, target, DUMMY_PROBABILITY); + } + binaryDictionary.flushWithGC(); + for (int j = 0; j < strongBigramTypedCount; j++) { + addBigramWords(binaryDictionary, strong, target, DUMMY_PROBABILITY); + } + for (int j = 0; j < weakBigramTypedCount; j++) { + addBigramWords(binaryDictionary, weak, target, DUMMY_PROBABILITY); + } + assertTrue(binaryDictionary.isValidBigram(strong, target)); + assertTrue(binaryDictionary.isValidBigram(weak, target)); + + for (int i = 0; i < bigramCount; i++) { + final int word0Index = random.nextInt(words.size()); + final String word0 = words.get(word0Index); + final int index = random.nextInt(words.size() - 1); + final int word1Index = (index >= word0Index) ? index + 1 : index; + final String word1 = words.get(word1Index); + + for (int j = 0; j < eachBigramTypedCount; j++) { + addBigramWords(binaryDictionary, word0, word1, DUMMY_PROBABILITY); + } + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + final int bigramCountBeforeGC = + Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.BIGRAM_COUNT_QUERY)); + binaryDictionary.flushWithGC(); + final int bigramCountAfterGC = + Integer.parseInt(binaryDictionary.getPropertyForTests( + BinaryDictionary.BIGRAM_COUNT_QUERY)); + assertTrue(bigramCountBeforeGC > bigramCountAfterGC); + assertTrue(binaryDictionary.isValidBigram(strong, target)); + assertFalse(binaryDictionary.isValidBigram(weak, target)); + break; + } + } } } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 5b8f0e977..cfc4c762a 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -21,8 +21,12 @@ import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.util.Pair; +import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; import com.android.inputmethod.latin.makedict.CodePointUtils; import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.utils.UnigramProperty; import java.io.File; import java.io.IOException; @@ -33,6 +37,7 @@ import java.util.Locale; import java.util.Map; import java.util.Random; +// TODO Use the seed passed as an argument for makedict test. @LargeTest public class BinaryDictionaryTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; @@ -48,24 +53,41 @@ public class BinaryDictionaryTests extends AndroidTestCase { super.tearDown(); } - private File createEmptyDictionaryAndGetFile(final String filename) throws IOException { - final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION, + private File createEmptyDictionaryAndGetFile(final String dictId, + final int formatVersion) throws IOException { + if (formatVersion == FormatSpec.VERSION4) { + return createEmptyVer4DictionaryAndGetFile(dictId); + } else { + throw new IOException("Dictionary format version " + formatVersion + + " is not supported."); + } + } + + private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException { + final File file = File.createTempFile(dictId, TEST_DICT_FILE_EXTENSION, getContext().getCacheDir()); + file.delete(); + file.mkdir(); Map<String, String> attributeMap = new HashMap<String, String>(); attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), - 3 /* dictVersion */, attributeMap)) { + FormatSpec.VERSION4, attributeMap)) { return file; } else { - throw new IOException("Empty dictionary cannot be created."); + throw new IOException("Empty dictionary " + file.getAbsolutePath() + + " cannot be created."); } } public void testIsValidDictionary() { + testIsValidDictionary(FormatSpec.VERSION4); + } + + private void testIsValidDictionary(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -77,7 +99,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.close(); assertFalse("binaryDictionary must be invalid after closing.", binaryDictionary.isValidDictionary()); - dictFile.delete(); + FileUtils.deleteRecursively(dictFile); binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); @@ -86,10 +108,28 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.close(); } + private void addUnigramWord(final BinaryDictionary binaryDictionary, final String word, + final int probability) { + binaryDictionary.addUnigramWord(word, probability, "" /* shortcutTarget */, + BinaryDictionary.NOT_A_PROBABILITY /* shortcutProbability */, + false /* isNotAWord */, false /* isBlacklisted */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + } + + private void addBigramWords(final BinaryDictionary binaryDictionary, final String word0, + final String word1, final int probability) { + binaryDictionary.addBigramWords(word0, word1, probability, + BinaryDictionary.NOT_A_VALID_TIMESTAMP /* timestamp */); + } + public void testAddUnigramWord() { + testAddUnigramWord(FormatSpec.VERSION4); + } + + private void testAddUnigramWord(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -98,21 +138,21 @@ public class BinaryDictionaryTests extends AndroidTestCase { Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); final int probability = 100; - binaryDictionary.addUnigramWord("aaa", probability); + addUnigramWord(binaryDictionary, "aaa", probability); // Reallocate and create. - binaryDictionary.addUnigramWord("aab", probability); + addUnigramWord(binaryDictionary, "aab", probability); // Insert into children. - binaryDictionary.addUnigramWord("aac", probability); + addUnigramWord(binaryDictionary, "aac", probability); // Make terminal. - binaryDictionary.addUnigramWord("aa", probability); + addUnigramWord(binaryDictionary, "aa", probability); // Create children. - binaryDictionary.addUnigramWord("aaaa", probability); + addUnigramWord(binaryDictionary, "aaaa", probability); // Reallocate and make termianl. - binaryDictionary.addUnigramWord("a", probability); + addUnigramWord(binaryDictionary, "a", probability); final int updatedProbability = 200; // Update. - binaryDictionary.addUnigramWord("aaa", updatedProbability); + addUnigramWord(binaryDictionary, "aaa", updatedProbability); assertEquals(probability, binaryDictionary.getFrequency("aab")); assertEquals(probability, binaryDictionary.getFrequency("aac")); @@ -125,13 +165,17 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRandomlyAddUnigramWord() { + testRandomlyAddUnigramWord(FormatSpec.VERSION4); + } + + private void testRandomlyAddUnigramWord(final int formatVersion) { final int wordCount = 1000; final int codePointSetSize = 50; final long seed = System.currentTimeMillis(); File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -148,7 +192,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { probabilityMap.put(word, random.nextInt(0xFF)); } for (String word : probabilityMap.keySet()) { - binaryDictionary.addUnigramWord(word, probabilityMap.get(word)); + addUnigramWord(binaryDictionary, word, probabilityMap.get(word)); } for (String word : probabilityMap.keySet()) { assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word)); @@ -157,9 +201,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWords() { + testAddBigramWords(FormatSpec.VERSION4); + } + + private void testAddBigramWords(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -170,13 +218,13 @@ 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); - binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); - binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability); - binaryDictionary.addBigramWords("abb", "aaa", bigramProbability); - binaryDictionary.addBigramWords("abb", "bcc", bigramProbability); + addUnigramWord(binaryDictionary, "aaa", unigramProbability); + addUnigramWord(binaryDictionary, "abb", unigramProbability); + addUnigramWord(binaryDictionary, "bcc", unigramProbability); + addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); + addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability); + addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); + addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); final int probability = binaryDictionary.calculateProbability(unigramProbability, bigramProbability); @@ -189,7 +237,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa")); assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc")); - binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability); + addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability); final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability, updatedBigramProbability); assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb")); @@ -205,22 +253,26 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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); + addUnigramWord(binaryDictionary, "abcde", unigramProbability); + addUnigramWord(binaryDictionary, "fghij", unigramProbability); + addBigramWords(binaryDictionary, "abcde", "fghij", bigramProbability); + addUnigramWord(binaryDictionary, "fgh", unigramProbability); + addUnigramWord(binaryDictionary, "abc", unigramProbability); + addUnigramWord(binaryDictionary, "f", unigramProbability); assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij")); assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getBigramProbability("abcde", "fgh")); - binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability); + addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability); assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij")); dictFile.delete(); } public void testRandomlyAddBigramWords() { + testRandomlyAddBigramWords(FormatSpec.VERSION4); + } + + private void testRandomlyAddBigramWords(final int formatVersion) { final int wordCount = 100; final int bigramCount = 1000; final int codePointSetSize = 50; @@ -229,7 +281,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -249,7 +301,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { words.add(word); final int unigramProbability = random.nextInt(0xFF); unigramProbabilities.put(word, unigramProbability); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } for (int i = 0; i < bigramCount; i++) { @@ -262,7 +314,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { bigramWords.add(bigram); final int bigramProbability = random.nextInt(0xF); bigramProbabilities.put(bigram, bigramProbability); - binaryDictionary.addBigramWords(word0, word1, bigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); } for (final Pair<String, String> bigram : bigramWords) { @@ -278,9 +330,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRemoveBigramWords() { + testRemoveBigramWords(FormatSpec.VERSION4); + } + + private void testRemoveBigramWords(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -289,13 +345,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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); + addUnigramWord(binaryDictionary, "aaa", unigramProbability); + addUnigramWord(binaryDictionary, "abb", unigramProbability); + addUnigramWord(binaryDictionary, "bcc", unigramProbability); + addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); + addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability); + addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); + addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc")); @@ -304,7 +360,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.removeBigramWords("aaa", "abb"); assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb")); - binaryDictionary.addBigramWords("aaa", "abb", bigramProbability); + addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb")); @@ -324,9 +380,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testFlushDictionary() { + testFlushDictionary(FormatSpec.VERSION4); + } + + private void testFlushDictionary(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -335,8 +395,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); final int probability = 100; - binaryDictionary.addUnigramWord("aaa", probability); - binaryDictionary.addUnigramWord("abcd", probability); + addUnigramWord(binaryDictionary, "aaa", probability); + addUnigramWord(binaryDictionary, "abcd", probability); // Close without flushing. binaryDictionary.close(); @@ -347,8 +407,8 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa")); assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd")); - binaryDictionary.addUnigramWord("aaa", probability); - binaryDictionary.addUnigramWord("abcd", probability); + addUnigramWord(binaryDictionary, "aaa", probability); + addUnigramWord(binaryDictionary, "abcd", probability); binaryDictionary.flush(); binaryDictionary.close(); @@ -358,7 +418,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { assertEquals(probability, binaryDictionary.getFrequency("aaa")); assertEquals(probability, binaryDictionary.getFrequency("abcd")); - binaryDictionary.addUnigramWord("bcde", probability); + addUnigramWord(binaryDictionary, "bcde", probability); binaryDictionary.flush(); binaryDictionary.close(); @@ -372,9 +432,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testFlushWithGCDictionary() { + testFlushWithGCDictionary(FormatSpec.VERSION4); + } + + private void testFlushWithGCDictionary(final int formatVersion) { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -384,13 +448,13 @@ public class BinaryDictionaryTests extends AndroidTestCase { 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); + addUnigramWord(binaryDictionary, "aaa", unigramProbability); + addUnigramWord(binaryDictionary, "abb", unigramProbability); + addUnigramWord(binaryDictionary, "bcc", unigramProbability); + addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability); + addBigramWords(binaryDictionary, "aaa", "bcc", bigramProbability); + addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability); + addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability); binaryDictionary.flushWithGC(); binaryDictionary.close(); @@ -415,8 +479,12 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } - // TODO: Evaluate performance of GC public void testAddBigramWordsAndFlashWithGC() { + testAddBigramWordsAndFlashWithGC(FormatSpec.VERSION4); + } + + // TODO: Evaluate performance of GC + private void testAddBigramWordsAndFlashWithGC(final int formatVersion) { final int wordCount = 100; final int bigramCount = 1000; final int codePointSetSize = 30; @@ -425,7 +493,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -446,7 +514,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { words.add(word); final int unigramProbability = random.nextInt(0xFF); unigramProbabilities.put(word, unigramProbability); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } for (int i = 0; i < bigramCount; i++) { @@ -459,7 +527,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { bigramWords.add(bigram); final int bigramProbability = random.nextInt(0xF); bigramProbabilities.put(bigram, bigramProbability); - binaryDictionary.addBigramWords(word0, word1, bigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); } binaryDictionary.flushWithGC(); @@ -480,7 +548,11 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } - public void testRandomOperetionsAndFlashWithGC() { + public void testRandomOperationsAndFlashWithGC() { + testRandomOperationsAndFlashWithGC(FormatSpec.VERSION4); + } + + private void testRandomOperationsAndFlashWithGC(final int formatVersion) { final int flashWithGCIterationCount = 50; final int operationCountInEachIteration = 200; final int initialUnigramCount = 100; @@ -494,7 +566,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -513,7 +585,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { words.add(word); final int unigramProbability = random.nextInt(0xFF); unigramProbabilities.put(word, unigramProbability); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } binaryDictionary.flushWithGC(); binaryDictionary.close(); @@ -529,7 +601,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { words.add(word); final int unigramProbability = random.nextInt(0xFF); unigramProbabilities.put(word, unigramProbability); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } // Add bigram. if (random.nextFloat() < addBigramProb && words.size() > 2) { @@ -547,7 +619,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final Pair<String, String> bigram = new Pair<String, String>(word0, word1); bigramWords.add(bigram); bigramProbabilities.put(bigram, bigramProbability); - binaryDictionary.addBigramWords(word0, word1, bigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); } // Remove bigram. if (random.nextFloat() < removeBigramProb && !bigramWords.isEmpty()) { @@ -588,6 +660,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddManyUnigramsAndFlushWithGC() { + testAddManyUnigramsAndFlushWithGC(FormatSpec.VERSION4); + } + + private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) { final int flashWithGCIterationCount = 3; final int codePointSetSize = 50; @@ -596,7 +672,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -615,7 +691,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { words.add(word); final int unigramProbability = random.nextInt(0xFF); unigramProbabilities.put(word, unigramProbability); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } for (int j = 0; j < words.size(); j++) { @@ -632,6 +708,10 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testUnigramAndBigramCount() { + testUnigramAndBigramCount(FormatSpec.VERSION4); + } + + private void testUnigramAndBigramCount(final int formatVersion) { final int flashWithGCIterationCount = 10; final int codePointSetSize = 50; final int unigramCountPerIteration = 1000; @@ -641,7 +721,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { File dictFile = null; try { - dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary"); + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } @@ -659,7 +739,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { final String word = CodePointUtils.generateWord(random, codePointSet); words.add(word); final int unigramProbability = random.nextInt(0xFF); - binaryDictionary.addUnigramWord(word, unigramProbability); + addUnigramWord(binaryDictionary, word, unigramProbability); } for (int j = 0; j < bigramCountPerIteration; j++) { final String word0 = words.get(random.nextInt(words.size())); @@ -669,7 +749,7 @@ public class BinaryDictionaryTests extends AndroidTestCase { } bigrams.add(new Pair<String, String>(word0, word1)); final int bigramProbability = random.nextInt(0xF); - binaryDictionary.addBigramWords(word0, word1, bigramProbability); + addBigramWords(binaryDictionary, word0, word1, bigramProbability); } assertEquals(new HashSet<String>(words).size(), Integer.parseInt( binaryDictionary.getPropertyForTests(BinaryDictionary.UNIGRAM_COUNT_QUERY))); @@ -685,4 +765,242 @@ public class BinaryDictionaryTests extends AndroidTestCase { dictFile.delete(); } + + public void testAddMultipleDictionaryEntries() { + testAddMultipleDictionaryEntries(FormatSpec.VERSION4); + } + + private void testAddMultipleDictionaryEntries(final int formatVersion) { + final int codePointSetSize = 20; + final int lmParamCount = 1000; + final double bigramContinueRate = 0.9; + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final HashMap<Pair<String, String>, Integer> bigramProbabilities = + new HashMap<Pair<String, String>, Integer>(); + + final LanguageModelParam[] languageModelParams = new LanguageModelParam[lmParamCount]; + String prevWord = null; + for (int i = 0; i < languageModelParams.length; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + final int probability = random.nextInt(0xFF); + final int bigramProbability = random.nextInt(0xF); + unigramProbabilities.put(word, probability); + if (prevWord == null) { + languageModelParams[i] = new LanguageModelParam(word, probability, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + } else { + languageModelParams[i] = new LanguageModelParam(prevWord, word, probability, + bigramProbability, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + bigramProbabilities.put(new Pair<String, String>(prevWord, word), + bigramProbability); + } + prevWord = (random.nextDouble() < bigramContinueRate) ? word : null; + } + + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + binaryDictionary.addMultipleDictionaryEntries(languageModelParams); + + for (Map.Entry<String, Integer> entry : unigramProbabilities.entrySet()) { + assertEquals((int)entry.getValue(), binaryDictionary.getFrequency(entry.getKey())); + } + + for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) { + final String word0 = entry.getKey().first; + final String word1 = entry.getKey().second; + final int unigramProbability = unigramProbabilities.get(word1); + final int bigramProbability = entry.getValue(); + final int probability = binaryDictionary.calculateProbability( + unigramProbability, bigramProbability); + assertEquals(probability, binaryDictionary.getBigramProbability(word0, word1)); + } + } + + public void testGetUnigramProperties() { + testGetUnigramProperties(FormatSpec.VERSION4); + } + + private void testGetUnigramProperties(final int formatVersion) { + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + final int ITERATION_COUNT = 1000; + final int codePointSetSize = 20; + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final UnigramProperty invalidUnigramProperty = + binaryDictionary.getUnigramProperty("dummyWord"); + assertFalse(invalidUnigramProperty.isValid()); + + for (int i = 0; i < ITERATION_COUNT; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + final int unigramProbability = random.nextInt(0xFF); + final boolean isNotAWord = random.nextBoolean(); + final boolean isBlacklisted = random.nextBoolean(); + // TODO: Add tests for historical info. + binaryDictionary.addUnigramWord(word, unigramProbability, + null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY, + isNotAWord, isBlacklisted, BinaryDictionary.NOT_A_VALID_TIMESTAMP); + final UnigramProperty unigramProperty = + binaryDictionary.getUnigramProperty(word); + assertEquals(word, unigramProperty.mCodePoints); + assertTrue(unigramProperty.isValid()); + assertEquals(isNotAWord, unigramProperty.mIsNotAWord); + assertEquals(isBlacklisted, unigramProperty.mIsBlacklisted); + assertEquals(false, unigramProperty.mHasBigrams); + assertEquals(false, unigramProperty.mHasShortcuts); + assertEquals(unigramProbability, unigramProperty.mProbability); + assertTrue(unigramProperty.mShortcutTargets.isEmpty()); + } + } + + public void testAddShortcuts() { + testAddShortcuts(FormatSpec.VERSION4); + } + + private void testAddShortcuts(final int formatVersion) { + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int unigramProbability = 100; + final int shortcutProbability = 10; + binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz", + shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, + 0 /* timestamp */); + UnigramProperty unigramProperty = binaryDictionary.getUnigramProperty("aaa"); + assertEquals(1, unigramProperty.mShortcutTargets.size()); + assertEquals("zzz", unigramProperty.mShortcutTargets.get(0).mWord); + assertEquals(shortcutProbability, unigramProperty.mShortcutTargets.get(0).mFrequency); + final int updatedShortcutProbability = 2; + binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz", + updatedShortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, + 0 /* timestamp */); + unigramProperty = binaryDictionary.getUnigramProperty("aaa"); + assertEquals(1, unigramProperty.mShortcutTargets.size()); + assertEquals("zzz", unigramProperty.mShortcutTargets.get(0).mWord); + assertEquals(updatedShortcutProbability, + unigramProperty.mShortcutTargets.get(0).mFrequency); + binaryDictionary.addUnigramWord("aaa", unigramProbability, "yyy", + shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, + 0 /* timestamp */); + final HashMap<String, Integer> shortcutTargets = new HashMap<String, Integer>(); + shortcutTargets.put("zzz", updatedShortcutProbability); + shortcutTargets.put("yyy", shortcutProbability); + unigramProperty = binaryDictionary.getUnigramProperty("aaa"); + assertEquals(2, unigramProperty.mShortcutTargets.size()); + for (WeightedString shortcutTarget : unigramProperty.mShortcutTargets) { + assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord)); + assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), shortcutTarget.mFrequency); + shortcutTargets.remove(shortcutTarget.mWord); + } + shortcutTargets.put("zzz", updatedShortcutProbability); + shortcutTargets.put("yyy", shortcutProbability); + binaryDictionary.flushWithGC(); + unigramProperty = binaryDictionary.getUnigramProperty("aaa"); + assertEquals(2, unigramProperty.mShortcutTargets.size()); + for (WeightedString shortcutTarget : unigramProperty.mShortcutTargets) { + assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord)); + assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), shortcutTarget.mFrequency); + shortcutTargets.remove(shortcutTarget.mWord); + } + } + + public void testAddManyShortcuts() { + testAddManyShortcuts(FormatSpec.VERSION4); + } + + private void testAddManyShortcuts(final int formatVersion) { + final long seed = System.currentTimeMillis(); + final Random random = new Random(seed); + final int UNIGRAM_COUNT = 1000; + final int SHORTCUT_COUNT = 10000; + final int codePointSetSize = 20; + final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random); + + final ArrayList<String> words = new ArrayList<String>(); + final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>(); + final HashMap<String, HashMap<String, Integer>> shortcutTargets = + new HashMap<String, HashMap<String, Integer>>(); + + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + for (int i = 0; i < UNIGRAM_COUNT; i++) { + final String word = CodePointUtils.generateWord(random, codePointSet); + final int unigramProbability = random.nextInt(0xFF); + addUnigramWord(binaryDictionary, word, unigramProbability); + words.add(word); + unigramProbabilities.put(word, unigramProbability); + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); + } + } + for (int i = 0; i < SHORTCUT_COUNT; i++) { + final String shortcutTarget = CodePointUtils.generateWord(random, codePointSet); + final int shortcutProbability = random.nextInt(0xF); + final String word = words.get(random.nextInt(words.size())); + final int unigramProbability = unigramProbabilities.get(word); + binaryDictionary.addUnigramWord(word, unigramProbability, shortcutTarget, + shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */, + 0 /* timestamp */); + if (shortcutTargets.containsKey(word)) { + final HashMap<String, Integer> shortcutTargetsOfWord = shortcutTargets.get(word); + shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); + } else { + final HashMap<String, Integer> shortcutTargetsOfWord = + new HashMap<String, Integer>(); + shortcutTargetsOfWord.put(shortcutTarget, shortcutProbability); + shortcutTargets.put(word, shortcutTargetsOfWord); + } + if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); + } + } + + for (final String word : words) { + final UnigramProperty unigramProperty = binaryDictionary.getUnigramProperty(word); + assertEquals((int)unigramProbabilities.get(word), unigramProperty.mProbability); + assertEquals(shortcutTargets.get(word).size(), unigramProperty.mShortcutTargets.size()); + for (final WeightedString shortcutTarget : unigramProperty.mShortcutTargets) { + final String targetCodePonts = shortcutTarget.mWord; + assertEquals((int)shortcutTargets.get(word).get(targetCodePonts), + shortcutTarget.mFrequency); + } + } + } } diff --git a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java deleted file mode 100644 index 6aae1044e..000000000 --- a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -/** - * Unit test for ExpandableDictionary - */ -@SmallTest -public class ExpandableDictionaryTests extends AndroidTestCase { - - private final static int UNIGRAM_FREQ = 50; - // See UserBinaryDictionary for more information about this variable. - // For tests, its actual value does not matter. - private final static int SHORTCUT_FREQ = 14; - - public void testAddWordAndGetWordFrequency() { - final ExpandableDictionary dict = new ExpandableDictionary(Dictionary.TYPE_USER); - - // Add words - dict.addWord("abcde", "abcde", UNIGRAM_FREQ, SHORTCUT_FREQ); - dict.addWord("abcef", null, UNIGRAM_FREQ + 1, 0); - - // Check words - assertFalse(dict.isValidWord("abcde")); - assertEquals(UNIGRAM_FREQ, dict.getWordFrequency("abcde")); - assertTrue(dict.isValidWord("abcef")); - assertEquals(UNIGRAM_FREQ+1, dict.getWordFrequency("abcef")); - - dict.addWord("abc", null, UNIGRAM_FREQ + 2, 0); - assertTrue(dict.isValidWord("abc")); - assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc")); - - // Add existing word with lower frequency - dict.addWord("abc", null, UNIGRAM_FREQ, 0); - assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc")); - - // Add existing word with higher frequency - dict.addWord("abc", null, UNIGRAM_FREQ + 3, 0); - assertEquals(UNIGRAM_FREQ + 3, dict.getWordFrequency("abc")); - } -} diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index b9b52a6f3..aaad740e4 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -30,6 +30,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodSubtype; import android.widget.EditText; import android.widget.FrameLayout; @@ -37,15 +38,20 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { private static final String PREF_DEBUG_MODE = "debug_mode"; + private static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; + // Default value for auto-correction threshold. This is the string representation of the + // index in the resources array of auto-correction threshold settings. + private static final String DEFAULT_AUTO_CORRECTION_THRESHOLD = "1"; - // The message that sets the underline is posted with a 200 ms delay - protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200; + // The message that sets the underline is posted with a 500 ms delay + protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 500; // The message that sets predictions is posted with a 200 ms delay protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; @@ -54,6 +60,8 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { protected MyEditText mEditText; protected View mInputView; protected InputConnection mInputConnection; + private boolean mPreviousDebugSetting; + private String mPreviousAutoCorrectSetting; // A helper class to ease span tests public static class SpanGetter { @@ -135,7 +143,17 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { final boolean previousSetting = prefs.getBoolean(key, defaultValue); final SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(key, value); - editor.commit(); + editor.apply(); + return previousSetting; + } + + protected String setStringPreference(final String key, final String value, + final String defaultValue) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mLatinIME); + final String previousSetting = prefs.getString(key, defaultValue); + final SharedPreferences.Editor editor = prefs.edit(); + editor.putString(key, value); + editor.apply(); return previousSetting; } @@ -154,9 +172,10 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { mEditText.setEnabled(true); setupService(); mLatinIME = getService(); - final boolean previousDebugSetting = setDebugMode(true); + mPreviousDebugSetting = setDebugMode(true); + mPreviousAutoCorrectSetting = setStringPreference(PREF_AUTO_CORRECTION_THRESHOLD, + DEFAULT_AUTO_CORRECTION_THRESHOLD, DEFAULT_AUTO_CORRECTION_THRESHOLD); mLatinIME.onCreate(); - setDebugMode(previousDebugSetting); final EditorInfo ei = new EditorInfo(); final InputConnection ic = mEditText.onCreateInputConnection(ei); final LayoutInflater inflater = @@ -172,6 +191,14 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { changeLanguage("en_US"); } + @Override + protected void tearDown() { + mLatinIME.mHandler.removeAllMessages(); + setStringPreference(PREF_AUTO_CORRECTION_THRESHOLD, mPreviousAutoCorrectSetting, + DEFAULT_AUTO_CORRECTION_THRESHOLD); + setDebugMode(mPreviousDebugSetting); + } + // We need to run the messages added to the handler from LatinIME. The only way to do // that is to call Looper#loop() on the right looper, so we're going to get the looper // object and call #loop() here. The messages in the handler actually run on the UI @@ -244,7 +271,18 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { protected void changeLanguageWithoutWait(final String locale) { mEditText.mCurrentLocale = LocaleUtils.constructLocaleFromString(locale); - SubtypeSwitcher.getInstance().forceLocale(mEditText.mCurrentLocale); + final InputMethodSubtype subtype = new InputMethodSubtype( + R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark, + locale, "keyboard", "KeyboardLayoutSet=" + // TODO: this is forcing a QWERTY keyboard for all locales, which is wrong. + // It's still better than using whatever keyboard is the current one, but we + // should actually use the default keyboard for this locale. + + SubtypeLocaleUtils.QWERTY + + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE + + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, + false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */); + SubtypeSwitcher.getInstance().forceSubtype(subtype); mLatinIME.loadKeyboard(); runMessages(); mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard(); diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index c0dd9933c..6ad125053 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.utils.TextRange; import android.inputmethodservice.InputMethodService; @@ -39,7 +40,8 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // The following is meant to be a reasonable default for // the "word_separators" resource. - private static final String sSeparators = ".,:;!?-"; + private static final SettingsValues sSettings = + SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH); @Override protected void setUp() throws Exception { @@ -137,9 +139,9 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { */ public void testGetPreviousWord() { // If one of the following cases breaks, the bigram suggestions won't work. - assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSeparators, 2), "abc"); - assertNull(RichInputConnection.getNthPreviousWord("abc", sSeparators, 2)); - assertNull(RichInputConnection.getNthPreviousWord("abc. def", sSeparators, 2)); + assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSettings, 2), "abc"); + assertNull(RichInputConnection.getNthPreviousWord("abc", sSettings, 2)); + assertNull(RichInputConnection.getNthPreviousWord("abc. def", sSettings, 2)); // The following tests reflect the current behavior of the function // RichInputConnection#getNthPreviousWord. @@ -148,15 +150,15 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // this function if needed - especially since it does not seem very // logical. These tests are just there to catch any unintentional // changes in the behavior of the RichInputConnection#getPreviousWord method. - assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSeparators, 2), "abc"); - assertEquals(RichInputConnection.getNthPreviousWord("abc def.", sSeparators, 2), "abc"); - assertEquals(RichInputConnection.getNthPreviousWord("abc def .", sSeparators, 2), "def"); - assertNull(RichInputConnection.getNthPreviousWord("abc ", sSeparators, 2)); - - assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSeparators, 1), "def"); - assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSeparators, 1), "def"); - assertNull(RichInputConnection.getNthPreviousWord("abc def.", sSeparators, 1)); - assertNull(RichInputConnection.getNthPreviousWord("abc def .", sSeparators, 1)); + assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSettings, 2), "abc"); + assertEquals(RichInputConnection.getNthPreviousWord("abc def.", sSettings, 2), "abc"); + assertEquals(RichInputConnection.getNthPreviousWord("abc def .", sSettings, 2), "def"); + assertNull(RichInputConnection.getNthPreviousWord("abc ", sSettings, 2)); + + assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSettings, 1), "def"); + assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSettings, 1), "def"); + assertNull(RichInputConnection.getNthPreviousWord("abc def.", sSettings, 1)); + assertNull(RichInputConnection.getNthPreviousWord("abc def .", sSettings, 1)); } /** diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java index 1434c6b63..a67f6a4ac 100644 --- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java +++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java @@ -26,8 +26,15 @@ import android.test.suitebuilder.annotation.SmallTest; public class WordComposerTests extends AndroidTestCase { public void testMoveCursor() { final WordComposer wc = new WordComposer(); + // BMP is the Basic Multilingual Plane, as defined by Unicode. This includes + // most characters for most scripts, including all Roman alphabet languages, + // CJK, Arabic, Hebrew. Notable exceptions include some emoji and some + // very rare Chinese ideograms. BMP characters can be encoded on 2 bytes + // in UTF-16, whereas those outside the BMP need 4 bytes. + // http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane final String STR_WITHIN_BMP = "abcdef"; - wc.setComposingWord(STR_WITHIN_BMP, null); + final String PREVWORD = "prevword"; + wc.setComposingWord(STR_WITHIN_BMP, PREVWORD, null /* keyboard */); assertEquals(wc.size(), STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length())); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); @@ -43,13 +50,20 @@ public class WordComposerTests extends AndroidTestCase { // Move the cursor to after the 'f' assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + // Check the previous word is still there + assertEquals(PREVWORD, wc.getPreviousWord()); // Move the cursor past the end of the word assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15)); + // Do what LatinIME does when the cursor is moved outside of the word, + // and check the behavior is correct. + wc.reset(); + assertNull(wc.getPreviousWord()); // \uD861\uDED7 is 𨛗, a character outside the BMP final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh"; - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, + null /* keyboard */); assertEquals(wc.size(), STR_WITH_SUPPLEMENTARY_CHAR.codePointCount(0, STR_WITH_SUPPLEMENTARY_CHAR.length())); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); @@ -59,34 +73,46 @@ public class WordComposerTests extends AndroidTestCase { assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); + assertNull(wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + assertEquals(STR_WITHIN_BMP, wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, + null /* keyboard */); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); + assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */); wc.setCursorPositionWithinWord(3); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1)); + assertEquals(STR_WITHIN_BMP, wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, + null /* keyboard */); wc.setCursorPositionWithinWord(3); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9)); + assertNull(wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, + null /* keyboard */); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10)); + assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord()); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, + null /* keyboard */); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11)); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, + null /* keyboard */); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); - wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null); + wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, + null /* keyboard */); wc.setCursorPositionWithinWord(2); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0)); } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java index 32c07e106..e3ec2eca9 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java @@ -26,10 +26,10 @@ import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncodin 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; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -52,18 +52,18 @@ import java.util.TreeMap; @LargeTest public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private static final String TAG = BinaryDictDecoderEncoderTests.class.getSimpleName(); - private static final int DEFAULT_MAX_UNIGRAMS = 100; + private static final int DEFAULT_MAX_UNIGRAMS = 300; private static final int DEFAULT_CODE_POINT_SET_SIZE = 50; + private static final int LARGE_CODE_POINT_SET_SIZE = 300; private static final int UNIGRAM_FREQ = 10; private static final int BIGRAM_FREQ = 50; private static final int TOLERANCE_OF_BIGRAM_FREQ = 5; private static final int NUM_OF_NODES_HAVING_SHORTCUTS = 50; private static final int NUM_OF_SHORTCUTS = 5; - private static final int USE_BYTE_ARRAY = 1; - private static final int USE_BYTE_BUFFER = 2; - private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); + private static final ArrayList<String> sWordsWithVariousCodePoints = + CollectionUtils.newArrayList(); private static final SparseArray<List<Integer>> sEmptyBigrams = CollectionUtils.newSparseArray(); private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray(); @@ -71,21 +71,6 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { CollectionUtils.newSparseArray(); private static final HashMap<String, List<String>> sShortcuts = CollectionUtils.newHashMap(); - private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2); - private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE = - 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 FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP = - new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */, - true /* hasTimestamp */); - - private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - public BinaryDictDecoderEncoderTests() { this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS); } @@ -95,9 +80,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { Log.e(TAG, "Testing dictionary: seed is " + seed); final Random random = new Random(seed); sWords.clear(); - final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, - random); - generateWords(maxUnigrams, random, codePointSet); + sWordsWithVariousCodePoints.clear(); + generateWords(maxUnigrams, random); for (int i = 0; i < sWords.size(); ++i) { sChainBigrams.put(i, new ArrayList<Integer>()); @@ -124,23 +108,22 @@ 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) { + private void generateWords(final int number, final Random random) { + final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, + random); final Set<String> wordSet = CollectionUtils.newHashSet(); while (wordSet.size() < number) { wordSet.add(CodePointUtils.generateWord(random, codePointSet)); } sWords.addAll(wordSet); + + final int[] largeCodePointSet = CodePointUtils.generateCodePointSet( + LARGE_CODE_POINT_SET_SIZE, random); + wordSet.clear(); + while (wordSet.size() < number) { + wordSet.add(CodePointUtils.generateWord(random, largeCodePointSet)); + } + sWordsWithVariousCodePoints.addAll(wordSet); } /** @@ -186,7 +169,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { long now = -1, diff = -1; try { - final DictEncoder dictEncoder = getDictEncoder(file, formatOptions); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions, + getContext().getCacheDir()); now = System.currentTimeMillis(); // If you need to dump the dict to a textual file, uncomment the line below and the @@ -241,54 +225,21 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { private String outputOptions(final int bufferType, final FormatSpec.FormatOptions formatOptions) { String result = " : buffer type = " - + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); + + ((bufferType == BinaryDictUtils.USE_BYTE_BUFFER) ? "byte buffer" : "byte array"); result += " : version = " + formatOptions.mVersion; return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate; } - private DictionaryOptions getDictionaryOptions(final String id, final String version) { - final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(), - false, false); - options.mAttributes.put("version", version); - options.mAttributes.put("dictionary", id); - return options; - } - - private File setUpDictionaryFile(final String name, final String version) { - File file = null; - try { - file = new File(getContext().getCacheDir(), name + "." + version - + TEST_DICT_FILE_EXTENSION); - file.createNewFile(); - } catch (IOException e) { - // do nothing - } - assertTrue("Failed to create the dictionary file.", file.exists()); - 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 FormatOptions formatOptions, final DictionaryOptions dictOptions) { + final HashMap<String, List<String>> shortcutMap, final int bufferType) { long now, diff = -1; FusionDictionary dict = null; try { - final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, - dictOptions); + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType); now = System.currentTimeMillis(); dict = dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */); diff = System.currentTimeMillis() - now; @@ -310,17 +261,17 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final String dictName = "runReadAndWrite"; final String dictVersion = Long.toString(System.currentTimeMillis()); - final File file = setUpDictionaryFile(dictName, dictVersion); + final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, + getContext().getCacheDir()); final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - getDictionaryOptions(dictName, dictVersion)); + BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion)); addUnigrams(words.size(), dict, words, shortcuts); addBigrams(dict, words, bigrams); checkDictionary(dict, words, bigrams, shortcuts); final long write = timeWritingDictToFile(file, dict, formatOptions); - final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType, - formatOptions, dict.mOptions); + final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType); return "PROF: read=" + read + "ms, write=" + write + "ms :" + message + " : " + outputOptions(bufferType, formatOptions); @@ -340,6 +291,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { "chain with shortcuts")); results.add(runReadAndWrite(sWords, sStarBigrams, sShortcuts, bufferType, formatOptions, "star with shortcuts")); + results.add(runReadAndWrite(sWordsWithVariousCodePoints, sEmptyBigrams, + null /* shortcuts */, bufferType, formatOptions, + "unigram with various code points")); } // Unit test for CharEncoding.readString and CharEncoding.writeString. @@ -349,8 +303,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final byte[] buffer = new byte[50 * 3]; final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer); for (final String word : sWords) { - Log.d("testReadAndWriteString", "write : " + word); - Arrays.fill(buffer, (byte)0); + Arrays.fill(buffer, (byte) 0); CharEncoding.writeString(buffer, 0, word); dictBuffer.position(0); final String str = CharEncoding.readString(dictBuffer); @@ -361,12 +314,18 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testReadAndWriteWithByteBuffer() { final List<String> results = CollectionUtils.newArrayList(); - 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); - runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION2); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); @@ -376,12 +335,18 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testReadAndWriteWithByteArray() { final List<String> results = CollectionUtils.newArrayList(); - 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); - runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION2); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); @@ -437,8 +402,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { } private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words, - final SparseArray<List<Integer>> bigrams, final int bufferType, - final FormatOptions formatOptions, final DictionaryOptions dictOptions) { + final SparseArray<List<Integer>> bigrams, final int bufferType) { FileInputStream inStream = null; final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap(); @@ -448,8 +412,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { long now = -1, diff = -1; try { - final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions, - dictOptions); + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, bufferType); now = System.currentTimeMillis(); dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams); diff = System.currentTimeMillis() - now; @@ -476,20 +439,20 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final FormatSpec.FormatOptions formatOptions, final String message) { final String dictName = "runReadUnigrams"; final String dictVersion = Long.toString(System.currentTimeMillis()); - final File file = setUpDictionaryFile(dictName, dictVersion); + final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, + getContext().getCacheDir()); // making the dictionary from lists of words. final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - getDictionaryOptions(dictName, dictVersion)); + BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion)); addUnigrams(words.size(), dict, words, null /* shortcutMap */); addBigrams(dict, words, bigrams); timeWritingDictToFile(file, dict, formatOptions); - long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType, - formatOptions, dict.mOptions); + long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType); long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */, - bufferType, formatOptions, dict.mOptions); + bufferType); return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap + " : " + message + " : " + outputOptions(bufferType, formatOptions); @@ -508,13 +471,18 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testReadUnigramsAndBigramsBinaryWithByteBuffer() { final ArrayList<String> results = CollectionUtils.newArrayList(); - 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); - runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, - VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION2); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); @@ -524,13 +492,18 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testReadUnigramsAndBigramsBinaryWithByteArray() { final ArrayList<String> results = CollectionUtils.newArrayList(); - 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); - runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, - VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION2); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); @@ -550,7 +523,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { return null; } if (fileHeader == null) return null; - return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize, + return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mBodyOffset, address, fileHeader.mFormatOptions).mWord; } @@ -578,20 +551,21 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { final FormatOptions formatOptions, final String message) { final String dictName = "testGetTerminalPosition"; final String dictVersion = Long.toString(System.currentTimeMillis()); - final File file = setUpDictionaryFile(dictName, dictVersion); + final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, + getContext().getCacheDir()); final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - getDictionaryOptions(dictName, dictVersion)); + BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion)); addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); addBigrams(dict, words, bigrams); timeWritingDictToFile(file, dict, formatOptions); - final DictDecoder dictDecoder = getDictDecoder(file, DictDecoder.USE_BYTEARRAY, - formatOptions, dict.mOptions); + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY); try { dictDecoder.openDictBuffer(); } catch (IOException e) { - // ignore + Log.e(TAG, "IOException while opening the buffer", e); + } catch (UnsupportedFormatException e) { Log.e(TAG, "IOException while opening the buffer", e); } assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen()); @@ -638,65 +612,63 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase { public void testGetTerminalPosition() { final ArrayList<String> results = CollectionUtils.newArrayList(); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION2); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); - - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION2); - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE); - runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, BinaryDictUtils.VERSION2); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); + + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, BinaryDictUtils.VERSION2); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITHOUT_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); + runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER, + BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP); for (final String result : results) { Log.d(TAG, result); } } - private void runTestDeleteWord(final FormatOptions formatOptions) { + private void runTestDeleteWord(final FormatOptions formatOptions) + throws IOException, UnsupportedFormatException { final String dictName = "testDeleteWord"; final String dictVersion = Long.toString(System.currentTimeMillis()); - final File file = setUpDictionaryFile(dictName, dictVersion); + final File file = BinaryDictUtils.getDictFile(dictName, dictVersion, formatOptions, + getContext().getCacheDir()); final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - new FusionDictionary.DictionaryOptions( - new HashMap<String, String>(), false, false)); + BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion)); addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */); timeWritingDictToFile(file, dict, formatOptions); - final DictUpdater dictUpdater; - if (formatOptions.mVersion == 3) { - dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - } else if (formatOptions.mVersion == 4) { - dictUpdater = new Ver4DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - } else { - throw new RuntimeException("DictUpdater for version " + formatOptions.mVersion - + " doesn't exist."); - } - - try { - MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - dictUpdater.getTerminalPosition(sWords.get(0))); - dictUpdater.deleteWord(sWords.get(0)); - assertEquals(FormatSpec.NOT_VALID_WORD, - dictUpdater.getTerminalPosition(sWords.get(0))); - - MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, - dictUpdater.getTerminalPosition(sWords.get(5))); - dictUpdater.deleteWord(sWords.get(5)); - assertEquals(FormatSpec.NOT_VALID_WORD, - dictUpdater.getTerminalPosition(sWords.get(5))); - } catch (IOException e) { - } catch (UnsupportedFormatException e) { - } + final DictUpdater dictUpdater = BinaryDictUtils.getDictUpdater(file, formatOptions); + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + dictUpdater.getTerminalPosition(sWords.get(0))); + dictUpdater.deleteWord(sWords.get(0)); + assertEquals(FormatSpec.NOT_VALID_WORD, + dictUpdater.getTerminalPosition(sWords.get(0))); + + MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, + dictUpdater.getTerminalPosition(sWords.get(5))); + dictUpdater.deleteWord(sWords.get(5)); + assertEquals(FormatSpec.NOT_VALID_WORD, + dictUpdater.getTerminalPosition(sWords.get(5))); } - public void testDeleteWord() { - runTestDeleteWord(VERSION3_WITH_DYNAMIC_UPDATE); - runTestDeleteWord(VERSION4_WITH_DYNAMIC_UPDATE); + public void testDeleteWord() throws IOException, UnsupportedFormatException { + runTestDeleteWord(BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runTestDeleteWord(BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java index afe5adb73..308919499 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java @@ -23,6 +23,7 @@ import android.util.Log; 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.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -30,24 +31,16 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.HashMap; import java.util.Random; @LargeTest public class BinaryDictIOUtilsTests extends AndroidTestCase { private static final String TAG = BinaryDictIOUtilsTests.class.getSimpleName(); - private static final FormatSpec.FormatOptions FORMAT_OPTIONS = - new FormatSpec.FormatOptions(3, true); private static final ArrayList<String> sWords = CollectionUtils.newArrayList(); public static final int DEFAULT_MAX_UNIGRAMS = 1500; private final int mMaxUnigrams; - private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; - - private static final int VERSION3 = 3; - private static final int VERSION4 = 4; - private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", @@ -141,7 +134,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { int position = FormatSpec.NOT_VALID_WORD; try { - final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, + final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_READONLY_BYTEBUFFER); position = dictDecoder.getTerminalPosition(word); } catch (IOException e) { @@ -159,7 +152,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { * @throws IOException * @throws UnsupportedFormatException */ - private static PtNodeInfo findWordByBinaryDictReader(final DictDecoder dictDecoder, + private static PtNodeInfo findWordByDictDecoder(final DictDecoder dictDecoder, final String word) throws IOException, UnsupportedFormatException { int position = dictDecoder.getTerminalPosition(word); if (position != FormatSpec.NOT_VALID_WORD) { @@ -176,7 +169,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { PtNodeInfo info = null; try { dictDecoder.openDictBuffer(); - info = findWordByBinaryDictReader(dictDecoder, word); + info = findWordByDictDecoder(dictDecoder, word); } catch (IOException e) { } catch (UnsupportedFormatException e) { } @@ -186,16 +179,10 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { // return amount of time to insert a word private long insertAndCheckWord(final File file, final String word, final int frequency, final boolean exist, final ArrayList<WeightedString> bigrams, - final ArrayList<WeightedString> shortcuts, final int formatVersion) { + final ArrayList<WeightedString> shortcuts, final FormatOptions formatOptions) { long amountOfTime = -1; try { - final DictUpdater dictUpdater; - if (formatVersion == VERSION3) { - dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - } else { - throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't" - + " exist."); - } + final DictUpdater dictUpdater = BinaryDictUtils.getDictUpdater(file, formatOptions); if (!exist) { assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); @@ -212,18 +199,14 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { return amountOfTime; } - private void deleteWord(final File file, final String word, final int formatVersion) { + private void deleteWord(final File file, final String word, final FormatOptions formatOptions) { try { - final DictUpdater dictUpdater; - if (formatVersion == VERSION3) { - dictUpdater = new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); - } else { - throw new RuntimeException("DictUpdater for version " + formatVersion + " doesn't" - + " exist."); - } + final DictUpdater dictUpdater = BinaryDictUtils.getDictUpdater(file, formatOptions); dictUpdater.deleteWord(word); } catch (IOException e) { + Log.e(TAG, "Raised an IOException while deleting a word", e); } catch (UnsupportedFormatException e) { + Log.e(TAG, "Raised an UnsupportedFormatException while deleting a word", e); } } @@ -233,7 +216,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file); final FileHeader fileHeader = dictDecoder.readHeader(); assertEquals(word, - BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize, + BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mBodyOffset, position, fileHeader.mFormatOptions).mWord); } catch (IOException e) { Log.e(TAG, "Raised an IOException while looking up a word", e); @@ -242,23 +225,21 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { } } - private void runTestInsertWord(final int formatVersion) { - File file = null; - try { - file = File.createTempFile("testInsertWord", TEST_DICT_FILE_EXTENSION, - getContext().getCacheDir()); - } catch (IOException e) { - fail("IOException while creating temporary file: " + e); - } + private void runTestInsertWord(final FormatOptions formatOptions) { + final String testName = "testInsertWord"; + final String version = Long.toString(System.currentTimeMillis()); + final File file = BinaryDictUtils.getDictFile(testName, version, formatOptions, + getContext().getCacheDir()); // set an initial dictionary. final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); + BinaryDictUtils.makeDictionaryOptions(testName, version)); dict.add("abcd", 10, null, false); try { - final DictEncoder dictEncoder = new Ver3DictEncoder(file); - dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions, + getContext().getCacheDir()); + dictEncoder.writeDictionary(dict, formatOptions); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } catch (UnsupportedFormatException e) { @@ -266,54 +247,68 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { } MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd")); - insertAndCheckWord(file, "abcde", 10, false, null, null, formatVersion); + insertAndCheckWord(file, "abcde", 10, false, null, null, formatOptions); + checkReverseLookup(file, "abcde", getWordPosition(file, "abcde")); - insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null, formatVersion); + insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null, formatOptions); checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn")); - insertAndCheckWord(file, "abcdabcd", 10, false, null, null, formatVersion); + insertAndCheckWord(file, "abcdabcd", 10, false, null, null, formatOptions); checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd")); // update the existing word. - insertAndCheckWord(file, "abcdabcd", 15, true, null, null, formatVersion); + insertAndCheckWord(file, "abcdabcd", 15, true, null, null, formatOptions); + checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd")); - // split 1 - insertAndCheckWord(file, "ab", 20, false, null, null, formatVersion); + // Testing splitOnly + insertAndCheckWord(file, "ab", 20, false, null, null, formatOptions); + checkReverseLookup(file, "ab", getWordPosition(file, "ab")); + checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd")); + checkReverseLookup(file, "abcde", getWordPosition(file, "abcde")); + checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn")); - // split 2 - insertAndCheckWord(file, "ami", 30, false, null, null, formatVersion); + // Testing splitAndBranch + insertAndCheckWord(file, "ami", 30, false, null, null, formatOptions); + checkReverseLookup(file, "ami", getWordPosition(file, "ami")); + checkReverseLookup(file, "ab", getWordPosition(file, "ab")); + checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd")); + checkReverseLookup(file, "abcde", getWordPosition(file, "abcde")); + checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn")); + checkReverseLookup(file, "ami", getWordPosition(file, "ami")); + + insertAndCheckWord(file, "abcdefzzzz", 40, false, null, null, formatOptions); + checkReverseLookup(file, "abcdefzzzz", getWordPosition(file, "abcdefzzzz")); - deleteWord(file, "ami", formatVersion); + deleteWord(file, "ami", formatOptions); assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "ami")); - insertAndCheckWord(file, "abcdabfg", 30, false, null, null, formatVersion); + insertAndCheckWord(file, "abcdabfg", 30, false, null, null, formatOptions); - deleteWord(file, "abcd", formatVersion); + deleteWord(file, "abcd", formatOptions); assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd")); } public void testInsertWord() { - runTestInsertWord(VERSION3); + runTestInsertWord(BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runTestInsertWord(BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); } - private void runTestInsertWordWithBigrams(final int formatVersion) { - File file = null; - try { - file = File.createTempFile("testInsertWordWithBigrams", TEST_DICT_FILE_EXTENSION, - getContext().getCacheDir()); - } catch (IOException e) { - fail("IOException while creating temporary file: " + e); - } + private void runTestInsertWordWithBigrams(final FormatOptions formatOptions) { + final String testName = "testInsertWordWithBigrams"; + final String version = Long.toString(System.currentTimeMillis()); + File file = BinaryDictUtils.getDictFile(testName, version, formatOptions, + getContext().getCacheDir()); // set an initial dictionary. final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false)); + BinaryDictUtils.makeDictionaryOptions(testName, version)); dict.add("abcd", 10, null, false); dict.add("efgh", 15, null, false); try { - final DictEncoder dictEncoder = new Ver3DictEncoder(file); - dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions, + getContext().getCacheDir()); + dictEncoder.writeDictionary(dict, formatOptions); } catch (IOException e) { fail("IOException while writing an initial dictionary : " + e); } catch (UnsupportedFormatException e) { @@ -323,8 +318,8 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { final ArrayList<WeightedString> banana = new ArrayList<WeightedString>(); banana.add(new WeightedString("banana", 10)); - insertAndCheckWord(file, "banana", 0, false, null, null, formatVersion); - insertAndCheckWord(file, "recursive", 60, true, banana, null, formatVersion); + insertAndCheckWord(file, "banana", 0, false, null, null, formatOptions); + insertAndCheckWord(file, "recursive", 60, true, banana, null, formatOptions); final PtNodeInfo info = findWordFromFile(file, "recursive"); int bananaPos = getWordPosition(file, "banana"); @@ -334,27 +329,25 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { } public void testInsertWordWithBigrams() { - runTestInsertWordWithBigrams(VERSION3); + runTestInsertWordWithBigrams(BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runTestInsertWordWithBigrams(BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); } - private void runTestRandomWords(final int formatVersion) { - File file = null; - try { - file = File.createTempFile("testRandomWord", TEST_DICT_FILE_EXTENSION, - getContext().getCacheDir()); - } catch (IOException e) { - } - assertNotNull(file); + private void runTestRandomWords(final FormatOptions formatOptions) { + final String testName = "testRandomWord"; + final String version = Long.toString(System.currentTimeMillis()); + final File file = BinaryDictUtils.getDictFile(testName, version, formatOptions, + getContext().getCacheDir()); // set an initial dictionary. final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), - new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false, - false)); + BinaryDictUtils.makeDictionaryOptions(testName, version)); dict.add("initial", 10, null, false); try { - final DictEncoder dictEncoder = new Ver3DictEncoder(file); - dictEncoder.writeDictionary(dict, FORMAT_OPTIONS); + final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions, + getContext().getCacheDir()); + dictEncoder.writeDictionary(dict, formatOptions); } catch (IOException e) { assertTrue(false); } catch (UnsupportedFormatException e) { @@ -366,7 +359,7 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { int cnt = 0; for (final String word : sWords) { final long diff = insertAndCheckWord(file, word, - cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null, formatVersion); + cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null, formatOptions); maxTimeToInsert = Math.max(maxTimeToInsert, diff); minTimeToInsert = Math.min(minTimeToInsert, diff); sum += diff; @@ -377,13 +370,14 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase { MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word)); } - Log.d(TAG, "Test version " + formatVersion); + Log.d(TAG, "Test version " + formatOptions.mVersion); Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms."); Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms."); Log.d(TAG, "avg = " + ((double)sum/mMaxUnigrams/1000000) + " ms."); } public void testRandomWords() { - runTestRandomWords(VERSION3); + runTestRandomWords(BinaryDictUtils.VERSION3_WITH_DYNAMIC_UPDATE); + runTestRandomWords(BinaryDictUtils.VERSION4_WITH_DYNAMIC_UPDATE); } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java new file mode 100644 index 000000000..ad17a7118 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; +import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; +import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; + +import java.io.File; +import java.util.HashMap; + +public class BinaryDictUtils { + public static final int USE_BYTE_ARRAY = 1; + public static final int USE_BYTE_BUFFER = 2; + + public static final String TEST_DICT_FILE_EXTENSION = ".testDict"; + + public static final FormatSpec.FormatOptions VERSION2 = + new FormatSpec.FormatOptions(FormatSpec.VERSION2); + public static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(FormatSpec.VERSION3, false /* supportsDynamicUpdate */); + public static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(FormatSpec.VERSION3, true /* supportsDynamicUpdate */); + public static final FormatSpec.FormatOptions VERSION4_WITHOUT_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* supportsDynamicUpdate */); + public static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE = + new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* supportsDynamicUpdate */); + public static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE_AND_TIMESTAMP = + new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* supportsDynamicUpdate */, + true /* hasTimestamp */); + + public static DictionaryOptions makeDictionaryOptions(final String id, final String version) { + final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(), + false /* germanUmlautProcessing */, false /* frenchLigatureProcessing */); + options.mAttributes.put(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, "en_US"); + options.mAttributes.put(FileHeader.DICTIONARY_ID_ATTRIBUTE, id); + options.mAttributes.put(FileHeader.DICTIONARY_VERSION_ATTRIBUTE, version); + return options; + } + + public static File getDictFile(final String name, final String version, + final FormatOptions formatOptions, final File directory) { + if (formatOptions.mVersion == FormatSpec.VERSION2 + || formatOptions.mVersion == FormatSpec.VERSION3) { + return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION); + } else if (formatOptions.mVersion == FormatSpec.VERSION4) { + return new File(directory, name + "." + version); + } else { + throw new RuntimeException("the format option has a wrong version : " + + formatOptions.mVersion); + } + } + + public static DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions, + final File cacheDir) { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + return new Ver4DictEncoder(cacheDir); + } else if (formatOptions.mVersion == FormatSpec.VERSION3 + || formatOptions.mVersion == FormatSpec.VERSION2) { + return new Ver3DictEncoder(file); + } else { + throw new RuntimeException("The format option has a wrong version : " + + formatOptions.mVersion); + } + } + + public static DictUpdater getDictUpdater(final File file, final FormatOptions formatOptions) + throws UnsupportedFormatException { + if (formatOptions.mVersion == FormatSpec.VERSION4) { + return new Ver4DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); + } else if (formatOptions.mVersion == FormatSpec.VERSION3) { + return new Ver3DictUpdater(file, DictDecoder.USE_WRITABLE_BYTEBUFFER); + } else { + throw new UnsupportedFormatException("The format option has a wrong version : " + + formatOptions.mVersion); + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index 7c1decb71..17423a7ba 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -16,8 +16,6 @@ package com.android.inputmethod.latin.personalization; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; @@ -28,6 +26,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -38,21 +37,12 @@ import java.util.concurrent.TimeUnit; @LargeTest public class UserHistoryDictionaryTests extends AndroidTestCase { private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); - private SharedPreferences mPrefs; private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; - private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000; - private static final int WAIT_TERMINATING_IN_MILLISECONDS = 100; - - @Override - public void setUp() { - mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - } - /** * Generates a random word. */ @@ -78,7 +68,8 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private void addToDict(final UserHistoryDictionary dict, final List<String> words) { String prevWord = null; for (String word : words) { - dict.addToDictionary(prevWord, word, true); + dict.addToDictionary(prevWord, word, true, + (int)TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); prevWord = word; } } @@ -92,14 +83,11 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final List<String> words = generateWords(numberOfWords, random); final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffix /* locale */, mPrefs); + new Locale(testFilenameSuffix)); // Add random words to the user history dictionary. addToDict(dict, words); if (checkContents) { - try { - Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS)); - } catch (InterruptedException e) { - } + dict.waitAllTasksForTests(); for (int i = 0; i < numberOfWords; ++i) { final String word = words.get(i); assertTrue(dict.isInDictionaryForTests(word)); @@ -116,7 +104,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private void clearHistory(final String testFilenameSuffix) { final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffix /* locale */, mPrefs); + new Locale(testFilenameSuffix)); dict.clearAndFlushDictionary(); dict.close(); } @@ -126,23 +114,16 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { * @param testFilenameSuffix file name suffix used for testing. */ private void waitForWriting(final String testFilenameSuffix) { - try { - final UserHistoryDictionary dict = - PersonalizationHelper.getUserHistoryDictionary(getContext(), - testFilenameSuffix, mPrefs); - dict.shutdownExecutorForTests(); - while (!dict.isTerminatedForTests()) { - Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS); - } - } catch (InterruptedException e) { - Log.d(TAG, "InterruptedException: ", e); - } + final UserHistoryDictionary dict = + PersonalizationHelper.getUserHistoryDictionary(getContext(), + new Locale(testFilenameSuffix)); + dict.waitAllTasksForTests(); } public void testRandomWords() { Log.d(TAG, "This test can be used for profiling."); Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); - final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); + final String testFilenameSuffix = "test_random_words" + System.currentTimeMillis(); final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; @@ -159,7 +140,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final File dictFile = new File(getContext().getFilesDir(), fileName); if (dictFile != null) { assertTrue(dictFile.exists()); - assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); dictFile.delete(); } } @@ -177,9 +157,9 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { // Create filename suffixes for this test. for (int i = 0; i < numberOfLanguages; i++) { - testFilenameSuffixes[i] = "testSwitchingLanguages" + i; - final String fileName = UserHistoryDictionary.NAME + "." + - testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; + testFilenameSuffixes[i] = "test_switching_languages" + i; + final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffixes[i] + + ExpandableBinaryDictionary.DICT_FILE_EXTENSION; dictFiles[i] = new File(getContext().getFilesDir(), fileName); clearHistory(testFilenameSuffixes[i]); } @@ -205,7 +185,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { for (final File file : dictFiles) { if (file != null) { assertTrue(file.exists()); - assertTrue(file.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE); file.delete(); } } @@ -213,10 +192,8 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { } public void testAddManyWords() { - final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis(); - final int numberOfWords = - ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ? - 10000 : 1000; + final String testFilenameSuffix = "test_random_words" + System.currentTimeMillis(); + final int numberOfWords = 10000; final Random random = new Random(123456); clearHistory(testFilenameSuffix); try { @@ -230,7 +207,6 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { final File 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/StringAndJsonUtilsTests.java index 4e396a1cf..2123e84e8 100644 --- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Locale; @SmallTest -public class StringUtilsTests extends AndroidTestCase { +public class StringAndJsonUtilsTests extends AndroidTestCase { public void testContainsInArray() { assertFalse("empty array", StringUtils.containsInArray("key", new String[0])); assertFalse("not in 1 element", StringUtils.containsInArray("key", new String[] { @@ -44,7 +44,7 @@ public class StringUtilsTests extends AndroidTestCase { })); } - public void testContainsInExtraValues() { + public void testContainsInCommaSplittableText() { assertFalse("null", StringUtils.containsInCommaSplittableText("key", null)); assertFalse("empty", StringUtils.containsInCommaSplittableText("key", "")); assertFalse("not in 1 element", @@ -56,7 +56,28 @@ public class StringUtilsTests extends AndroidTestCase { assertTrue("in 2 elements", StringUtils.containsInCommaSplittableText("key", "key1,key")); } - public void testAppendToExtraValuesIfNotExists() { + public void testJoinCommaSplittableText() { + assertEquals("2 nulls", "", + StringUtils.joinCommaSplittableText(null, null)); + assertEquals("null and empty", "", + StringUtils.joinCommaSplittableText(null, "")); + assertEquals("empty and null", "", + StringUtils.joinCommaSplittableText("", null)); + assertEquals("2 empties", "", + StringUtils.joinCommaSplittableText("", "")); + assertEquals("text and null", "text", + StringUtils.joinCommaSplittableText("text", null)); + assertEquals("text and empty", "text", + StringUtils.joinCommaSplittableText("text", "")); + assertEquals("null and text", "text", + StringUtils.joinCommaSplittableText(null, "text")); + assertEquals("empty and text", "text", + StringUtils.joinCommaSplittableText("", "text")); + assertEquals("2 texts", "text1,text2", + StringUtils.joinCommaSplittableText("text1", "text2")); + } + + public void testAppendToCommaSplittableTextIfNotExists() { assertEquals("null", "key", StringUtils.appendToCommaSplittableTextIfNotExists("key", null)); assertEquals("empty", "key", @@ -77,7 +98,7 @@ public class StringUtilsTests extends AndroidTestCase { StringUtils.appendToCommaSplittableTextIfNotExists("key", "key1,key,key3")); } - public void testRemoveFromExtraValuesIfExists() { + public void testRemoveFromCommaSplittableTextIfExists() { assertEquals("null", "", StringUtils.removeFromCommaSplittableTextIfExists("key", null)); assertEquals("empty", "", StringUtils.removeFromCommaSplittableTextIfExists("key", "")); @@ -271,11 +292,11 @@ public class StringUtilsTests extends AndroidTestCase { assertTrue(bytesStr.equals(bytesStr2)); } - public void testJsonStringUtils() { + public void testJsonUtils() { 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); + final String str = JsonUtils.listToJsonStr(objArray); + final List<Object> newObjArray = JsonUtils.jsonStrToList(str); for (int i = 0; i < objs.length; ++i) { assertEquals(objs[i], newObjArray.get(i)); } diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java index 856b2dbda..ccdd56750 100644 --- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java @@ -41,7 +41,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { InputMethodSubtype ES_US; InputMethodSubtype FR; InputMethodSubtype FR_CA; + InputMethodSubtype FR_CH; InputMethodSubtype DE; + InputMethodSubtype DE_CH; InputMethodSubtype ZZ; InputMethodSubtype DE_QWERTY; InputMethodSubtype FR_QWERTZ; @@ -70,8 +72,12 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { Locale.FRENCH.toString(), "azerty"); FR_CA = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( Locale.CANADA_FRENCH.toString(), "qwerty"); + FR_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "fr_CH", "swiss"); DE = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( Locale.GERMAN.toString(), "qwertz"); + DE_CH = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( + "de_CH", "swiss"); ZZ = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"); DE_QWERTY = AdditionalSubtypeUtils.createAdditionalSubtype( @@ -112,7 +118,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("es_US", "spanish", SubtypeLocaleUtils.getKeyboardLayoutSetName(ES_US)); assertEquals("fr ", "azerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR)); assertEquals("fr_CA", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CA)); + assertEquals("fr_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(FR_CH)); assertEquals("de ", "qwertz", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE)); + assertEquals("de_CH", "swiss", SubtypeLocaleUtils.getKeyboardLayoutSetName(DE_CH)); assertEquals("zz ", "qwerty", SubtypeLocaleUtils.getKeyboardLayoutSetName(ZZ)); } @@ -125,7 +133,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // es_US spanish F Spanish (US) exception // fr azerty F French // fr_CA qwerty F French (Canada) + // fr_CH swiss F French (Switzerland) // de qwertz F German + // de_CH swiss F German (Switzerland) // zz qwerty F Alphabet (QWERTY) // fr qwertz T French (QWERTZ) // de qwerty T German (QWERTY) @@ -148,8 +158,12 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR)); assertEquals("fr_CA", "French (Canada)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA)); + assertEquals("fr_CH", "French (Switzerland)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH)); assertEquals("de ", "German", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); + assertEquals("de_CH", "German (Switzerland)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); assertEquals("zz ", "Alphabet (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); return null; @@ -189,7 +203,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // es_US spanish F Espagnol (États-Unis) exception // fr azerty F Français // fr_CA qwerty F Français (Canada) + // fr_CH swiss F Français (Suisse) // de qwertz F Allemand + // de_CH swiss F Allemand (Suisse) // zz qwerty F Aucune langue (QWERTY) // fr qwertz T Français (QWERTZ) // de qwerty T Allemand (QWERTY) @@ -212,8 +228,12 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR)); assertEquals("fr_CA", "Français (Canada)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA)); + assertEquals("fr_CH", "Français (Suisse)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CH)); assertEquals("de ", "Allemand", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE)); + assertEquals("de_CH", "Allemand (Suisse)", + SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE_CH)); assertEquals("zz ", "Alphabet latin (QWERTY)", SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ)); return null; @@ -300,7 +320,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { // es_US spanish F Es Español Español (EE.UU.) exception // fr azerty F Fr Français Français // fr_CA qwerty F Fr Français Français (Canada) + // fr_CH swiss F Fr Français Français (Suisse) // de qwertz F De Deutsch Deutsch + // de_CH swiss F De Deutsch Deutsch (Schweiz) // zz qwerty F QWERTY QWERTY // fr qwertz T Fr Français Français // de qwerty T De Deutsch Deutsch @@ -317,7 +339,11 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("fr ", "Français", SubtypeLocaleUtils.getFullDisplayName(FR)); assertEquals("fr_CA", "Français (Canada)", SubtypeLocaleUtils.getFullDisplayName(FR_CA)); + assertEquals("fr_CH", "Français (Suisse)", + SubtypeLocaleUtils.getFullDisplayName(FR_CH)); assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getFullDisplayName(DE)); + assertEquals("de_CH", "Deutsch (Schweiz)", + SubtypeLocaleUtils.getFullDisplayName(DE_CH)); assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getFullDisplayName(ZZ)); assertEquals("en_US", "English", SubtypeLocaleUtils.getMiddleDisplayName(EN_US)); @@ -325,7 +351,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("es_US", "Español", SubtypeLocaleUtils.getMiddleDisplayName(ES_US)); assertEquals("fr ", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR)); assertEquals("fr_CA", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CA)); + assertEquals("fr_CH", "Français", SubtypeLocaleUtils.getMiddleDisplayName(FR_CH)); assertEquals("de ", "Deutsch", SubtypeLocaleUtils.getMiddleDisplayName(DE)); + assertEquals("de_CH", "Deutsch", SubtypeLocaleUtils.getMiddleDisplayName(DE_CH)); assertEquals("zz ", "QWERTY", SubtypeLocaleUtils.getMiddleDisplayName(ZZ)); assertEquals("en_US", "En", SubtypeLocaleUtils.getShortDisplayName(EN_US)); @@ -333,7 +361,9 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase { assertEquals("es_US", "Es", SubtypeLocaleUtils.getShortDisplayName(ES_US)); assertEquals("fr ", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR)); assertEquals("fr_CA", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CA)); + assertEquals("fr_CH", "Fr", SubtypeLocaleUtils.getShortDisplayName(FR_CH)); assertEquals("de ", "De", SubtypeLocaleUtils.getShortDisplayName(DE)); + assertEquals("de_CH", "De", SubtypeLocaleUtils.getShortDisplayName(DE_CH)); assertEquals("zz ", "", SubtypeLocaleUtils.getShortDisplayName(ZZ)); return null; } diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java index 1944fd332..fc921b4fd 100644 --- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java @@ -24,8 +24,10 @@ import android.util.Log; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictEncoder; import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.Ver3DictDecoder; import com.android.inputmethod.latin.makedict.Ver3DictEncoder; import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList; @@ -52,6 +54,12 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase private static final ArrayList<String> NOT_HAVE_BIGRAM = new ArrayList<String>(); private static final FormatSpec.FormatOptions FORMAT_OPTIONS = new FormatSpec.FormatOptions(2); private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; + private static final HashMap<String, String> HEADER_OPTIONS = new HashMap<String, String>(); + static { + HEADER_OPTIONS.put(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, "en_US"); + HEADER_OPTIONS.put(FileHeader.DICTIONARY_ID_ATTRIBUTE, "test"); + HEADER_OPTIONS.put(FileHeader.DICTIONARY_VERSION_ATTRIBUTE, "1000"); + } /** * Return same frequency for all words and bigrams @@ -139,18 +147,14 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase private void writeDictToFile(final File file, final UserHistoryDictionaryBigramList bigramList) { final DictEncoder dictEncoder = new Ver3DictEncoder(file); - UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS); + UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS, + HEADER_OPTIONS); } - private void readDictFromFile(final File file, final OnAddWordListener listener) { + private void readDictFromFile(final File file, final OnAddWordListener listener) + throws IOException, FileNotFoundException, UnsupportedFormatException { final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY); - try { - dictDecoder.openDictBuffer(); - } catch (FileNotFoundException e) { - Log.e(TAG, "file not found", e); - } catch (IOException e) { - Log.e(TAG, "IOException", e); - } + dictDecoder.openDictBuffer(); UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener); } @@ -163,13 +167,14 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase addBigramToBigramList("this", "was", addedWords, originalList); addBigramToBigramList("hello", "world", addedWords, originalList); - final FusionDictionary fusionDict = - UserHistoryDictIOUtils.constructFusionDictionary(this, originalList); + final FusionDictionary fusionDict = UserHistoryDictIOUtils.constructFusionDictionary( + this, originalList, HEADER_OPTIONS); checkWordsInFusionDict(fusionDict, addedWords); } - public void testReadAndWrite() { + public void testReadAndWrite() throws IOException, FileNotFoundException, + UnsupportedFormatException { final Context context = getContext(); File file = null; diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index 3d09c0508..895e4a231 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -30,6 +30,7 @@ MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_CORE_SOURCE_DIRECTORY)/makedict USED_TARGETTED_UTILS := \ $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ByteArrayDictBuffer.java \ $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/CollectionUtils.java \ + $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/FileUtils.java \ $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/JniUtils.java DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \ 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 5c7e8b4f2..b3543a0c2 100644 --- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java +++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java @@ -159,9 +159,9 @@ public class DictionaryMaker { if (OPTION_VERSION_2.equals(arg)) { // Do nothing, this is the default } else if (OPTION_VERSION_3.equals(arg)) { - outputBinaryFormatVersion = 3; + outputBinaryFormatVersion = FormatSpec.VERSION3; } else if (OPTION_VERSION_4.equals(arg)) { - outputBinaryFormatVersion = 4; + outputBinaryFormatVersion = FormatSpec.VERSION4; } else if (OPTION_HELP.equals(arg)) { displayHelp(); } else { @@ -358,7 +358,7 @@ public class DictionaryMaker { final File outputFile = new File(outputFilename); final FormatSpec.FormatOptions formatOptions = new FormatSpec.FormatOptions(version); final DictEncoder dictEncoder; - if (version == 4) { + if (version == FormatSpec.VERSION4) { dictEncoder = new Ver4DictEncoder(outputFile); } else { dictEncoder = new Ver3DictEncoder(outputFile); diff --git a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl index 4cd9c236b..a8ac981e0 100644 --- a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl +++ b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl @@ -87,11 +87,11 @@ public final class KeyboardTextsSet { return (text == null) ? LANGUAGE_DEFAULT[id] : text; } + // These texts' name should be aligned with the @string/<name> in + // values*/strings-action-keys.xml. private static final String[] RESOURCE_NAMES = { - // These texts' name should be aligned with the @string/<name> in values/strings.xml. // Labels for action. "label_go_key", - // "label_search_key", "label_send_key", "label_next_key", "label_done_key", @@ -113,6 +113,8 @@ public final class KeyboardTextsSet { }; /* @TEXTS@ */ + // TODO: Use the language + "_" + region representation for the locale string key. + // Currently we are dropping the region from the key. private static final Object[] LANGUAGES_AND_TEXTS = { /* @LANGUAGES_AND_TEXTS@ */ }; diff --git a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml index 8b86b1ba2..f8050a395 100644 --- a/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml @@ -65,24 +65,27 @@ <!-- U+060C: "،" ARABIC COMMA --> <string name="keylabel_for_comma">،</string> <string name="more_keys_for_comma">"\\,"</string> + <!-- U+0651: "ّ" ARABIC SHADDA --> + <string name="keyhintlabel_for_period">ّ</string> + <string name="more_keys_for_period">!text/more_keys_for_arabic_diacritics</string> + <string name="keyhintlabel_for_tablet_period">ّ</string> + <string name="more_keys_for_tablet_period">!text/more_keys_for_arabic_diacritics</string> <string name="keylabel_for_symbols_question">؟</string> <string name="keylabel_for_symbols_semicolon">؛</string> <!-- U+066A: "٪" ARABIC PERCENT SIGN --> <string name="keylabel_for_symbols_percent">٪</string> - <string name="more_keys_for_symbols_question">\?</string> + <!-- U+00BF: "¿" INVERTED QUESTION MARK --> + <string name="more_keys_for_question">\?,¿</string> <string name="more_keys_for_symbols_semicolon">;</string> <!-- U+2030: "‰" PER MILLE SIGN --> <string name="more_keys_for_symbols_percent">\\%,‰</string> - <!-- U+060C: "،" ARABIC COMMA - U+061B: "؛" ARABIC SEMICOLON - U+061F: "؟" ARABIC QUESTION MARK --> - <string name="keylabel_for_apostrophe">،</string> - <string name="keyhintlabel_for_apostrophe">؟</string> <!-- U+061F: "؟" ARABIC QUESTION MARK U+060C: "،" ARABIC COMMA U+061B: "؛" ARABIC SEMICOLON --> + <string name="keylabel_for_tablet_comma">"،"</string> + <string name="keyhintlabel_for_tablet_comma">"؟"</string> + <string name="more_keys_for_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,/,\",\'"</string> <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,،,؟,\@,&,\\%,+,؛,/,(|),)|("</string> - <string name="more_keys_for_apostrophe">"؟,؛,!,:,-,/,\',\""</string> <!-- U+266A: "♪" EIGHTH NOTE --> <string name="more_keys_for_bullet">♪</string> <!-- U+2605: "★" BLACK STAR @@ -121,5 +124,4 @@ <!-- In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. --> <!-- Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. --> <string name="more_keys_for_arabic_diacritics">"!fixedColumnOrder!7, ٕ|ٕ, ٔ|ٔ, ْ|ْ, ٍ|ٍ, ٌ|ٌ, ً|ً, ّ|ّ, ٖ|ٖ, ٰ|ٰ, ٓ|ٓ, ِ|ِ, ُ|ُ, َ|َ,ـــ|ـ"</string> - <string name="keyhintlabel_for_arabic_diacritics">ّ</string> </resources> diff --git a/tools/make-keyboard-text/res/values-be/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml index 4723503f1..4723503f1 100644 --- a/tools/make-keyboard-text/res/values-be/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml index 66393732c..f63e85bd5 100644 --- a/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml @@ -72,7 +72,7 @@ <string name="more_keys_for_l">l·l,ł</string> <!-- U+00B7: "·" MIDDLE DOT --> <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,;,/,(,),#,·,!,\\,,\?,&,\\%,+,\",-,:,',\@"</string> - <string name="more_keys_for_period">\?,·</string> + <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!8,;,/,(,),#,·,',\\,,&,\\%,+,\",-,:,\@"</string> <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA --> <string name="keylabel_for_spanish_row2_10">ç</string> </resources> diff --git a/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml index 9dc8717ec..bb8bb7201 100644 --- a/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml @@ -55,6 +55,18 @@ <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE --> <string name="more_keys_for_n">ñ,ń</string> + <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS --> + <string name="keylabel_for_swiss_row1_11">ü</string> + <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE --> + <string name="more_keys_for_swiss_row1_11">è</string> + <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS --> + <string name="keylabel_for_swiss_row2_10">ö</string> + <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE --> + <string name="more_keys_for_swiss_row2_10">é</string> + <!-- U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS --> + <string name="keylabel_for_swiss_row2_11">ä</string> + <!-- U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE --> + <string name="more_keys_for_swiss_row2_11">à</string> <string name="single_quotes">!text/single_9qm_lqm</string> <string name="double_quotes">!text/double_9qm_lqm</string> <string name="single_angle_quotes">!text/single_raqm_laqm</string> diff --git a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml index 8e6b4ee06..f4fe7f787 100644 --- a/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml @@ -67,16 +67,7 @@ U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE U+010D: "č" LATIN SMALL LETTER C WITH CARON --> <string name="more_keys_for_c">ç,ć,č</string> - <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> - <string name="keylabel_for_spanish_row2_10">ñ</string> <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK U+00BF: "¿" INVERTED QUESTION MARK --> - <string name="more_keys_for_punctuation">"!fixedColumnOrder!4,;,!,\\,,\?,:,¡,\@,¿"</string> - <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK --> - <string name="more_keys_for_tablet_comma">"!,¡"</string> - <!-- U+00BF: "¿" INVERTED QUESTION MARK --> - <string name="more_keys_for_period">"\?,¿"</string> - <string name="keylabel_for_apostrophe">\"</string> - <string name="keyhintlabel_for_apostrophe">\'</string> - <string name="more_keys_for_apostrophe">\'</string> + <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,¡,;,/,(,),#,!,\\,,\?,¿,&,\\%,+,\",-,:,',\@"</string> </resources> diff --git a/tools/make-keyboard-text/res/values-et/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-et-rEE/donottranslate-more-keys.xml index d037044bd..d037044bd 100644 --- a/tools/make-keyboard-text/res/values-et/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-et-rEE/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml index ab4fbda44..5bcfe7fc9 100644 --- a/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml @@ -65,11 +65,17 @@ <!-- U+060C: "،" ARABIC COMMA --> <string name="keylabel_for_comma">،</string> <string name="more_keys_for_comma">"\\,"</string> + <!-- U+064B: "ً" ARABIC FATHATAN --> + <string name="keyhintlabel_for_period">ً</string> + <string name="more_keys_for_period">!text/more_keys_for_arabic_diacritics</string> + <string name="keyhintlabel_for_tablet_period">ً</string> + <string name="more_keys_for_tablet_period">!text/more_keys_for_arabic_diacritics</string> <string name="keylabel_for_symbols_question">؟</string> <string name="keylabel_for_symbols_semicolon">؛</string> <!-- U+066A: "٪" ARABIC PERCENT SIGN --> <string name="keylabel_for_symbols_percent">٪</string> - <string name="more_keys_for_symbols_question">\?</string> + <!-- U+00BF: "¿" INVERTED QUESTION MARK --> + <string name="more_keys_for_question">\?,¿</string> <string name="more_keys_for_symbols_semicolon">;</string> <!-- U+2030: "‰" PER MILLE SIGN --> <string name="more_keys_for_symbols_percent">\\%,‰</string> @@ -79,13 +85,9 @@ U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> <string name="keylabel_for_tablet_comma">"،"</string> - <string name="keyhintlabel_for_tablet_comma">"!"</string> - <string name="more_keys_for_tablet_comma">"!,\\,"</string> + <string name="keyhintlabel_for_tablet_comma">"؟"</string> + <string name="more_keys_for_tablet_comma">"!fixedColumnOrder!4,:,!,؟,؛,-,/,«|»,»|«"</string> <string name="keyhintlabel_for_period">"؟"</string> - <string name="more_keys_for_period">"؟,\?"</string> - <string name="keylabel_for_apostrophe">،</string> - <string name="keyhintlabel_for_apostrophe">؟</string> - <string name="more_keys_for_apostrophe">"!fixedColumnOrder!4,:,!,؟,؛,-,/,«|»,»|«"</string> <!-- U+FDFC: "﷼" RIAL SIGN --> <string name="keylabel_for_currency">﷼</string> <!-- U+061F: "؟" ARABIC QUESTION MARK @@ -130,5 +132,4 @@ <!-- In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label. --> <!-- Note: The space character is needed as a preceding letter to draw Arabic diacritics characters correctly. --> <string name="more_keys_for_arabic_diacritics">"!fixedColumnOrder!7, ٕ|ٕ, ْ|ْ, ّ|ّ, ٌ|ٌ, ٍ|ٍ, ً|ً, ٔ|ٔ, ٖ|ٖ, ٰ|ٰ, ٓ|ٓ, ُ|ُ, ِ|ِ, َ|َ,ـــ|ـ"</string> - <string name="keyhintlabel_for_arabic_diacritics">ً</string> </resources> diff --git a/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml index 7b11a183d..665677698 100644 --- a/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml @@ -65,4 +65,16 @@ <string name="more_keys_for_c">ç,ć,č</string> <!-- U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS --> <string name="more_keys_for_y">%,ÿ</string> + <!-- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE --> + <string name="keylabel_for_swiss_row1_11">è</string> + <!-- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS --> + <string name="more_keys_for_swiss_row1_11">ü</string> + <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE --> + <string name="keylabel_for_swiss_row2_10">é</string> + <!-- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS --> + <string name="more_keys_for_swiss_row2_10">ö</string> + <!-- U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE --> + <string name="keylabel_for_swiss_row2_11">à</string> + <!-- U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS --> + <string name="more_keys_for_swiss_row2_11">ä</string> </resources> diff --git a/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml index 2f34128bd..a94d50e53 100644 --- a/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-hy-rAM/donottranslate-more-keys.xml @@ -29,12 +29,18 @@ <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> + <string name="more_keys_for_question">՞,¿</string> <!-- U+055C: "՜" ARMENIAN EXCLAMATION MARK --> <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK --> - <string name="more_keys_for_symbols_exclamation">՜,¡</string> + <string name="more_keys_for_exclamation">՜,¡</string> <!-- U+058F: "֏" ARMENIAN DRAM SIGN --> <!-- TODO: Enable this when we have glyph for the following letter <string name="keylabel_for_currency">֏</string> --> + <!-- U+055D: "՝" ARMENIAN COMMA --> + <string name="keylabel_for_tablet_comma">՝</string> + <!-- U+0589: "։" ARMENIAN FULL STOP --> + <string name="keylabel_for_period">։</string> + <string name="keylabel_for_tablet_period">։</string> + <string name="more_keys_for_tablet_period">!text/more_keys_for_punctuation</string> </resources> diff --git a/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml index 994e35ae9..e231a398e 100644 --- a/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml @@ -29,6 +29,7 @@ U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN --> <string name="more_keys_for_plus">±,﬩</string> <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,;,/,(|),)|(,#,!,\\,,\?,&,\\%,+,\",-,:,',\@"</string> + <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!7,;,/,(|),)|(,#,',\\,,&,\\%,+,\",-,:,\@"</string> <!-- The all letters need to be mirrored are found at http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt --> <string name="more_keys_for_left_parenthesis">!fixedColumnOrder!3,<|>,{|},[|]</string> @@ -54,8 +55,4 @@ <string name="double_angle_quotes">!text/double_laqm_raqm_rtl</string> <!-- U+20AA: "₪" NEW SHEQEL SIGN --> <string name="keylabel_for_currency">₪</string> - <string name="keyhintlabel_for_tablet_comma">!</string> - <string name="more_keys_for_tablet_comma">!</string> - <string name="keyhintlabel_for_period">\?</string> - <string name="more_keys_for_period">\?</string> </resources> diff --git a/tools/make-keyboard-text/res/values-ka/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ka-rGE/donottranslate-more-keys.xml index 8c2add44c..8c2add44c 100644 --- a/tools/make-keyboard-text/res/values-ka/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ka-rGE/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-km-rKH/donottranslate-more-keys.xml index c33831c56..c33831c56 100644 --- a/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-km-rKH/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values-lo/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-lo-rLA/donottranslate-more-keys.xml index 1d8ffa8cf..1d8ffa8cf 100644 --- a/tools/make-keyboard-text/res/values-lo/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-lo-rLA/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values-mn/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-mn-rMN/donottranslate-more-keys.xml index a7f366685..a7f366685 100644 --- a/tools/make-keyboard-text/res/values-mn/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-mn-rMN/donottranslate-more-keys.xml diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml index 3c59b4bd1..ceb46dcd2 100644 --- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml @@ -63,6 +63,12 @@ <string name="keylabel_for_south_slavic_row3_8"></string> <string name="more_keys_for_cyrillic_ie"></string> <string name="more_keys_for_cyrillic_i"></string> + <string name="keylabel_for_swiss_row1_11"></string> + <string name="keylabel_for_swiss_row2_10"></string> + <string name="keylabel_for_swiss_row2_11"></string> + <string name="more_keys_for_swiss_row1_11"></string> + <string name="more_keys_for_swiss_row2_10"></string> + <string name="more_keys_for_swiss_row2_11"></string> <!-- Label for "switch to alphabetic" key. --> <string name="label_to_alpha_key">ABC</string> <string name="single_quotes">!text/single_lqm_rqm</string> @@ -78,6 +84,7 @@ <string name="keylabel_for_currency">$</string> <string name="more_keys_for_currency">$,¢,€,£,¥,₱</string> <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,;,/,(,),#,!,\\,,\?,&,\\%,+,\",-,:,',\@"</string> + <string name="more_keys_for_tablet_punctuation">"!fixedColumnOrder!7,;,/,(,),#,',\\,,&,\\%,+,\",-,:,\@"</string> <!-- U+2020: "†" DAGGER U+2021: "‡" DOUBLE DAGGER U+2605: "★" BLACK STAR --> @@ -103,7 +110,6 @@ <string name="more_keys_for_less_than">!fixedColumnOrder!3,‹,≤,«</string> <string name="more_keys_for_greater_than">!fixedColumnOrder!3,›,≥,»</string> <string name="more_keys_for_arabic_diacritics"></string> - <string name="keyhintlabel_for_arabic_diacritics"></string> <string name="keylabel_for_symbols_1">1</string> <string name="keylabel_for_symbols_2">2</string> <string name="keylabel_for_symbols_3">3</string> @@ -154,34 +160,37 @@ <!-- U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N U+2205: "∅" EMPTY SET --> <string name="more_keys_for_symbols_0">ⁿ,∅</string> + <!-- Comma key --> <string name="keylabel_for_comma">,</string> <string name="more_keys_for_comma"></string> + <string name="keylabel_for_tablet_comma">,</string> + <string name="keyhintlabel_for_tablet_comma"></string> + <string name="more_keys_for_tablet_comma"></string> + <!-- Period key --> + <string name="keylabel_for_period">.</string> + <string name="keyhintlabel_for_period"></string> + <string name="more_keys_for_period">!text/more_keys_for_punctuation</string> + <string name="keylabel_for_tablet_period">.</string> + <string name="keyhintlabel_for_tablet_period"></string> + <string name="more_keys_for_tablet_period">!text/more_keys_for_tablet_punctuation</string> <string name="keylabel_for_symbols_question">\?</string> <string name="keylabel_for_symbols_semicolon">;</string> <string name="keylabel_for_symbols_percent">%</string> <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK --> - <string name="more_keys_for_symbols_exclamation">¡</string> + <string name="more_keys_for_exclamation">¡</string> <!-- U+00BF: "¿" INVERTED QUESTION MARK --> - <string name="more_keys_for_symbols_question">¿</string> + <string name="more_keys_for_question">¿</string> <string name="more_keys_for_symbols_semicolon"></string> <!-- U+2030: "‰" PER MILLE SIGN --> <string name="more_keys_for_symbols_percent">‰</string> - <string name="keylabel_for_tablet_comma">,</string> - <string name="keyhintlabel_for_tablet_comma"></string> - <string name="more_keys_for_tablet_comma"></string> - <string name="keyhintlabel_for_period"></string> - <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> - <string name="more_keys_for_period">…</string> - <string name="keylabel_for_apostrophe">\'</string> - <string name="keyhintlabel_for_apostrophe">\"</string> - <string name="more_keys_for_apostrophe">\"</string> <string name="more_keys_for_q"></string> <string name="more_keys_for_x"></string> <string name="keylabel_for_q">q</string> <string name="keylabel_for_w">w</string> <string name="keylabel_for_y">y</string> <string name="keylabel_for_x">x</string> - <string name="keylabel_for_spanish_row2_10"></string> + <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> + <string name="keylabel_for_spanish_row2_10">ñ</string> <string name="more_keys_for_am_pm">!fixedColumnOrder!2,!hasLabels!,\@string/label_time_am,\@string/label_time_pm</string> <string name="settings_as_more_key">!icon/settings_key|!code/key_settings</string> <string name="shortcut_as_more_key">!icon/shortcut_key|!code/key_shortcut</string> diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java new file mode 100644 index 000000000..9fdc1f607 --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java @@ -0,0 +1,61 @@ +/* + * 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.tools; + +import java.util.HashMap; +import java.util.Locale; + +/** + * A class to help with handling Locales in string form. + * + * This is a subset of com/android/inputmethod/latin/utils/LocaleUtils.java in order to use + * for the make-keyboard-text tool. + */ +public final class LocaleUtils { + private LocaleUtils() { + // Intentional empty constructor for utility class. + } + + private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + + /** + * Creates a locale from a string specification. + */ + public static Locale constructLocaleFromString(final String localeStr) { + if (localeStr == null) { + return null; + } + synchronized (sLocaleCache) { + Locale retval = sLocaleCache.get(localeStr); + if (retval != null) { + return retval; + } + String[] localeParams = localeStr.split("_", 3); + if (localeParams.length == 1) { + retval = new Locale(localeParams[0]); + } else if (localeParams.length == 2) { + retval = new Locale(localeParams[0], localeParams[1]); + } else if (localeParams.length == 3) { + retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); + } + if (retval != null) { + sLocaleCache.put(localeStr, retval); + } + return retval; + } + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java index 2643e01ec..a88383025 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java @@ -37,7 +37,7 @@ public class MoreKeysResources { private static final String MARK_DEFAULT_TEXTS = "@DEFAULT_TEXTS@"; private static final String MARK_TEXTS = "@TEXTS@"; private static final String MARK_LANGUAGES_AND_TEXTS = "@LANGUAGES_AND_TEXTS@"; - private static final String DEFAUT_LANGUAGE_NAME = "DEFAULT"; + private static final String DEFAULT_LANGUAGE_NAME = "DEFAULT"; private static final String ARRAY_NAME_FOR_LANGUAGE = "LANGUAGE_%s"; private static final String EMPTY_STRING_VAR = "EMPTY"; @@ -72,7 +72,7 @@ public class MoreKeysResources { final int languagePos = dirName.indexOf('-'); if (languagePos < 0) { // Default resource. - return DEFAUT_LANGUAGE_NAME; + return DEFAULT_LANGUAGE_NAME; } final String language = dirName.substring(languagePos + 1); final int countryPos = language.indexOf("-r"); @@ -84,10 +84,12 @@ public class MoreKeysResources { public void writeToJava(final String outDir) { final ArrayList<String> list = JarUtils.getNameListing(mJar, JAVA_TEMPLATE); - if (list.isEmpty()) + if (list.isEmpty()) { throw new RuntimeException("Can't find java template " + JAVA_TEMPLATE); - if (list.size() > 1) + } + if (list.size() > 1) { throw new RuntimeException("Found multiple java template " + JAVA_TEMPLATE); + } final String template = list.get(0); final String javaPackage = template.substring(0, template.lastIndexOf('/')); PrintStream ps = null; @@ -131,7 +133,7 @@ public class MoreKeysResources { } private void dumpNames(final PrintStream out) { - final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); int id = 0; for (final StringResource res : defaultResMap.getResources()) { out.format(" /* %2d */ \"%s\",\n", id, res.mName); @@ -141,17 +143,17 @@ public class MoreKeysResources { } private void dumpDefaultTexts(final PrintStream out) { - final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); dumpTextsInternal(out, defaultResMap, defaultResMap); } private void dumpTexts(final PrintStream out) { - final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); final ArrayList<String> allLanguages = new ArrayList<String>(); allLanguages.addAll(mResourcesMap.keySet()); Collections.sort(allLanguages); for (final String language : allLanguages) { - if (language.equals(DEFAUT_LANGUAGE_NAME)) { + if (language.equals(DEFAULT_LANGUAGE_NAME)) { continue; } out.format(" /* Language %s: %s */\n", language, getLanguageDisplayName(language)); @@ -174,17 +176,22 @@ public class MoreKeysResources { allLanguages.addAll(mResourcesMap.keySet()); Collections.sort(allLanguages); for (final String language : allLanguages) { - out.format(" \"%s\", " + ARRAY_NAME_FOR_LANGUAGE + ", /* %s */\n", - language, language, getLanguageDisplayName(language)); + final Locale locale = LocaleUtils.constructLocaleFromString(language); + // If we use a different key, dump the original as comment for now. + final String languageKeyToDump = locale.getCountry().isEmpty() + ? String.format("\"%s\"", language) + : String.format("\"%s\" /* \"%s\" */", locale.getLanguage(), language); + out.format(" %s, " + ARRAY_NAME_FOR_LANGUAGE + ", /* %s */\n", + languageKeyToDump, language, getLanguageDisplayName(language)); } } private static String getLanguageDisplayName(final String language) { - if (language.equals(NO_LANGUAGE_CODE)) { + final Locale locale = LocaleUtils.constructLocaleFromString(language); + if (locale.getLanguage().equals(NO_LANGUAGE_CODE)) { return NO_LANGUAGE_DISPLAY_NAME; - } else { - return new Locale(language).getDisplayLanguage(); } + return locale.getDisplayName(Locale.ENGLISH); } private static void dumpTextsInternal(final PrintStream out, final StringResourceMap resMap, |