diff options
104 files changed, 3385 insertions, 1078 deletions
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml index 056bc355e..8fe415d11 100644 --- a/java/res/values-ka-rGE/strings.xml +++ b/java/res/values-ka-rGE/strings.xml @@ -167,7 +167,7 @@ <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ინსტალაციისათვის აირჩიეთ ლექსიკონის ფაილი"</string> <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"ნამდვილად გსურთ ამ ფაილის <xliff:g id="LANGUAGE_NAME">%s</xliff:g>-ისთვის ინსტალაცია?"</string> <string name="error" msgid="8940763624668513648">"წარმოიშვა შეცდომა"</string> - <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"კონტაქტების საქაღალდის ჩამოწერა"</string> + <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"კონტაქტების საქაღალდის ამონაწერი"</string> <string name="prefs_dump_user_dict" msgid="294870685041741951">"პერსონალური საქაღალდის ჩამოწერა"</string> <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"მომხმ. ისტორიის საქაღალდის ჩამოწერა"</string> <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"პერსონალიზაციის საქაღალდის ჩამოწერა"</string> diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml index 9f28cd1bb..dae75e350 100644 --- a/java/res/values-lo-rLA/strings.xml +++ b/java/res/values-lo-rLA/strings.xml @@ -167,7 +167,7 @@ <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ເລືອກໄຟລ໌ວັດຈະນານຸກົມເພື່ອຕິດຕັ້ງ"</string> <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"ຕິດຕັ້ງໄຟລ໌ນີ້ສຳລັບ <xliff:g id="LANGUAGE_NAME">%s</xliff:g> ແທ້ບໍ່??"</string> <string name="error" msgid="8940763624668513648">"ມີຂໍ້ຜິດພາດເກີດຂຶ້ນ"</string> - <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"ເທຂໍ້ມູນວັດຈະນານຸກົມລາຍຊື່ຜູ່ຕິດຕໍ່"</string> + <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"ດຶງຂໍ້ມູນວັດຈະນານຸກົມລາຍຊື່ຜູ່ຕິດຕໍ່"</string> <string name="prefs_dump_user_dict" msgid="294870685041741951">"ເທຂໍ້ມູນວັດຈະນານຸກົມສ່ວນໂຕ"</string> <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"ເທຂໍ້ມູນວັດຈະນານຸກົມປະຫວັດຜູ່ໃຊ້"</string> <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"ເທຂໍ້ມູນວັດຈະນານຸກົມຄວາມເປັນໂຕຕົນ"</string> diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml index ef8181faf..6c9740329 100644 --- a/java/res/values-mn-rMN/strings.xml +++ b/java/res/values-mn-rMN/strings.xml @@ -167,7 +167,7 @@ <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Суулгах толь бичгийн файлыг сонгоно уу"</string> <string name="read_external_dictionary_confirm_install_message" msgid="4782116251651288054">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>-д зориулсан энэ файлыг үнэхээр суулгах уу?"</string> <string name="error" msgid="8940763624668513648">"Алдаа гарсан"</string> - <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Харилцагчдын толь бичгийг хаях"</string> + <string name="prefs_dump_contacts_dict" msgid="7227327764402323097">"Харилцагчдын толь бичгийг жагсаах"</string> <string name="prefs_dump_user_dict" msgid="294870685041741951">"Хувийн толь бичгийг хаях"</string> <string name="prefs_dump_user_history_dict" msgid="6821075152449554628">"Хэрэглэгчийн түүхийн толь бичгийг хаях"</string> <string name="prefs_dump_personalization_dict" msgid="7558387996151745284">"Хувийн тохиргоотой толь бичгийг хаях"</string> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index ddff7697c..5efa73362 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -491,8 +491,6 @@ mobile devices. [CHAR LIMIT=25] --> <string name="prefs_key_popup_dismiss_end_scale_settings" translatable="false">Key popup dismiss end scale</string> <!-- Title of the settings for reading an external dictionary file --> <string name="prefs_read_external_dictionary">Read external dictionary file</string> - <!-- Title of the settings for using only personalization dictionary --> - <string name="prefs_use_only_personalization_dictionary" translatable="false">Use only personalization dictionary</string> <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] --> <string name="read_external_dictionary_no_files_message">No dictionary files in the Downloads folder</string> <!-- Title of the dialog that selects a file to install as an external dictionary [CHAR LIMIT=50] --> diff --git a/java/res/xml-sw600dp/key_azerty3_right.xml b/java/res/xml-sw600dp/key_azerty3_right.xml deleted file mode 100644 index 25b0e52b8..000000000 --- a/java/res/xml-sw600dp/key_azerty3_right.xml +++ /dev/null @@ -1,29 +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" -> - <Key - latin:keySpec=":" - latin:keyHintLabel=";" - latin:moreKeys=";" - latin:keyStyle="hasShiftedLetterHintStyle" /> -</merge> diff --git a/java/res/xml/key_azerty3_right.xml b/java/res/xml/key_azerty3_right.xml deleted file mode 100644 index 85a066613..000000000 --- a/java/res/xml/key_azerty3_right.xml +++ /dev/null @@ -1,37 +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:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" - > - <Key - latin:keySpec="\?" /> - </case> - <default> - <Key - latin:keySpec="\'" - latin:moreKeys="!text/more_keys_for_single_quote" /> - </default> - </switch> -</merge> diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml index 81a5d98b9..7b2b8eab4 100644 --- a/java/res/xml/prefs_for_debug.xml +++ b/java/res/xml/prefs_for_debug.xml @@ -65,11 +65,6 @@ android:key="pref_key_preview_dismiss_duration" android:title="@string/prefs_key_popup_dismiss_duration_settings" latin:maxValue="100" /> <!-- milliseconds --> - <CheckBoxPreference - android:defaultValue="false" - android:key="use_only_personalization_dictionary_for_debug" - android:persistent="true" - android:title="@string/prefs_use_only_personalization_dictionary" /> <PreferenceScreen android:key="read_external_dictionary" android:title="@string/prefs_read_external_dictionary" /> diff --git a/java/res/xml/rowkeys_azerty3.xml b/java/res/xml/rowkeys_azerty3.xml index 0aa215305..c955e237c 100644 --- a/java/res/xml/rowkeys_azerty3.xml +++ b/java/res/xml/rowkeys_azerty3.xml @@ -37,6 +37,17 @@ <Key latin:keySpec="n" latin:moreKeys="!text/more_keys_for_n" /> - <include - latin:keyboardLayout="@xml/key_azerty3_right" /> + <switch> + <case + latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted" + > + <Key + latin:keySpec="\?" /> + </case> + <default> + <Key + latin:keySpec="\'" + latin:moreKeys="!text/more_keys_for_single_quote" /> + </default> + </switch> </merge> diff --git a/java/res/xml/rowkeys_east_slavic2.xml b/java/res/xml/rowkeys_east_slavic2.xml index 20d963cb3..060100006 100644 --- a/java/res/xml/rowkeys_east_slavic2.xml +++ b/java/res/xml/rowkeys_east_slavic2.xml @@ -25,8 +25,8 @@ <Key latin:keySpec="ф" /> <Key - latin:keySpec="!text/keylabel_for_east_slavic_row2_1" - latin:moreKeys="!text/more_keys_for_east_slavic_row2_1" /> + latin:keySpec="!text/keylabel_for_east_slavic_row2_2" + latin:moreKeys="!text/more_keys_for_east_slavic_row2_2" /> <!-- U+0432: "в" CYRILLIC SMALL LETTER VE --> <Key latin:keySpec="в" /> diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java new file mode 100644 index 000000000..3f709a674 --- /dev/null +++ b/java/src/com/android/inputmethod/event/InputTransaction.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.event; + +import com.android.inputmethod.latin.settings.SettingsValues; + +/** + * An object encapsulating a single transaction for input. + */ +public class InputTransaction { + // UPDATE_LATER is stronger than UPDATE_NOW. The reason for this is, if we have to update later, + // it's because something will change that we can't evaluate now, which means that even if we + // re-evaluate now we'll have to do it again later. The only case where that wouldn't apply + // would be if we needed to update now to find out the new state right away, but then we + // can't do it with this deferred mechanism anyway. + public static final int SHIFT_NO_UPDATE = 0; + public static final int SHIFT_UPDATE_NOW = 1; + public static final int SHIFT_UPDATE_LATER = 2; + + // Initial conditions + public final SettingsValues mSettingsValues; + // If the key inserts a code point, mKeyCode is always equal to the code points. Otherwise, + // it's always a code that may not be a code point, typically a negative number. + public final int mKeyCode; + public final int mX; // Pressed x-coordinate, or one of Constants.*_COORDINATE + public final int mY; // Pressed y-coordinate, or one of Constants.*_COORDINATE + public final long mTimestamp; + public final int mSpaceState; + public final int mShiftState; + + // Outputs + private int mRequiredShiftUpdate = SHIFT_NO_UPDATE; + + public InputTransaction(final SettingsValues settingsValues, final int keyCode, + final int x, final int y, final long timestamp, final int spaceState, + final int shiftState) { + mSettingsValues = settingsValues; + mKeyCode = keyCode; + mX = x; + mY = y; + mTimestamp = timestamp; + mSpaceState = spaceState; + mShiftState = shiftState; + } + + public void requireShiftUpdate(final int updateType) { + mRequiredShiftUpdate = Math.max(mRequiredShiftUpdate, updateType); + } + public int getRequiredShiftUpdate() { + return mRequiredShiftUpdate; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 6c56b8aab..3590c486b 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -700,12 +700,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } @Override - public void onCancelMoreKeysPanel(final MoreKeysPanel panel) { + public void onCancelMoreKeysPanel() { PointerTracker.dismissAllMoreKeysPanels(); } @Override - public void onDismissMoreKeysPanel(final MoreKeysPanel panel) { + public void onDismissMoreKeysPanel() { dimEntireKeyboard(false /* dimmed */); if (isShowingMoreKeysPanel()) { mMoreKeysPanel.removeFromParent(); diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 1891dfc74..fc331970b 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -122,7 +122,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel onMoveKeyInternal(x, y, pointerId); if (hasOldKey && mCurrentKey == null) { // If the pointer has moved too far away from any target then cancel the panel. - mController.onCancelMoreKeysPanel(this); + mController.onCancelMoreKeysPanel(); } } @@ -184,7 +184,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel if (!isShowingInParent()) { return; } - mController.onDismissMoreKeysPanel(this); + mController.onDismissMoreKeysPanel(); } @Override diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java index 4a33e6536..7bddd09f6 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysPanel.java @@ -29,24 +29,22 @@ public interface MoreKeysPanel { /** * Remove the current {@link MoreKeysPanel} from the target view. - * @param panel the panel to be dismissed. */ - public void onDismissMoreKeysPanel(final MoreKeysPanel panel); + public void onDismissMoreKeysPanel(); /** * Instructs the parent to cancel the panel (e.g., when entering a different input mode). - * @param panel the panel to be canceled. */ - public void onCancelMoreKeysPanel(final MoreKeysPanel panel); + public void onCancelMoreKeysPanel(); } public static final Controller EMPTY_CONTROLLER = new Controller() { @Override public void onShowMoreKeysPanel(final MoreKeysPanel panel) {} @Override - public void onDismissMoreKeysPanel(final MoreKeysPanel panel) {} + public void onDismissMoreKeysPanel() {} @Override - public void onCancelMoreKeysPanel(final MoreKeysPanel panel) {} + public void onCancelMoreKeysPanel() {} }; /** diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 59cf64d4b..3539a874c 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -323,7 +323,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, } // Note that we need primaryCode argument because the keyboard may in shifted state and the - // primaryCode is different from {@link Key#mCode}. + // primaryCode is different from {@link Key#mKeyCode}. private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x, final int y, final long eventTime) { final boolean ignoreModifierKey = mIsInDraggingFinger && key.isModifier(); @@ -360,7 +360,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element, } // Note that we need primaryCode argument because the keyboard may be in shifted state and the - // primaryCode is different from {@link Key#mCode}. + // primaryCode is different from {@link Key#mKeyCode}. private void callListenerOnRelease(final Key key, final int primaryCode, final boolean withSliding) { // See the comment at {@link #callListenerOnPressAndCheckKeyboardLayoutChange(Key}}. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index ed2db07a8..cace069c4 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -107,149 +107,148 @@ public final class KeyboardTextsTable { /* 25: 6 */ "more_keys_for_cyrillic_ie", /* 26: 5 */ "more_keys_for_nordic_row2_10", /* 27: 5 */ "keylabel_for_east_slavic_row1_9", - /* 28: 5 */ "keylabel_for_east_slavic_row1_12", - /* 29: 5 */ "keylabel_for_east_slavic_row2_1", - /* 30: 5 */ "keylabel_for_east_slavic_row2_11", - /* 31: 5 */ "keylabel_for_east_slavic_row3_5", - /* 32: 5 */ "more_keys_for_cyrillic_soft_sign", - /* 33: 5 */ "more_keys_for_punctuation", - /* 34: 4 */ "more_keys_for_nordic_row2_11", - /* 35: 4 */ "keylabel_for_symbols_1", - /* 36: 4 */ "keylabel_for_symbols_2", - /* 37: 4 */ "keylabel_for_symbols_3", - /* 38: 4 */ "keylabel_for_symbols_4", - /* 39: 4 */ "keylabel_for_symbols_5", - /* 40: 4 */ "keylabel_for_symbols_6", - /* 41: 4 */ "keylabel_for_symbols_7", - /* 42: 4 */ "keylabel_for_symbols_8", - /* 43: 4 */ "keylabel_for_symbols_9", - /* 44: 4 */ "keylabel_for_symbols_0", - /* 45: 4 */ "label_to_symbol_key", - /* 46: 4 */ "label_to_symbol_with_microphone_key", - /* 47: 4 */ "additional_more_keys_for_symbols_1", - /* 48: 4 */ "additional_more_keys_for_symbols_2", - /* 49: 4 */ "additional_more_keys_for_symbols_3", - /* 50: 4 */ "additional_more_keys_for_symbols_4", - /* 51: 4 */ "additional_more_keys_for_symbols_5", - /* 52: 4 */ "additional_more_keys_for_symbols_6", - /* 53: 4 */ "additional_more_keys_for_symbols_7", - /* 54: 4 */ "additional_more_keys_for_symbols_8", - /* 55: 4 */ "additional_more_keys_for_symbols_9", - /* 56: 4 */ "additional_more_keys_for_symbols_0", - /* 57: 3 */ "more_keys_for_star", - /* 58: 3 */ "keyspec_left_parenthesis", - /* 59: 3 */ "keyspec_right_parenthesis", - /* 60: 3 */ "keyspec_left_square_bracket", - /* 61: 3 */ "keyspec_right_square_bracket", - /* 62: 3 */ "keyspec_left_curly_bracket", - /* 63: 3 */ "keyspec_right_curly_bracket", - /* 64: 3 */ "keyspec_less_than", - /* 65: 3 */ "keyspec_greater_than", - /* 66: 3 */ "keyspec_less_than_equal", - /* 67: 3 */ "keyspec_greater_than_equal", - /* 68: 3 */ "keyspec_left_double_angle_quote", - /* 69: 3 */ "keyspec_right_double_angle_quote", - /* 70: 3 */ "keyspec_left_single_angle_quote", - /* 71: 3 */ "keyspec_right_single_angle_quote", - /* 72: 3 */ "keylabel_for_tablet_comma", - /* 73: 3 */ "more_keys_for_tablet_period", - /* 74: 3 */ "more_keys_for_question", - /* 75: 2 */ "more_keys_for_h", - /* 76: 2 */ "more_keys_for_w", - /* 77: 2 */ "more_keys_for_cyrillic_u", - /* 78: 2 */ "more_keys_for_cyrillic_en", - /* 79: 2 */ "more_keys_for_cyrillic_ghe", - /* 80: 2 */ "more_keys_for_east_slavic_row2_1", - /* 81: 2 */ "more_keys_for_cyrillic_o", - /* 82: 2 */ "keylabel_for_south_slavic_row1_6", - /* 83: 2 */ "keylabel_for_south_slavic_row2_11", - /* 84: 2 */ "keylabel_for_south_slavic_row3_1", - /* 85: 2 */ "keylabel_for_south_slavic_row3_8", - /* 86: 2 */ "more_keys_for_cyrillic_i", - /* 87: 2 */ "keylabel_for_swiss_row1_11", - /* 88: 2 */ "keylabel_for_swiss_row2_10", - /* 89: 2 */ "keylabel_for_swiss_row2_11", - /* 90: 2 */ "more_keys_for_swiss_row1_11", - /* 91: 2 */ "more_keys_for_swiss_row2_10", - /* 92: 2 */ "more_keys_for_swiss_row2_11", - /* 93: 2 */ "keylabel_for_spanish_row2_10", - /* 94: 2 */ "more_keys_for_bullet", - /* 95: 2 */ "more_keys_for_left_parenthesis", - /* 96: 2 */ "more_keys_for_right_parenthesis", - /* 97: 2 */ "more_keys_for_arabic_diacritics", - /* 98: 2 */ "keylabel_for_comma", - /* 99: 2 */ "more_keys_for_comma", - /* 100: 2 */ "keyhintlabel_for_tablet_comma", - /* 101: 2 */ "more_keys_for_tablet_comma", - /* 102: 2 */ "keyhintlabel_for_period", - /* 103: 2 */ "more_keys_for_period", - /* 104: 2 */ "keyhintlabel_for_tablet_period", - /* 105: 2 */ "keylabel_for_symbols_question", - /* 106: 2 */ "keylabel_for_symbols_semicolon", - /* 107: 2 */ "keylabel_for_symbols_percent", - /* 108: 2 */ "more_keys_for_symbols_semicolon", - /* 109: 2 */ "more_keys_for_symbols_percent", - /* 110: 1 */ "more_keys_for_v", - /* 111: 1 */ "more_keys_for_j", - /* 112: 1 */ "more_keys_for_cyrillic_ka", - /* 113: 1 */ "more_keys_for_cyrillic_a", - /* 114: 1 */ "more_keys_for_east_slavic_row2_11", - /* 115: 1 */ "more_keys_for_currency_dollar", - /* 116: 1 */ "more_keys_for_tablet_punctuation", - /* 117: 1 */ "more_keys_for_plus", - /* 118: 1 */ "more_keys_for_less_than", - /* 119: 1 */ "more_keys_for_greater_than", - /* 120: 1 */ "keylabel_for_period", - /* 121: 1 */ "keylabel_for_tablet_period", - /* 122: 1 */ "more_keys_for_exclamation", - /* 123: 1 */ "more_keys_for_q", - /* 124: 1 */ "more_keys_for_x", - /* 125: 1 */ "keylabel_for_q", - /* 126: 1 */ "keylabel_for_w", - /* 127: 1 */ "keylabel_for_y", - /* 128: 1 */ "keylabel_for_x", - /* 129: 0 */ "more_keys_for_currency", - /* 130: 0 */ "more_keys_for_symbols_1", - /* 131: 0 */ "more_keys_for_symbols_2", - /* 132: 0 */ "more_keys_for_symbols_3", - /* 133: 0 */ "more_keys_for_symbols_4", - /* 134: 0 */ "more_keys_for_symbols_5", - /* 135: 0 */ "more_keys_for_symbols_6", - /* 136: 0 */ "more_keys_for_symbols_7", - /* 137: 0 */ "more_keys_for_symbols_8", - /* 138: 0 */ "more_keys_for_symbols_9", - /* 139: 0 */ "more_keys_for_symbols_0", - /* 140: 0 */ "more_keys_for_am_pm", - /* 141: 0 */ "settings_as_more_key", - /* 142: 0 */ "shortcut_as_more_key", - /* 143: 0 */ "action_next_as_more_key", - /* 144: 0 */ "action_previous_as_more_key", - /* 145: 0 */ "label_to_more_symbol_key", - /* 146: 0 */ "label_to_more_symbol_for_tablet_key", - /* 147: 0 */ "label_to_phone_numeric_key", - /* 148: 0 */ "label_to_phone_symbols_key", - /* 149: 0 */ "label_time_am", - /* 150: 0 */ "label_time_pm", - /* 151: 0 */ "keylabel_for_popular_domain", - /* 152: 0 */ "more_keys_for_popular_domain", - /* 153: 0 */ "keyspecs_for_left_parenthesis_more_keys", - /* 154: 0 */ "keyspecs_for_right_parenthesis_more_keys", - /* 155: 0 */ "single_laqm_raqm", - /* 156: 0 */ "single_raqm_laqm", - /* 157: 0 */ "double_laqm_raqm", - /* 158: 0 */ "double_raqm_laqm", - /* 159: 0 */ "single_lqm_rqm", - /* 160: 0 */ "single_9qm_lqm", - /* 161: 0 */ "single_9qm_rqm", - /* 162: 0 */ "single_rqm_9qm", - /* 163: 0 */ "double_lqm_rqm", - /* 164: 0 */ "double_9qm_lqm", - /* 165: 0 */ "double_9qm_rqm", - /* 166: 0 */ "double_rqm_9qm", - /* 167: 0 */ "more_keys_for_single_quote", - /* 168: 0 */ "more_keys_for_double_quote", - /* 169: 0 */ "more_keys_for_tablet_double_quote", - /* 170: 0 */ "emoji_key_as_more_key", + /* 28: 5 */ "keylabel_for_east_slavic_row2_2", + /* 29: 5 */ "keylabel_for_east_slavic_row2_11", + /* 30: 5 */ "keylabel_for_east_slavic_row3_5", + /* 31: 5 */ "more_keys_for_cyrillic_soft_sign", + /* 32: 5 */ "more_keys_for_punctuation", + /* 33: 4 */ "more_keys_for_nordic_row2_11", + /* 34: 4 */ "keylabel_for_symbols_1", + /* 35: 4 */ "keylabel_for_symbols_2", + /* 36: 4 */ "keylabel_for_symbols_3", + /* 37: 4 */ "keylabel_for_symbols_4", + /* 38: 4 */ "keylabel_for_symbols_5", + /* 39: 4 */ "keylabel_for_symbols_6", + /* 40: 4 */ "keylabel_for_symbols_7", + /* 41: 4 */ "keylabel_for_symbols_8", + /* 42: 4 */ "keylabel_for_symbols_9", + /* 43: 4 */ "keylabel_for_symbols_0", + /* 44: 4 */ "label_to_symbol_key", + /* 45: 4 */ "label_to_symbol_with_microphone_key", + /* 46: 4 */ "additional_more_keys_for_symbols_1", + /* 47: 4 */ "additional_more_keys_for_symbols_2", + /* 48: 4 */ "additional_more_keys_for_symbols_3", + /* 49: 4 */ "additional_more_keys_for_symbols_4", + /* 50: 4 */ "additional_more_keys_for_symbols_5", + /* 51: 4 */ "additional_more_keys_for_symbols_6", + /* 52: 4 */ "additional_more_keys_for_symbols_7", + /* 53: 4 */ "additional_more_keys_for_symbols_8", + /* 54: 4 */ "additional_more_keys_for_symbols_9", + /* 55: 4 */ "additional_more_keys_for_symbols_0", + /* 56: 3 */ "more_keys_for_star", + /* 57: 3 */ "keyspec_left_parenthesis", + /* 58: 3 */ "keyspec_right_parenthesis", + /* 59: 3 */ "keyspec_left_square_bracket", + /* 60: 3 */ "keyspec_right_square_bracket", + /* 61: 3 */ "keyspec_left_curly_bracket", + /* 62: 3 */ "keyspec_right_curly_bracket", + /* 63: 3 */ "keyspec_less_than", + /* 64: 3 */ "keyspec_greater_than", + /* 65: 3 */ "keyspec_less_than_equal", + /* 66: 3 */ "keyspec_greater_than_equal", + /* 67: 3 */ "keyspec_left_double_angle_quote", + /* 68: 3 */ "keyspec_right_double_angle_quote", + /* 69: 3 */ "keyspec_left_single_angle_quote", + /* 70: 3 */ "keyspec_right_single_angle_quote", + /* 71: 3 */ "keylabel_for_tablet_comma", + /* 72: 3 */ "more_keys_for_tablet_period", + /* 73: 3 */ "more_keys_for_question", + /* 74: 2 */ "more_keys_for_h", + /* 75: 2 */ "more_keys_for_w", + /* 76: 2 */ "more_keys_for_cyrillic_u", + /* 77: 2 */ "more_keys_for_cyrillic_en", + /* 78: 2 */ "more_keys_for_cyrillic_ghe", + /* 79: 2 */ "more_keys_for_east_slavic_row2_2", + /* 80: 2 */ "more_keys_for_cyrillic_o", + /* 81: 2 */ "keylabel_for_south_slavic_row1_6", + /* 82: 2 */ "keylabel_for_south_slavic_row2_11", + /* 83: 2 */ "keylabel_for_south_slavic_row3_1", + /* 84: 2 */ "keylabel_for_south_slavic_row3_8", + /* 85: 2 */ "more_keys_for_cyrillic_i", + /* 86: 2 */ "keylabel_for_swiss_row1_11", + /* 87: 2 */ "keylabel_for_swiss_row2_10", + /* 88: 2 */ "keylabel_for_swiss_row2_11", + /* 89: 2 */ "more_keys_for_swiss_row1_11", + /* 90: 2 */ "more_keys_for_swiss_row2_10", + /* 91: 2 */ "more_keys_for_swiss_row2_11", + /* 92: 2 */ "keylabel_for_spanish_row2_10", + /* 93: 2 */ "more_keys_for_bullet", + /* 94: 2 */ "more_keys_for_left_parenthesis", + /* 95: 2 */ "more_keys_for_right_parenthesis", + /* 96: 2 */ "more_keys_for_arabic_diacritics", + /* 97: 2 */ "keylabel_for_comma", + /* 98: 2 */ "more_keys_for_comma", + /* 99: 2 */ "keyhintlabel_for_tablet_comma", + /* 100: 2 */ "more_keys_for_tablet_comma", + /* 101: 2 */ "keyhintlabel_for_period", + /* 102: 2 */ "more_keys_for_period", + /* 103: 2 */ "keyhintlabel_for_tablet_period", + /* 104: 2 */ "keylabel_for_symbols_question", + /* 105: 2 */ "keylabel_for_symbols_semicolon", + /* 106: 2 */ "keylabel_for_symbols_percent", + /* 107: 2 */ "more_keys_for_symbols_semicolon", + /* 108: 2 */ "more_keys_for_symbols_percent", + /* 109: 1 */ "more_keys_for_v", + /* 110: 1 */ "more_keys_for_j", + /* 111: 1 */ "more_keys_for_cyrillic_ka", + /* 112: 1 */ "more_keys_for_cyrillic_a", + /* 113: 1 */ "more_keys_for_east_slavic_row2_11", + /* 114: 1 */ "more_keys_for_currency_dollar", + /* 115: 1 */ "more_keys_for_tablet_punctuation", + /* 116: 1 */ "more_keys_for_plus", + /* 117: 1 */ "more_keys_for_less_than", + /* 118: 1 */ "more_keys_for_greater_than", + /* 119: 1 */ "keylabel_for_period", + /* 120: 1 */ "keylabel_for_tablet_period", + /* 121: 1 */ "more_keys_for_exclamation", + /* 122: 1 */ "more_keys_for_q", + /* 123: 1 */ "more_keys_for_x", + /* 124: 1 */ "keylabel_for_q", + /* 125: 1 */ "keylabel_for_w", + /* 126: 1 */ "keylabel_for_y", + /* 127: 1 */ "keylabel_for_x", + /* 128: 0 */ "more_keys_for_currency", + /* 129: 0 */ "more_keys_for_symbols_1", + /* 130: 0 */ "more_keys_for_symbols_2", + /* 131: 0 */ "more_keys_for_symbols_3", + /* 132: 0 */ "more_keys_for_symbols_4", + /* 133: 0 */ "more_keys_for_symbols_5", + /* 134: 0 */ "more_keys_for_symbols_6", + /* 135: 0 */ "more_keys_for_symbols_7", + /* 136: 0 */ "more_keys_for_symbols_8", + /* 137: 0 */ "more_keys_for_symbols_9", + /* 138: 0 */ "more_keys_for_symbols_0", + /* 139: 0 */ "more_keys_for_am_pm", + /* 140: 0 */ "settings_as_more_key", + /* 141: 0 */ "shortcut_as_more_key", + /* 142: 0 */ "action_next_as_more_key", + /* 143: 0 */ "action_previous_as_more_key", + /* 144: 0 */ "label_to_more_symbol_key", + /* 145: 0 */ "label_to_more_symbol_for_tablet_key", + /* 146: 0 */ "label_to_phone_numeric_key", + /* 147: 0 */ "label_to_phone_symbols_key", + /* 148: 0 */ "label_time_am", + /* 149: 0 */ "label_time_pm", + /* 150: 0 */ "keylabel_for_popular_domain", + /* 151: 0 */ "more_keys_for_popular_domain", + /* 152: 0 */ "keyspecs_for_left_parenthesis_more_keys", + /* 153: 0 */ "keyspecs_for_right_parenthesis_more_keys", + /* 154: 0 */ "single_laqm_raqm", + /* 155: 0 */ "single_raqm_laqm", + /* 156: 0 */ "double_laqm_raqm", + /* 157: 0 */ "double_raqm_laqm", + /* 158: 0 */ "single_lqm_rqm", + /* 159: 0 */ "single_9qm_lqm", + /* 160: 0 */ "single_9qm_rqm", + /* 161: 0 */ "single_rqm_9qm", + /* 162: 0 */ "double_lqm_rqm", + /* 163: 0 */ "double_9qm_lqm", + /* 164: 0 */ "double_9qm_rqm", + /* 165: 0 */ "double_rqm_9qm", + /* 166: 0 */ "more_keys_for_single_quote", + /* 167: 0 */ "more_keys_for_double_quote", + /* 168: 0 */ "more_keys_for_tablet_double_quote", + /* 169: 0 */ "emoji_key_as_more_key", }; private static final String EMPTY = ""; @@ -273,7 +272,7 @@ public final class KeyboardTextsTable { /* double_angle_quotes */ "!text/double_laqm_raqm", /* keylabel_for_currency */ "$", /* more_keys_for_r ~ */ - 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, /* ~ more_keys_for_cyrillic_soft_sign */ /* more_keys_for_punctuation */ "!fixedColumnOrder!8,;,/,!text/keyspec_left_parenthesis,!text/keyspec_right_parenthesis,#,!,\\,,?,&,\\%,+,\",-,:,',@", /* more_keys_for_nordic_row2_11 */ EMPTY, @@ -533,7 +532,7 @@ public final class KeyboardTextsTable { /* label_to_alpha_key */ "\u0623\u200C\u0628\u200C\u062C", /* more_keys_for_y ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_soft_sign */ /* more_keys_for_punctuation */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(", /* more_keys_for_nordic_row2_11 */ null, @@ -722,10 +721,8 @@ public final class KeyboardTextsTable { /* more_keys_for_nordic_row2_10 */ null, // U+045E: "ў" CYRILLIC SMALL LETTER SHORT U /* keylabel_for_east_slavic_row1_9 */ "\u045E", - // U+0451: "ё" CYRILLIC SMALL LETTER IO - /* keylabel_for_east_slavic_row1_12 */ "\u0451", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* keylabel_for_east_slavic_row2_1 */ "\u044B", + /* keylabel_for_east_slavic_row2_2 */ "\u044B", // U+044D: "э" CYRILLIC SMALL LETTER E /* keylabel_for_east_slavic_row2_11 */ "\u044D", // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I @@ -813,7 +810,7 @@ public final class KeyboardTextsTable { /* more_keys_for_l */ "l\u00B7l,\u0142", /* more_keys_for_g ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, + null, /* ~ more_keys_for_cyrillic_soft_sign */ // U+00B7: "·" MIDDLE DOT /* more_keys_for_punctuation */ "!fixedColumnOrder!9,;,/,(,),#,\u00B7,!,\\,,?,&,\\%,+,\",-,:,',@", @@ -974,7 +971,7 @@ public final class KeyboardTextsTable { // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS /* more_keys_for_nordic_row2_10 */ "\u00E4", /* keylabel_for_east_slavic_row1_9 ~ */ - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_punctuation */ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS /* more_keys_for_nordic_row2_11 */ "\u00F6", @@ -1033,7 +1030,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_i */ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* keylabel_for_swiss_row1_11 */ "\u00FC", @@ -1227,7 +1224,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ more_keys_for_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE @@ -1317,7 +1314,7 @@ public final class KeyboardTextsTable { /* more_keys_for_n */ "\u00F1,\u0144", /* label_to_alpha_key ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_soft_sign */ // U+00A1: "¡" INVERTED EXCLAMATION MARK // U+00BF: "¿" INVERTED QUESTION MARK @@ -1445,7 +1442,7 @@ public final class KeyboardTextsTable { // U+FDFC: "﷼" RIAL SIGN /* keylabel_for_currency */ "\uFDFC", /* more_keys_for_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_soft_sign */ // U+061F: "؟" ARABIC QUESTION MARK // U+060C: "،" ARABIC COMMA @@ -1620,7 +1617,7 @@ public final class KeyboardTextsTable { // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE /* more_keys_for_nordic_row2_10 */ "\u00F8", /* keylabel_for_east_slavic_row1_9 ~ */ - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_punctuation */ // U+00E6: "æ" LATIN SMALL LETTER AE /* more_keys_for_nordic_row2_11 */ "\u00E6", @@ -1685,7 +1682,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_i */ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE /* keylabel_for_swiss_row1_11 */ "\u00E8", @@ -1717,7 +1714,7 @@ public final class KeyboardTextsTable { // U+20B9: "₹" INDIAN RUPEE SIGN /* keylabel_for_currency */ "\u20B9", /* more_keys_for_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_nordic_row2_11 */ // U+0967: "१" DEVANAGARI DIGIT ONE /* keylabel_for_symbols_1 */ "\u0967", @@ -1853,7 +1850,7 @@ public final class KeyboardTextsTable { /* label_to_alpha_key */ "\u0531\u0532\u0533", /* more_keys_for_y ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_soft_sign */ // U+058A: "֊" ARMENIAN HYPHEN // U+055C: "՜" ARMENIAN EXCLAMATION MARK @@ -2025,7 +2022,7 @@ public final class KeyboardTextsTable { /* more_keys_for_r ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ additional_more_keys_for_symbols_0 */ // U+2605: "★" BLACK STAR /* more_keys_for_star */ "\u2605", @@ -2096,10 +2093,8 @@ public final class KeyboardTextsTable { /* more_keys_for_nordic_row2_10 */ null, // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* keylabel_for_east_slavic_row1_9 */ "\u0449", - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* keylabel_for_east_slavic_row1_12 */ "\u044A", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* keylabel_for_east_slavic_row2_1 */ "\u044B", + /* keylabel_for_east_slavic_row2_2 */ "\u044B", // U+044D: "э" CYRILLIC SMALL LETTER E /* keylabel_for_east_slavic_row2_11 */ "\u044D", // U+0438: "и" CYRILLIC SMALL LETTER I @@ -2119,7 +2114,7 @@ public final class KeyboardTextsTable { // U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE /* more_keys_for_cyrillic_ghe */ "\u0493", // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I - /* more_keys_for_east_slavic_row2_1 */ "\u0456", + /* more_keys_for_east_slavic_row2_2 */ "\u0456", // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O /* more_keys_for_cyrillic_o */ "\u04E9", /* keylabel_for_south_slavic_row1_6 ~ */ @@ -2151,7 +2146,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_east_slavic_row2_11 */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* more_keys_for_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", @@ -2175,10 +2170,8 @@ public final class KeyboardTextsTable { /* more_keys_for_nordic_row2_10 */ null, // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* keylabel_for_east_slavic_row1_9 */ "\u0449", - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* keylabel_for_east_slavic_row1_12 */ "\u044A", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* keylabel_for_east_slavic_row2_1 */ "\u044B", + /* keylabel_for_east_slavic_row2_2 */ "\u044B", // U+044D: "э" CYRILLIC SMALL LETTER E /* keylabel_for_east_slavic_row2_11 */ "\u044D", // U+0438: "и" CYRILLIC SMALL LETTER I @@ -2195,7 +2188,7 @@ public final class KeyboardTextsTable { // U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER /* more_keys_for_cyrillic_en */ "\u04A3", /* more_keys_for_cyrillic_ghe */ null, - /* more_keys_for_east_slavic_row2_1 */ null, + /* more_keys_for_east_slavic_row2_2 */ null, // U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O /* more_keys_for_cyrillic_o */ "\u04E9", }; @@ -2432,7 +2425,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_o */ // U+0455: "ѕ" CYRILLIC SMALL LETTER DZE /* keylabel_for_south_slavic_row1_6 */ "\u0455", @@ -2510,7 +2503,7 @@ public final class KeyboardTextsTable { // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS /* more_keys_for_nordic_row2_10 */ "\u00F6", /* keylabel_for_east_slavic_row1_9 ~ */ - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_punctuation */ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS /* more_keys_for_nordic_row2_11 */ "\u00E4", @@ -2532,7 +2525,7 @@ public final class KeyboardTextsTable { // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN /* keylabel_for_currency */ "\u0930\u0941.", /* more_keys_for_r ~ */ - null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_nordic_row2_11 */ // U+0967: "१" DEVANAGARI DIGIT ONE /* keylabel_for_symbols_1 */ "\u0967", @@ -2804,10 +2797,8 @@ public final class KeyboardTextsTable { /* more_keys_for_nordic_row2_10 */ null, // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* keylabel_for_east_slavic_row1_9 */ "\u0449", - // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN - /* keylabel_for_east_slavic_row1_12 */ "\u044A", // U+044B: "ы" CYRILLIC SMALL LETTER YERU - /* keylabel_for_east_slavic_row2_1 */ "\u044B", + /* keylabel_for_east_slavic_row2_2 */ "\u044B", // U+044D: "э" CYRILLIC SMALL LETTER E /* keylabel_for_east_slavic_row2_11 */ "\u044D", // U+0438: "и" CYRILLIC SMALL LETTER I @@ -2968,7 +2959,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, /* ~ more_keys_for_cyrillic_o */ // TODO: Move these to sr-Latn once we can handle IETF language tag with script name specified. // BEGIN: More keys definitions for Serbian (Latin) @@ -3081,7 +3072,7 @@ public final class KeyboardTextsTable { // U+0153: "œ" LATIN SMALL LIGATURE OE /* more_keys_for_nordic_row2_10 */ "\u00F8,\u0153", /* keylabel_for_east_slavic_row1_9 ~ */ - null, null, null, null, null, null, null, + null, null, null, null, null, null, /* ~ more_keys_for_punctuation */ // U+00E6: "æ" LATIN SMALL LETTER AE /* more_keys_for_nordic_row2_11 */ "\u00E6", @@ -3284,10 +3275,8 @@ public final class KeyboardTextsTable { /* ~ more_keys_for_nordic_row2_10 */ // U+0449: "щ" CYRILLIC SMALL LETTER SHCHA /* keylabel_for_east_slavic_row1_9 */ "\u0449", - // U+0457: "ї" CYRILLIC SMALL LETTER YI - /* keylabel_for_east_slavic_row1_12 */ "\u0457", // U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I - /* keylabel_for_east_slavic_row2_1 */ "\u0456", + /* keylabel_for_east_slavic_row2_2 */ "\u0456", // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE /* keylabel_for_east_slavic_row2_11 */ "\u0454", // U+0438: "и" CYRILLIC SMALL LETTER I @@ -3303,7 +3292,7 @@ public final class KeyboardTextsTable { // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN /* more_keys_for_cyrillic_ghe */ "\u0491", // U+0457: "ї" CYRILLIC SMALL LETTER YI - /* more_keys_for_east_slavic_row2_1 */ "\u0457", + /* more_keys_for_east_slavic_row2_2 */ "\u0457", }; /* Language vi: Vietnamese */ @@ -3565,7 +3554,7 @@ public final class KeyboardTextsTable { null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, /* ~ more_keys_for_question */ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX /* more_keys_for_h */ "\u0125", @@ -3584,60 +3573,60 @@ public final class KeyboardTextsTable { // Currently we are dropping the region from the key. private static final Object[] LANGUAGES_AND_TEXTS = { // "locale", TEXT_ARRAY, /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */ - "DEFAULT", LANGUAGE_DEFAULT, /* 171/171 default */ + "DEFAULT", LANGUAGE_DEFAULT, /* 170/170 default */ "af", LANGUAGE_af, /* 7/ 12 Afrikaans */ - "ar", LANGUAGE_ar, /* 58/110 Arabic */ + "ar", LANGUAGE_ar, /* 58/109 Arabic */ "az", LANGUAGE_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ - "be", LANGUAGE_be_BY, /* 10/ 33 Belarusian (Belarus) */ + "be", LANGUAGE_be_BY, /* 9/ 32 Belarusian (Belarus) */ "bg", LANGUAGE_bg, /* 2/ 11 Bulgarian */ - "ca", LANGUAGE_ca, /* 11/117 Catalan */ + "ca", LANGUAGE_ca, /* 11/116 Catalan */ "cs", LANGUAGE_cs, /* 17/ 21 Czech */ - "da", LANGUAGE_da, /* 19/ 35 Danish */ - "de", LANGUAGE_de, /* 16/ 93 German */ + "da", LANGUAGE_da, /* 19/ 34 Danish */ + "de", LANGUAGE_de, /* 16/ 92 German */ "el", LANGUAGE_el, /* 1/ 11 Greek */ "en", LANGUAGE_en, /* 8/ 10 English */ - "eo", LANGUAGE_eo, /* 26/129 Esperanto */ - "es", LANGUAGE_es, /* 8/ 34 Spanish */ + "eo", LANGUAGE_eo, /* 26/128 Esperanto */ + "es", LANGUAGE_es, /* 8/ 33 Spanish */ "et", LANGUAGE_et_EE, /* 22/ 27 Estonian (Estonia) */ - "fa", LANGUAGE_fa, /* 61/120 Persian */ - "fi", LANGUAGE_fi, /* 10/ 35 Finnish */ - "fr", LANGUAGE_fr, /* 13/ 93 French */ - "hi", LANGUAGE_hi, /* 24/ 57 Hindi */ + "fa", LANGUAGE_fa, /* 61/119 Persian */ + "fi", LANGUAGE_fi, /* 10/ 34 Finnish */ + "fr", LANGUAGE_fr, /* 13/ 92 French */ + "hi", LANGUAGE_hi, /* 24/ 56 Hindi */ "hr", LANGUAGE_hr, /* 9/ 19 Croatian */ "hu", LANGUAGE_hu, /* 9/ 19 Hungarian */ - "hy", LANGUAGE_hy_AM, /* 8/123 Armenian (Armenia) */ + "hy", LANGUAGE_hy_AM, /* 8/122 Armenian (Armenia) */ "is", LANGUAGE_is, /* 13/ 25 Icelandic */ "it", LANGUAGE_it, /* 5/ 5 Italian */ - "iw", LANGUAGE_iw, /* 20/118 Hebrew */ + "iw", LANGUAGE_iw, /* 20/117 Hebrew */ "ka", LANGUAGE_ka_GE, /* 3/ 11 Georgian (Georgia) */ - "kk", LANGUAGE_kk, /* 16/115 Kazakh */ - "km", LANGUAGE_km_KH, /* 2/116 Khmer (Cambodia) */ - "ky", LANGUAGE_ky, /* 11/ 82 Kirghiz */ + "kk", LANGUAGE_kk, /* 15/114 Kazakh */ + "km", LANGUAGE_km_KH, /* 2/115 Khmer (Cambodia) */ + "ky", LANGUAGE_ky, /* 10/ 81 Kirghiz */ "lo", LANGUAGE_lo_LA, /* 2/ 20 Lao (Laos) */ "lt", LANGUAGE_lt, /* 18/ 22 Lithuanian */ "lv", LANGUAGE_lv, /* 18/ 22 Latvian */ - "mk", LANGUAGE_mk, /* 9/ 87 Macedonian */ + "mk", LANGUAGE_mk, /* 9/ 86 Macedonian */ "mn", LANGUAGE_mn_MN, /* 2/ 20 Mongolian (Mongolia) */ - "nb", LANGUAGE_nb, /* 11/ 35 Norwegian Bokmål */ - "ne", LANGUAGE_ne_NP, /* 24/ 57 Nepali (Nepal) */ + "nb", LANGUAGE_nb, /* 11/ 34 Norwegian Bokmål */ + "ne", LANGUAGE_ne_NP, /* 24/ 56 Nepali (Nepal) */ "nl", LANGUAGE_nl, /* 9/ 12 Dutch */ "pl", LANGUAGE_pl, /* 10/ 16 Polish */ "pt", LANGUAGE_pt, /* 6/ 8 Portuguese */ "rm", LANGUAGE_rm, /* 1/ 2 Raeto-Romance */ "ro", LANGUAGE_ro, /* 6/ 15 Romanian */ - "ru", LANGUAGE_ru, /* 10/ 33 Russian */ + "ru", LANGUAGE_ru, /* 9/ 32 Russian */ "sk", LANGUAGE_sk, /* 20/ 22 Slovak */ "sl", LANGUAGE_sl, /* 8/ 19 Slovenian */ - "sr", LANGUAGE_sr, /* 11/ 87 Serbian */ - "sv", LANGUAGE_sv, /* 21/ 35 Swedish */ + "sr", LANGUAGE_sr, /* 11/ 86 Serbian */ + "sv", LANGUAGE_sv, /* 21/ 34 Swedish */ "sw", LANGUAGE_sw, /* 9/ 17 Swahili */ "th", LANGUAGE_th, /* 2/ 20 Thai */ "tl", LANGUAGE_tl, /* 7/ 10 Tagalog */ "tr", LANGUAGE_tr, /* 7/ 17 Turkish */ - "uk", LANGUAGE_uk, /* 12/ 81 Ukrainian */ + "uk", LANGUAGE_uk, /* 11/ 80 Ukrainian */ "vi", LANGUAGE_vi, /* 8/ 20 Vietnamese */ "zu", LANGUAGE_zu, /* 8/ 10 Zulu */ - "zz", LANGUAGE_zz, /* 19/112 Alphabet */ + "zz", LANGUAGE_zz, /* 19/111 Alphabet */ }; static { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b53d79269..544fd0319 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -87,6 +87,7 @@ public final class BinaryDictionary extends Dictionary { private final String mDictFilePath; private final boolean mIsUpdatable; private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; + private final int[] mOutputSuggestionCount = new int[1]; private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; private final int[] mSpaceIndices = new int[MAX_RESULTS]; private final int[] mOutputScores = new int[MAX_RESULTS]; @@ -158,10 +159,10 @@ public final class BinaryDictionary extends Dictionary { ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo, ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities); private static native int getNextWordNative(long dict, int token, int[] outCodePoints); - private static native int getSuggestionsNative(long dict, long proximityInfo, + private static native void getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint, - int[] suggestOptions, int[] prevWordCodePointArray, + int[] suggestOptions, int[] prevWordCodePointArray, int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence); private static native void addUnigramWordNative(long dict, int[] word, int probability, @@ -258,12 +259,13 @@ public final class BinaryDictionary extends Dictionary { mNativeSuggestOptions.setIsGesture(isGesture); mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); // proximityInfo and/or prevWordForBigrams may not be null. - final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), + getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(), - prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices, - mOutputTypes, mOutputAutoCommitFirstWordConfidence); + prevWordCodePointArray, mOutputSuggestionCount, mOutputCodePoints, mOutputScores, + mSpaceIndices, mOutputTypes, mOutputAutoCommitFirstWordConfidence); + final int count = mOutputSuggestionCount[0]; final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); for (int j = 0; j < count; ++j) { final int start = j * MAX_WORD_LENGTH; diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index d1ff714fc..e71723a15 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -144,6 +144,7 @@ public final class Constants { public static final int NOT_A_CODE = -1; public static final int NOT_A_CURSOR_POSITION = -1; + // TODO: replace the following constants with state in InputTransaction? public static final int NOT_A_COORDINATE = -1; public static final int SUGGESTION_STRIP_COORDINATE = -2; public static final int SPELL_CHECKER_COORDINATE = -3; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index cd18a6ba5..d6178fcee 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -55,7 +55,6 @@ public class DictionaryFacilitatorForSuggest { private final ConcurrentHashMap<String, Dictionary> mDictionaries = CollectionUtils.newConcurrentHashMap(); - private HashSet<String> mDictionarySubsetForDebug = null; private Dictionary mMainDictionary; private ContactsBinaryDictionary mContactsDictionary; @@ -85,7 +84,6 @@ public class DictionaryFacilitatorForSuggest { mContext = context; mLocale = locale; mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1); - initForDebug(settingsValues); loadMainDict(context, locale, listener); setUserDictionary(new UserBinaryDictionary(context, locale)); resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues); @@ -101,7 +99,6 @@ public class DictionaryFacilitatorForSuggest { final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) { mContext = oldDictionaryFacilitator.mContext; mLocale = oldDictionaryFacilitator.mLocale; - mDictionarySubsetForDebug = oldDictionaryFacilitator.mDictionarySubsetForDebug; mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1); loadMainDict(mContext, mLocale, listener); // Transfer user dictionary. @@ -130,7 +127,6 @@ public class DictionaryFacilitatorForSuggest { mContext = oldDictionaryFacilitator.mContext; mLocale = oldDictionaryFacilitator.mLocale; mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); - initForDebug(settingsValues); // Transfer main dictionary. setMainDictionary(oldDictionaryFacilitator.mMainDictionary); oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_MAIN); @@ -197,12 +193,12 @@ public class DictionaryFacilitatorForSuggest { } } - // initialize a debug flag for the personalization - private void initForDebug(final SettingsValues settingsValues) { - if (settingsValues.mUseOnlyPersonalizationDictionaryForDebug) { - mDictionarySubsetForDebug = new HashSet<String>(); - mDictionarySubsetForDebug.add(Dictionary.TYPE_PERSONALIZATION); - } + public boolean needsToBeRecreated(final Locale newLocale, + final SettingsValues newSettingsValues) { + return !mLocale.equals(newLocale) + || (newSettingsValues.mUseContactsDict != (mContactsDictionary != null)) + || (newSettingsValues.mUsePersonalizedDicts != (mUserHistoryDictionary != null)) + || (newSettingsValues.mUsePersonalizedDicts != hasPersonalizationDictionary()); } public void close() { @@ -531,10 +527,6 @@ public class DictionaryFacilitatorForSuggest { } private void addOrReplaceDictionary(final String key, final Dictionary dict) { - if (mDictionarySubsetForDebug != null && !mDictionarySubsetForDebug.contains(key)) { - Log.w(TAG, "Ignore add " + key + " dictionary for debug."); - return; - } final Dictionary oldDict; if (dict == null) { oldDict = mDictionaries.remove(key); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index bfc578082..a9e548060 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -59,6 +59,7 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; +import com.android.inputmethod.event.InputTransaction; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; @@ -78,7 +79,6 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; -import com.android.inputmethod.latin.utils.CompletionInfoUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; @@ -124,9 +124,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private View mKeyPreviewBackingView; private SuggestionStripView mSuggestionStripView; - // TODO[IL]: remove this member completely. - public CompletionInfo[] mApplicationSpecifiedCompletions; - private RichInputMethodManager mRichImm; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; @@ -192,8 +189,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; switch (msg.what) { case MSG_UPDATE_SUGGESTION_STRIP: + cancelUpdateSuggestionStrip(); latinIme.mInputLogic.performUpdateSuggestionStripSync( - latinIme.mSettings.getCurrent(), this /* handler */); + latinIme.mSettings.getCurrent()); break; case MSG_UPDATE_SHIFT_STATE: switcher.updateShiftState(); @@ -530,27 +528,31 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final EditorInfo editorInfo = getCurrentInputEditorInfo(); final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); mSettings.loadSettings(this, locale, inputAttributes); - AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(mSettings.getCurrent()); - // To load the keyboard we need to load all the settings once, but resetting the - // contacts dictionary should be deferred until after the new layout has been displayed - // to improve responsivity. In the language switching process, we post a reopenDictionaries - // message, then come here to read the settings for the new language before we change - // the layout; at this time, we need to skip resetting the contacts dictionary. It will - // be done later inside {@see #initSuggest()} when the reopenDictionaries message is - // processed. final SettingsValues currentSettingsValues = mSettings.getCurrent(); - final Suggest suggest = mInputLogic.mSuggest; - if (!mHandler.hasPendingReopenDictionaries() && suggest != null) { + AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues); + // This method is called on startup and language switch, before the new layout has + // been displayed. Opening dictionaries never affects responsivity as dictionaries are + // asynchronously loaded. + initOrResetSuggestForSettingsValues(mInputLogic.mSuggest, locale, currentSettingsValues); + } + + private void initOrResetSuggestForSettingsValues(final Suggest oldSuggest, + final Locale locale, final SettingsValues settingsValues) { + if (!mHandler.hasPendingReopenDictionaries() && oldSuggest != null) { // May need to reset dictionaries depending on the user settings. final DictionaryFacilitatorForSuggest oldDictionaryFacilitator = - suggest.mDictionaryFacilitator; + oldSuggest.mDictionaryFacilitator; + if (!oldDictionaryFacilitator.needsToBeRecreated(locale, settingsValues)) { + // Continue to use the same dictionary facilitator if no configuration has changed. + refreshPersonalizationDictionarySession(); + return; + } final DictionaryFacilitatorForSuggest dictionaryFacilitator = - new DictionaryFacilitatorForSuggest(currentSettingsValues, - oldDictionaryFacilitator); + new DictionaryFacilitatorForSuggest(settingsValues, oldDictionaryFacilitator); // Create Suggest instance with the new dictionary facilitator. - resetSuggest(new Suggest(suggest /* oldSuggest */, dictionaryFacilitator)); - } else if (suggest == null) { - initSuggestForLocale(locale); + replaceSuggest(new Suggest(oldSuggest, dictionaryFacilitator)); + } else if (oldSuggest == null) { + initSuggest(); } } @@ -610,13 +612,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { subtypeLocale = switcherSubtypeLocale; } - initSuggestForLocale(subtypeLocale); + initSuggestForLocale(mInputLogic.mSuggest, subtypeLocale); } - private void initSuggestForLocale(final Locale locale) { + private void initSuggestForLocale(final Suggest oldSuggest, final Locale locale) { final SettingsValues settingsValues = mSettings.getCurrent(); final DictionaryFacilitatorForSuggest oldDictionaryFacilitator = - (mInputLogic.mSuggest == null) ? null : mInputLogic.mSuggest.mDictionaryFacilitator; + (oldSuggest == null) ? null : oldSuggest.mDictionaryFacilitator; // Creates new dictionary facilitator for the new locale. final DictionaryFacilitatorForSuggest dictionaryFacilitator = new DictionaryFacilitatorForSuggest(this /* context */, locale, settingsValues, @@ -625,7 +627,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (settingsValues.mCorrectionEnabled) { newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold); } - resetSuggest(newSuggest); + replaceSuggest(newSuggest); } /* package private */ void resetSuggestMainDict() { @@ -633,10 +635,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputLogic.mSuggest.mDictionaryFacilitator; final DictionaryFacilitatorForSuggest dictionaryFacilitator = new DictionaryFacilitatorForSuggest(this /* listener */, oldDictionaryFacilitator); - resetSuggest(new Suggest(mInputLogic.mSuggest /* oldSuggest */, dictionaryFacilitator)); + replaceSuggest(new Suggest(mInputLogic.mSuggest /* oldSuggest */, dictionaryFacilitator)); } - private void resetSuggest(final Suggest newSuggest) { + private void replaceSuggest(final Suggest newSuggest) { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.getInstance().initDictionary(newSuggest.mDictionaryFacilitator); } @@ -808,7 +810,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? updateFullscreenMode(); - mApplicationSpecifiedCompletions = null; // The app calling setText() has the effect of clearing the composing // span, so we should reset our state unconditionally, even if restarting is true. @@ -875,7 +876,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // This will set the punctuation suggestions if next word suggestion is off; // otherwise it will clear the suggestion strip. - setNeutralSuggestionStripInternal(); + setNeutralSuggestionStrip(); mHandler.cancelUpdateSuggestionStrip(); mHandler.cancelDoubleSpacePeriodTimer(); @@ -950,8 +951,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown(). // TODO: find a better way to simulate actual execution. if (isInputViewShown() && - mInputLogic.onUpdateSelection(mSettings.getCurrent(), oldSelStart, oldSelEnd, - newSelStart, newSelEnd, composingSpanStart, composingSpanEnd)) { + mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { mKeyboardSwitcher.updateShiftState(); } @@ -1030,8 +1030,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } return; } - mApplicationSpecifiedCompletions = - CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions); final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = SuggestedWords.getFromApplicationSpecifiedCompletions( @@ -1046,18 +1044,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void setSuggestionStripShownInternal(final boolean isSuggestionStripVisible) { - // TODO: Modify this if we support suggestions with hard keyboard - if (!onEvaluateInputViewShown() || !hasSuggestionStripView()) { - return; - } - if (isSuggestionStripVisible) { - mSuggestionStripView.setVisibility(View.VISIBLE); - } else { - mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE); - } - } - private int getAdjustedBackingViewHeight() { final int currentHeight = mKeyPreviewBackingView.getHeight(); if (currentHeight > 0) { @@ -1280,8 +1266,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSubtypeSwitcher.switchToShortcutIME(this); // Still call the *#onCodeInput methods for readability. } - mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(), mHandler, - mKeyboardSwitcher); + final InputTransaction completeInputTransaction = + mInputLogic.onCodeInput(mSettings.getCurrent(), codeToSend, keyX, keyY, + mKeyboardSwitcher.getKeyboardShiftMode(), mHandler); + switch (completeInputTransaction.getRequiredShiftUpdate()) { + case InputTransaction.SHIFT_UPDATE_LATER: + mHandler.postUpdateShiftState(); + break; + case InputTransaction.SHIFT_UPDATE_NOW: + mKeyboardSwitcher.updateShiftState(); + break; + default: // SHIFT_NO_UPDATE + } mKeyboardSwitcher.onCodeInput(codePoint); } @@ -1295,7 +1291,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onStartBatchInput() { - mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler); + mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler); } @Override @@ -1305,7 +1301,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onEndBatchInput(final InputPointers batchPointers) { - mInputLogic.onEndBatchInput(mSettings.getCurrent(), batchPointers); + mInputLogic.onEndBatchInput(batchPointers); } @Override @@ -1383,13 +1379,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void setSuggestedWords(final SuggestedWords suggestedWords, final boolean isSuggestionStripVisible) { mInputLogic.setSuggestedWords(suggestedWords); + // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { return; } + mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); + if (!onEvaluateInputViewShown()) { + return; + } + if (!isSuggestionStripVisible) { + mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE); + return; + } + mSuggestionStripView.setVisibility(View.VISIBLE); + final SettingsValues currentSettings = mSettings.getCurrent(); final boolean showSuggestions; - if (SuggestedWords.EMPTY == suggestedWords - || suggestedWords.isPunctuationSuggestions() + if (SuggestedWords.EMPTY == suggestedWords || suggestedWords.isPunctuationSuggestions() || !currentSettings.isSuggestionsRequested()) { showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( currentSettings.mInputAttributes); @@ -1400,8 +1406,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } - mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); - setSuggestionStripShownInternal(isSuggestionStripVisible); } // TODO[IL]: Move this out of LatinIME. @@ -1445,32 +1449,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sequenceNumber, callback); } - // TODO[IL]: Move this to InputLogic - public SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, - final SuggestedWords suggestedWords, final SuggestedWords previousSuggestedWords) { - // TODO: consolidate this into getSuggestedWords - // We update the suggestion strip only when we have some suggestions to show, i.e. when - // the suggestion count is > 1; else, we leave the old suggestions, with the typed word - // replaced with the new one. However, when the length of the typed word is 1 or 0 (after - // a deletion typically), we do want to remove the old suggestions. Also, if we are showing - // the "add to dictionary" hint, we need to revert to suggestions - although it is unclear - // how we can come here if it's displayed. - if (suggestedWords.size() > 1 || typedWord.length() <= 1 - || !hasSuggestionStripView() || isShowingAddToDictionaryHint()) { - return suggestedWords; - } else { - final SuggestedWords punctuationList = - mSettings.getCurrent().mSpacingAndPunctuations.mSuggestPuncList; - final SuggestedWords oldSuggestedWords = previousSuggestedWords == punctuationList - ? SuggestedWords.EMPTY : previousSuggestedWords; - final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = - SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); - return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, - false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - true /* isObsoleteSuggestions */, false /* isPrediction */); - } - } - @Override public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) { final SuggestedWords suggestedWords = @@ -1511,15 +1489,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.showAddToDictionaryHint(word); } - // TODO[IL]: Define a clean interface for this // This will show either an empty suggestion strip (if prediction is enabled) or // punctuation suggestions (if it's disabled). @Override public void setNeutralSuggestionStrip() { - setNeutralSuggestionStripInternal(); - } - - private void setNeutralSuggestionStripInternal() { final SettingsValues currentSettings = mSettings.getCurrent(); final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; @@ -1733,7 +1706,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final DictionaryFacilitatorForSuggest dictionaryFacilitator = new DictionaryFacilitatorForSuggest(this, locale, mSettings.getCurrent(), this /* listener */, oldDictionaryFacilitator); - resetSuggest(new Suggest(locale, dictionaryFacilitator)); + replaceSuggest(new Suggest(locale, dictionaryFacilitator)); } // DO NOT USE THIS for any other purpose than testing. diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 6b6bbf3a7..630a03670 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -50,7 +50,7 @@ public final class RichInputMethodManager { private static final RichInputMethodManager sInstance = new RichInputMethodManager(); private InputMethodManagerCompatWrapper mImmWrapper; - private InputMethodInfo mInputMethodInfoOfThisIme; + private InputMethodInfoCache mInputMethodInfoCache; final HashMap<InputMethodInfo, List<InputMethodSubtype>> mSubtypeListCacheWithImplicitlySelectedSubtypes = CollectionUtils.newHashMap(); final HashMap<InputMethodInfo, List<InputMethodSubtype>> @@ -83,7 +83,8 @@ public final class RichInputMethodManager { return; } mImmWrapper = new InputMethodManagerCompatWrapper(context); - mInputMethodInfoOfThisIme = getInputMethodInfoOfThisIme(context); + mInputMethodInfoCache = new InputMethodInfoCache( + mImmWrapper.mImm, context.getPackageName()); // Initialize additional subtypes. SubtypeLocaleUtils.init(context); @@ -99,20 +100,10 @@ public final class RichInputMethodManager { return mImmWrapper.mImm; } - private InputMethodInfo getInputMethodInfoOfThisIme(final Context context) { - final String packageName = context.getPackageName(); - for (final InputMethodInfo imi : mImmWrapper.mImm.getInputMethodList()) { - if (imi.getPackageName().equals(packageName)) { - return imi; - } - } - throw new RuntimeException("Input method id for " + packageName + " not found."); - } - public List<InputMethodSubtype> getMyEnabledInputMethodSubtypeList( boolean allowsImplicitlySelectedSubtypes) { - return getEnabledInputMethodSubtypeList(mInputMethodInfoOfThisIme, - allowsImplicitlySelectedSubtypes); + return getEnabledInputMethodSubtypeList( + getInputMethodInfoOfThisIme(), allowsImplicitlySelectedSubtypes); } public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) { @@ -153,10 +144,10 @@ public final class RichInputMethodManager { private boolean switchToNextInputMethodAndSubtype(final IBinder token) { final InputMethodManager imm = mImmWrapper.mImm; final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList(); - final int currentIndex = getImiIndexInList(mInputMethodInfoOfThisIme, enabledImis); + final int currentIndex = getImiIndexInList(getInputMethodInfoOfThisIme(), enabledImis); if (currentIndex == INDEX_NOT_FOUND) { Log.w(TAG, "Can't find current IME in enabled IMEs: IME package=" - + mInputMethodInfoOfThisIme.getPackageName()); + + getInputMethodInfoOfThisIme().getPackageName()); return false; } final InputMethodInfo nextImi = getNextNonAuxiliaryIme(currentIndex, enabledImis); @@ -213,16 +204,45 @@ public final class RichInputMethodManager { return true; } + private static class InputMethodInfoCache { + private final InputMethodManager mImm; + private final String mImePackageName; + + private InputMethodInfo mCachedValue; + + public InputMethodInfoCache(final InputMethodManager imm, final String imePackageName) { + mImm = imm; + mImePackageName = imePackageName; + } + + public synchronized InputMethodInfo get() { + if (mCachedValue != null) { + return mCachedValue; + } + for (final InputMethodInfo imi : mImm.getInputMethodList()) { + if (imi.getPackageName().equals(mImePackageName)) { + mCachedValue = imi; + return imi; + } + } + throw new RuntimeException("Input method id for " + mImePackageName + " not found."); + } + + public synchronized void clear() { + mCachedValue = null; + } + } + public InputMethodInfo getInputMethodInfoOfThisIme() { - return mInputMethodInfoOfThisIme; + return mInputMethodInfoCache.get(); } public String getInputMethodIdOfThisIme() { - return mInputMethodInfoOfThisIme.getId(); + return getInputMethodInfoOfThisIme().getId(); } public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) { - return checkIfSubtypeBelongsToImeAndEnabled(mInputMethodInfoOfThisIme, subtype); + return checkIfSubtypeBelongsToImeAndEnabled(getInputMethodInfoOfThisIme(), subtype); } public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled( @@ -258,7 +278,7 @@ public final class RichInputMethodManager { } public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) { - return getSubtypeIndexInIme(subtype, mInputMethodInfoOfThisIme) != INDEX_NOT_FOUND; + return getSubtypeIndexInIme(subtype, getInputMethodInfoOfThisIme()) != INDEX_NOT_FOUND; } private static int getSubtypeIndexInIme(final InputMethodSubtype subtype, @@ -286,7 +306,8 @@ public final class RichInputMethodManager { public boolean hasMultipleEnabledSubtypesInThisIme( final boolean shouldIncludeAuxiliarySubtypes) { - final List<InputMethodInfo> imiList = Collections.singletonList(mInputMethodInfoOfThisIme); + final List<InputMethodInfo> imiList = Collections.singletonList( + getInputMethodInfoOfThisIme()); return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList); } @@ -340,7 +361,7 @@ public final class RichInputMethodManager { public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString, final String keyboardLayoutSetName) { - final InputMethodInfo myImi = mInputMethodInfoOfThisIme; + final InputMethodInfo myImi = getInputMethodInfoOfThisIme(); final int count = myImi.getSubtypeCount(); for (int i = 0; i < count; i++) { final InputMethodSubtype subtype = myImi.getSubtypeAt(i); @@ -355,13 +376,14 @@ public final class RichInputMethodManager { public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) { mImmWrapper.mImm.setInputMethodAndSubtype( - token, mInputMethodInfoOfThisIme.getId(), subtype); + token, getInputMethodIdOfThisIme(), subtype); } public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) { mImmWrapper.mImm.setAdditionalInputMethodSubtypes( - mInputMethodInfoOfThisIme.getId(), subtypes); - // Clear the cache so that we go read the subtypes again next time. + getInputMethodIdOfThisIme(), subtypes); + // Clear the cache so that we go read the {@link InputMethodInfo} of this IME and list of + // subtypes again next time. clearSubtypeCaches(); } @@ -382,5 +404,6 @@ public final class RichInputMethodManager { public void clearSubtypeCaches() { mSubtypeListCacheWithImplicitlySelectedSubtypes.clear(); mSubtypeListCacheWithoutImplicitlySelectedSubtypes.clear(); + mInputMethodInfoCache.clear(); } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index df25b0c29..ba64028ca 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -60,6 +60,7 @@ public final class Suggest { // Locale used for upper- and title-casing words public final Locale mLocale; + // TODO: Move dictionaryFacilitator constructing logics from LatinIME to Suggest. public Suggest(final Locale locale, final DictionaryFacilitatorForSuggest dictionaryFacilitator) { mLocale = locale; diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 06bc90c97..dc2c9fd0e 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -167,15 +167,10 @@ public class SuggestedWords { final CompletionInfo[] infos) { final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList(); for (final CompletionInfo info : infos) { - if (info == null) continue; - final CharSequence text = info.getText(); - if (null == text) continue; - final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(), - SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED, - Dictionary.DICTIONARY_APPLICATION_DEFINED, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); - result.add(suggestedWordInfo); + if (null == info || null == info.getText()) { + continue; + } + result.add(new SuggestedWordInfo(info)); } return result; } @@ -234,6 +229,9 @@ public class SuggestedWords { public static final int KIND_FLAG_EXACT_MATCH = 0x40000000; public final String mWord; + // The completion info from the application. Null for suggestions that don't come from + // the application (including keyboard-computed ones, so this is almost always null) + public final CompletionInfo mApplicationSpecifiedCompletionInfo; public final int mScore; public final int mKind; // one of the KIND_* constants above public final int mCodePointCount; @@ -260,6 +258,7 @@ public class SuggestedWords { final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord, final int autoCommitFirstWordConfidence) { mWord = word; + mApplicationSpecifiedCompletionInfo = null; mScore = score; mKind = kind; mSourceDict = sourceDict; @@ -268,6 +267,22 @@ public class SuggestedWords { mAutoCommitFirstWordConfidence = autoCommitFirstWordConfidence; } + /** + * Create a new suggested word info from an application-specified completion. + * If the passed argument or its contained text is null, this throws a NPE. + * @param applicationSpecifiedCompletion The application-specified completion info. + */ + public SuggestedWordInfo(final CompletionInfo applicationSpecifiedCompletion) { + mWord = applicationSpecifiedCompletion.getText().toString(); + mApplicationSpecifiedCompletionInfo = applicationSpecifiedCompletion; + mScore = SuggestedWordInfo.MAX_SCORE; + mKind = SuggestedWordInfo.KIND_APP_DEFINED; + mSourceDict = Dictionary.DICTIONARY_APPLICATION_DEFINED; + mCodePointCount = StringUtils.codePointCount(mWord); + mIndexOfTouchPointOfSecondWord = SuggestedWordInfo.NOT_AN_INDEX; + mAutoCommitFirstWordConfidence = SuggestedWordInfo.NOT_A_CONFIDENCE; + } + public boolean isEligibleForAutoCommit() { return (KIND_CORRECTION == mKind && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord); } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index f2f9f1e68..dd9d6e8a9 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -23,12 +23,12 @@ import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.event.EventInterpreter; +import com.android.inputmethod.event.InputTransaction; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; @@ -74,7 +74,7 @@ public final class InputLogic { // TODO : make all these fields private as soon as possible. // Current space state of the input method. This can be any of the above constants. - public int mSpaceState; + private int mSpaceState; // Never null public SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; // TODO: mSuggest should be touched by a single thread. @@ -85,7 +85,7 @@ public final class InputLogic { public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; public final WordComposer mWordComposer; public final RichInputConnection mConnection; - public final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus(); + private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus(); private int mDeleteCount; private long mLastKeyTime; @@ -96,7 +96,7 @@ public final class InputLogic { // TODO: This boolean is persistent state and causes large side effects at unexpected times. // Find a way to remove it for readability. - public boolean mIsAutoCorrectionIndicatorOn; + private boolean mIsAutoCorrectionIndicatorOn; public InputLogic(final LatinIME latinIME, final SuggestionStripViewAccessor suggestionStripViewAccessor) { @@ -205,9 +205,9 @@ public final class InputLogic { LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords); // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. final int primaryCode = suggestion.charAt(0); - onCodeInput(primaryCode, + onCodeInput(settingsValues, primaryCode, Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE, - settingsValues, handler, keyboardSwitcher); + keyboardSwitcher.getKeyboardShiftMode(), handler); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, false /* isBatchMode */, suggestedWords.mIsPrediction); @@ -227,17 +227,16 @@ public final class InputLogic { } } - // TODO: stop relying on mApplicationSpecifiedCompletions. The SuggestionInfo object - // should contain a reference to the CompletionInfo instead. - if (settingsValues.isApplicationSpecifiedCompletionsOn() - && mLatinIME.mApplicationSpecifiedCompletions != null - && index >= 0 && index < mLatinIME.mApplicationSpecifiedCompletions.length) { + // TODO: We should not need the following branch. We should be able to take the same + // code path as for other kinds, use commitChosenWord, and do everything normally. We will + // however need to reset the suggestion strip right away, because we know we can't take + // the risk of calling commitCompletion twice because we don't know how the app will react. + if (SuggestedWordInfo.KIND_APP_DEFINED == suggestionInfo.mKind) { mSuggestedWords = SuggestedWords.EMPTY; mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); keyboardSwitcher.updateShiftState(); resetComposingState(true /* alsoResetLastComposedWord */); - final CompletionInfo completionInfo = mLatinIME.mApplicationSpecifiedCompletions[index]; - mConnection.commitCompletion(completionInfo); + mConnection.commitCompletion(suggestionInfo.mApplicationSpecifiedCompletionInfo); mConnection.endBatchEdit(); return; } @@ -290,19 +289,14 @@ public final class InputLogic { * Consider an update to the cursor position. Evaluate whether this update has happened as * part of normal typing or whether it was an explicit cursor move by the user. In any case, * do the necessary adjustments. - * @param settingsValues the current settings * @param oldSelStart old selection start * @param oldSelEnd old selection end * @param newSelStart new selection start * @param newSelEnd new selection end - * @param composingSpanStart composing span start - * @param composingSpanEnd composing span end * @return whether the cursor has moved as a result of user interaction. */ - public boolean onUpdateSelection(final SettingsValues settingsValues, - final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd, - final int composingSpanStart, final int composingSpanEnd) { + public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd, + final int newSelStart, final int newSelEnd) { if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) { return false; } @@ -335,8 +329,7 @@ public final class InputLogic { // we'd have the suggestion strip noticeably janky. To avoid that, we don't clear // it here, which means we'll keep outdated suggestions for a split second but the // visual result is better. - resetEntireInputState(settingsValues, newSelStart, newSelEnd, - false /* clearSuggestionStrip */); + resetEntireInputState(newSelStart, newSelEnd, false /* clearSuggestionStrip */); } else { // resetEntireInputState calls resetCachesUponCursorMove, but forcing the // composition to end. But in all cases where we don't reset the entire input @@ -360,48 +353,49 @@ public final class InputLogic { * Typically, this is called whenever a key is pressed on the software keyboard. This is not * the entry point for gesture input; see the onBatchInput* family of functions for this. * + * @param settingsValues the current settings values. * @param code the code to handle. It may be a code point, or an internal key code. * @param x the x-coordinate where the user pressed the key, or NOT_A_COORDINATE. * @param y the y-coordinate where the user pressed the key, or NOT_A_COORDINATE. + * @param keyboardShiftMode the current shift mode of the keyboard, as returned by + * {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()} + * @return the complete transaction object */ - public void onCodeInput(final int code, final int x, final int y, - final SettingsValues settingsValues, - // TODO: remove these two arguments - final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) { + public InputTransaction onCodeInput(final SettingsValues settingsValues, final int code, + final int x, final int y, final int keyboardShiftMode, + // TODO: remove this argument + final LatinIME.UIHandler handler) { + final InputTransaction inputTransaction = new InputTransaction(settingsValues, code, x, y, + SystemClock.uptimeMillis(), mSpaceState, + getActualCapsMode(settingsValues, keyboardShiftMode)); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_onCodeInput(code, x, y); + ResearchLogger.latinIME_onCodeInput(inputTransaction.mKeyCode, + inputTransaction.mX, inputTransaction.mY); } - final long when = SystemClock.uptimeMillis(); - if (code != Constants.CODE_DELETE - || when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) { + if (inputTransaction.mKeyCode != Constants.CODE_DELETE + || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) { mDeleteCount = 0; } - mLastKeyTime = when; + mLastKeyTime = inputTransaction.mTimestamp; mConnection.beginBatchEdit(); - // The space state depends only on the last character pressed and its own previous - // state. Here, we revert the space state to neutral if the key is actually modifying - // the input contents (any non-shift key), which is what we should do for - // all inputs that do not result in a special state. Each character handling is then - // free to override the state as they see fit. - final int spaceState = mSpaceState; if (!mWordComposer.isComposingWord()) { mIsAutoCorrectionIndicatorOn = false; } // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state. - if (code != Constants.CODE_SPACE) { + if (inputTransaction.mKeyCode != Constants.CODE_SPACE) { handler.cancelDoubleSpacePeriodTimer(); } boolean didAutoCorrect = false; - switch (code) { + switch (inputTransaction.mKeyCode) { case Constants.CODE_DELETE: - handleBackspace(settingsValues, spaceState, handler, keyboardSwitcher); - LatinImeLogger.logOnDelete(x, y); + handleBackspace(inputTransaction, handler); + LatinImeLogger.logOnDelete(inputTransaction.mX, inputTransaction.mY); break; case Constants.CODE_SHIFT: - performRecapitalization(settingsValues); - keyboardSwitcher.updateShiftState(); + performRecapitalization(inputTransaction.mSettingsValues); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); break; case Constants.CODE_CAPSLOCK: // Note: Changing keyboard to shift lock state is handled in @@ -455,32 +449,35 @@ public final class InputLogic { } else { // No action label, and the action from imeOptions is NONE: this is a regular // enter key that should input a carriage return. - didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER, - x, y, spaceState, keyboardSwitcher, handler); + didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler); } break; case Constants.CODE_SHIFT_ENTER: - didAutoCorrect = handleNonSpecialCharacter(settingsValues, Constants.CODE_ENTER, - x, y, spaceState, keyboardSwitcher, handler); + // TODO: remove this object + final InputTransaction tmpTransaction = new InputTransaction( + inputTransaction.mSettingsValues, inputTransaction.mKeyCode, + inputTransaction.mX, inputTransaction.mY, inputTransaction.mTimestamp, + inputTransaction.mSpaceState, inputTransaction.mShiftState); + didAutoCorrect = handleNonSpecialCharacter(tmpTransaction, handler); break; case Constants.CODE_ALPHA_FROM_EMOJI: // Note: Switching back from Emoji keyboard to the main keyboard is being handled in // {@link KeyboardState#onCodeInput(int,int)}. break; default: - didAutoCorrect = handleNonSpecialCharacter(settingsValues, - code, x, y, spaceState, keyboardSwitcher, handler); + didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler); break; } // Reset after any single keystroke, except shift, capslock, and symbol-shift - if (!didAutoCorrect && code != Constants.CODE_SHIFT - && code != Constants.CODE_CAPSLOCK - && code != Constants.CODE_SWITCH_ALPHA_SYMBOL) + if (!didAutoCorrect && inputTransaction.mKeyCode != Constants.CODE_SHIFT + && inputTransaction.mKeyCode != Constants.CODE_CAPSLOCK + && inputTransaction.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL) mLastComposedWord.deactivate(); - if (Constants.CODE_DELETE != code) { + if (Constants.CODE_DELETE != inputTransaction.mKeyCode) { mEnteredText = null; } mConnection.endBatchEdit(); + return inputTransaction; } public void onStartBatchInput(final SettingsValues settingsValues, @@ -504,7 +501,7 @@ public final class InputLogic { 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(settingsValues, mConnection.getExpectedSelectionStart(), + resetEntireInputState(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */); } else if (wordComposerSize <= 1) { // We auto-correct the previous (typed, not gestured) string iff it's one character @@ -585,8 +582,7 @@ public final class InputLogic { mInputLogicHandler.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber); } - public void onEndBatchInput(final SettingsValues settingValues, - final InputPointers batchPointers) { + public void onEndBatchInput(final InputPointers batchPointers) { mInputLogicHandler.onEndBatchInput(batchPointers, mAutoCommitSequenceNumber); ++mAutoCommitSequenceNumber; } @@ -625,31 +621,26 @@ public final class InputLogic { * manage keyboard-related stuff like shift, language switch, settings, layout switch, or * any key that results in multiple code points like the ".com" key. * - * @param settingsValues The current settings values. - * @param codePoint the code point associated with the key. - * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. - * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. - * @param spaceState the space state at start of the batch input. + * @param inputTransaction The transaction in progress. * @return whether this caused an auto-correction to happen. */ - private boolean handleNonSpecialCharacter(final SettingsValues settingsValues, - final int codePoint, final int x, final int y, final int spaceState, - // TODO: remove these arguments - final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { + private boolean handleNonSpecialCharacter(final InputTransaction inputTransaction, + // TODO: remove this argument + final LatinIME.UIHandler handler) { mSpaceState = SpaceState.NONE; final boolean didAutoCorrect; - if (settingsValues.isWordSeparator(codePoint) - || Character.getType(codePoint) == Character.OTHER_SYMBOL) { - didAutoCorrect = handleSeparator(settingsValues, codePoint, - Constants.SUGGESTION_STRIP_COORDINATE == x, spaceState, keyboardSwitcher, - handler); - if (settingsValues.mIsInternal) { - LatinImeLoggerUtils.onSeparator((char)codePoint, x, y); + if (inputTransaction.mSettingsValues.isWordSeparator(inputTransaction.mKeyCode) + || Character.getType(inputTransaction.mKeyCode) == Character.OTHER_SYMBOL) { + didAutoCorrect = handleSeparator(inputTransaction, + Constants.SUGGESTION_STRIP_COORDINATE == inputTransaction.mX, handler); + if (inputTransaction.mSettingsValues.mIsInternal) { + LatinImeLoggerUtils.onSeparator((char)inputTransaction.mKeyCode, + inputTransaction.mX, inputTransaction.mY); } } else { didAutoCorrect = false; - if (SpaceState.PHANTOM == spaceState) { - if (settingsValues.mIsInternal) { + if (SpaceState.PHANTOM == inputTransaction.mSpaceState) { + if (inputTransaction.mSettingsValues.mIsInternal) { if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) { LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer); @@ -658,14 +649,13 @@ public final class InputLogic { 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(settingsValues, mConnection.getExpectedSelectionStart(), + resetEntireInputState(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */); } else { - commitTyped(settingsValues, LastComposedWord.NOT_A_SEPARATOR); + commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR); } } - handleNonSeparator(settingsValues, codePoint, x, y, spaceState, - keyboardSwitcher, handler); + handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction, handler); } return didAutoCorrect; } @@ -673,15 +663,12 @@ public final class InputLogic { /** * Handle a non-separator. * @param settingsValues The current settings values. - * @param codePoint the code point associated with the key. - * @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. - * @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable. - * @param spaceState the space state at start of the batch input. + * @param inputTransaction The transaction in progress. */ private void handleNonSeparator(final SettingsValues settingsValues, - final int codePoint, final int x, final int y, final int spaceState, - // TODO: Remove these arguments - final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { + final InputTransaction inputTransaction, + // TODO: Remove this argument + final LatinIME.UIHandler handler) { // 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. @@ -689,7 +676,8 @@ public final class InputLogic { // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead. // See onStartBatchInput() to see how to do it. - if (SpaceState.PHANTOM == spaceState && !settingsValues.isWordConnector(codePoint)) { + if (SpaceState.PHANTOM == inputTransaction.mSpaceState + && !settingsValues.isWordConnector(inputTransaction.mKeyCode)) { if (isComposingWord) { // Sanity check throw new RuntimeException("Should not be composing here"); @@ -700,7 +688,7 @@ public final class InputLogic { 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(settingsValues, mConnection.getExpectedSelectionStart(), + resetEntireInputState(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */); isComposingWord = false; } @@ -711,7 +699,7 @@ public final class InputLogic { if (!isComposingWord // We only start composing if this is a word code point. Essentially that means it's a // a letter or a word connector. - && settingsValues.isWordCodePoint(codePoint) + && settingsValues.isWordCodePoint(inputTransaction.mKeyCode) // We never go into composing state if suggestions are not requested. && settingsValues.isSuggestionsRequested() && // In languages with spaces, we only start composing a word when we are not already @@ -722,8 +710,8 @@ public final class InputLogic { // the character is a single quote or a dash. The idea here is, single quote and dash // are not separators and they should be treated as normal characters, except in the // first position where they should not start composing a word. - isComposingWord = (Constants.CODE_SINGLE_QUOTE != codePoint - && Constants.CODE_DASH != codePoint); + isComposingWord = (Constants.CODE_SINGLE_QUOTE != inputTransaction.mKeyCode + && Constants.CODE_DASH != inputTransaction.mKeyCode); // Here we don't need to reset the last composed word. It will be reset // when we commit this one, if we ever do; if on the other hand we backspace // it entirely and resume suggestions on the previous word, we'd like to still @@ -731,26 +719,25 @@ public final class InputLogic { resetComposingState(false /* alsoResetLastComposedWord */); } if (isComposingWord) { - mWordComposer.add(codePoint, x, y); + mWordComposer.add(inputTransaction.mKeyCode, inputTransaction.mX, inputTransaction.mY); // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { // 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(settingsValues, keyboardSwitcher.getKeyboardShiftMode()), - getNthPreviousWordForSuggestion( + inputTransaction.mShiftState, getNthPreviousWordForSuggestion( settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */)); } mConnection.setComposingText(getTextWithUnderline( mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpace(settingsValues, - codePoint, spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x); + final boolean swapWeakSpace = maybeStripSpace(inputTransaction, + Constants.SUGGESTION_STRIP_COORDINATE == inputTransaction.mX); - sendKeyCodePoint(settingsValues, codePoint); + sendKeyCodePoint(settingsValues, inputTransaction.mKeyCode); if (swapWeakSpace) { - swapSwapperAndSpace(keyboardSwitcher); + swapSwapperAndSpace(inputTransaction); mSpaceState = SpaceState.WEAK; } // In case the "add to dictionary" hint was still displayed. @@ -758,77 +745,80 @@ public final class InputLogic { } handler.postUpdateSuggestionStrip(); if (settingsValues.mIsInternal) { - LatinImeLoggerUtils.onNonSeparator((char)codePoint, x, y); + LatinImeLoggerUtils.onNonSeparator((char)inputTransaction.mKeyCode, inputTransaction.mX, + inputTransaction.mY); } } /** * Handle input of a separator code point. - * @param settingsValues The current settings values. - * @param codePoint the code point associated with the key. + * @param inputTransaction The transaction in progress. * @param isFromSuggestionStrip whether this code point comes from the suggestion strip. - * @param spaceState the space state at start of the batch input. * @return whether this caused an auto-correction to happen. */ - private boolean handleSeparator(final SettingsValues settingsValues, - final int codePoint, final boolean isFromSuggestionStrip, final int spaceState, - // TODO: remove these arguments - final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { + private boolean handleSeparator(final InputTransaction inputTransaction, + final boolean isFromSuggestionStrip, + // TODO: remove this argument + final LatinIME.UIHandler handler) { boolean didAutoCorrect = false; // We avoid sending spaces in languages without spaces if we were composing. - final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint - && !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces + final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == inputTransaction.mKeyCode + && !inputTransaction.mSettingsValues.mSpacingAndPunctuations + .mCurrentLanguageHasSpaces && mWordComposer.isComposingWord(); 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(settingsValues, mConnection.getExpectedSelectionStart(), + resetEntireInputState(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */); } // isComposingWord() may have changed since we stored wasComposing if (mWordComposer.isComposingWord()) { - if (settingsValues.mCorrectionEnabled) { + if (inputTransaction.mSettingsValues.mCorrectionEnabled) { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR - : StringUtils.newSingleCodePointString(codePoint); - commitCurrentAutoCorrection(settingsValues, separator, handler); + : StringUtils.newSingleCodePointString(inputTransaction.mKeyCode); + commitCurrentAutoCorrection(inputTransaction.mSettingsValues, separator, handler); didAutoCorrect = true; } else { - commitTyped(settingsValues, StringUtils.newSingleCodePointString(codePoint)); + commitTyped(inputTransaction.mSettingsValues, + StringUtils.newSingleCodePointString(inputTransaction.mKeyCode)); } } - final boolean swapWeakSpace = maybeStripSpace(settingsValues, codePoint, spaceState, - isFromSuggestionStrip); + final boolean swapWeakSpace = maybeStripSpace(inputTransaction, isFromSuggestionStrip); - final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint + final boolean isInsideDoubleQuoteOrAfterDigit = + Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode && mConnection.isInsideDoubleQuoteOrAfterDigit(); final boolean needsPrecedingSpace; - if (SpaceState.PHANTOM != spaceState) { + if (SpaceState.PHANTOM != inputTransaction.mSpaceState) { needsPrecedingSpace = false; - } else if (Constants.CODE_DOUBLE_QUOTE == codePoint) { + } else if (Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode) { // Double quotes behave like they are usually preceded by space iff we are // not inside a double quote or after a digit. needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit; } else { - needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint); + needsPrecedingSpace = inputTransaction.mSettingsValues.isUsuallyPrecededBySpace( + inputTransaction.mKeyCode); } if (needsPrecedingSpace) { - promotePhantomSpace(settingsValues); + promotePhantomSpace(inputTransaction.mSettingsValues); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleSeparator(codePoint, mWordComposer.isComposingWord()); + ResearchLogger.latinIME_handleSeparator(inputTransaction.mKeyCode, + mWordComposer.isComposingWord()); } if (!shouldAvoidSendingCode) { - sendKeyCodePoint(settingsValues, codePoint); + sendKeyCodePoint(inputTransaction.mSettingsValues, inputTransaction.mKeyCode); } - if (Constants.CODE_SPACE == codePoint) { - if (settingsValues.isSuggestionsRequested()) { - if (maybeDoubleSpacePeriod(settingsValues, handler)) { - keyboardSwitcher.updateShiftState(); + if (Constants.CODE_SPACE == inputTransaction.mKeyCode) { + if (inputTransaction.mSettingsValues.isSuggestionsRequested()) { + if (maybeDoubleSpacePeriod(inputTransaction.mSettingsValues, handler)) { + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); mSpaceState = SpaceState.DOUBLE; } else if (!mSuggestedWords.isPunctuationSuggestions()) { mSpaceState = SpaceState.WEAK; @@ -839,11 +829,12 @@ public final class InputLogic { handler.postUpdateSuggestionStrip(); } else { if (swapWeakSpace) { - swapSwapperAndSpace(keyboardSwitcher); + swapSwapperAndSpace(inputTransaction); mSpaceState = SpaceState.SWAP_PUNCTUATION; - } else if ((SpaceState.PHANTOM == spaceState - && settingsValues.isUsuallyFollowedBySpace(codePoint)) - || (Constants.CODE_DOUBLE_QUOTE == codePoint + } else if ((SpaceState.PHANTOM == inputTransaction.mSpaceState + && inputTransaction.mSettingsValues.isUsuallyFollowedBySpace( + inputTransaction.mKeyCode)) + || (Constants.CODE_DOUBLE_QUOTE == inputTransaction.mKeyCode && isInsideDoubleQuoteOrAfterDigit)) { // If we are in phantom space state, and the user presses a separator, we want to // stay in phantom space state so that the next keypress has a chance to add the @@ -864,31 +855,29 @@ public final class InputLogic { mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); } - keyboardSwitcher.updateShiftState(); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); return didAutoCorrect; } /** * Handle a press on the backspace key. - * @param settingsValues The current settings values. - * @param spaceState The space state at start of this batch edit. + * @param inputTransaction The transaction in progress. */ - private void handleBackspace(final SettingsValues settingsValues, final int spaceState, - // TODO: remove these arguments - final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) { + private void handleBackspace(final InputTransaction inputTransaction, + // TODO: remove this argument + final LatinIME.UIHandler handler) { mSpaceState = SpaceState.NONE; - final int deleteCountAtStart = mDeleteCount; mDeleteCount++; // 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 // during key repeat. - handler.postUpdateShiftState(); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_LATER); 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(settingsValues, mConnection.getExpectedSelectionStart(), + resetEntireInputState(mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), true /* clearSuggestionStrip */); // When we exit this if-clause, mWordComposer.isComposingWord() will return false. } @@ -909,14 +898,14 @@ public final class InputLogic { if (!mWordComposer.isComposingWord()) { // If we just removed the last character, auto-caps mode may have changed so we // need to re-evaluate. - keyboardSwitcher.updateShiftState(); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); } } else { if (mLastComposedWord.canRevertCommit()) { - if (settingsValues.mIsInternal) { + if (inputTransaction.mSettingsValues.mIsInternal) { LatinImeLoggerUtils.onAutoCorrectionCancellation(); } - revertCommit(settingsValues, handler); + revertCommit(inputTransaction.mSettingsValues, handler); return; } if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) { @@ -933,14 +922,14 @@ public final class InputLogic { // reverting any autocorrect at this point. So we can safely return. return; } - if (SpaceState.DOUBLE == spaceState) { + if (SpaceState.DOUBLE == inputTransaction.mSpaceState) { handler.cancelDoubleSpacePeriodTimer(); if (mConnection.revertDoubleSpacePeriod()) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) return; } - } else if (SpaceState.SWAP_PUNCTUATION == spaceState) { + } else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { if (mConnection.revertSwapPunctuation()) { // Likewise return; @@ -966,8 +955,8 @@ public final class InputLogic { // This should never happen. Log.e(TAG, "Backspace when we don't know the selection position"); } - if (settingsValues.isBeforeJellyBean() || - settingsValues.mInputAttributes.isTypeNull()) { + if (inputTransaction.mSettingsValues.isBeforeJellyBean() || + inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) { // There are two possible reasons to send a key event: either the field has // type TYPE_NULL, in which case the keyboard should send events, or we are // running in backward compatibility mode. Before Jelly bean, the keyboard @@ -1013,15 +1002,16 @@ public final class InputLogic { } } } - if (settingsValues.isSuggestionStripVisible() - && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces + if (inputTransaction.mSettingsValues.isSuggestionStripVisible() + && inputTransaction.mSettingsValues.mSpacingAndPunctuations + .mCurrentLanguageHasSpaces && !mConnection.isCursorFollowedByWordCharacter( - settingsValues.mSpacingAndPunctuations)) { - restartSuggestionsOnWordTouchedByCursor(settingsValues, + inputTransaction.mSettingsValues.mSpacingAndPunctuations)) { + restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues, true /* includeResumedWordInSuggestions */); } // We just removed at least one character. We need to update the auto-caps state. - keyboardSwitcher.updateShiftState(); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); } } @@ -1037,9 +1027,9 @@ public final class InputLogic { * * This method will check that there are two characters before the cursor and that the first * one is a space before it does the actual swapping. + * @param inputTransaction The transaction in progress. */ - // TODO: Remove this argument - private void swapSwapperAndSpace(final KeyboardSwitcher keyboardSwitcher) { + private void swapSwapperAndSpace(final InputTransaction inputTransaction) { 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) { @@ -1049,28 +1039,34 @@ public final class InputLogic { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text); } - keyboardSwitcher.updateShiftState(); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); } } /* * Strip a trailing space if necessary and returns whether it's a swap weak space situation. - * @param settingsValues The current settings values. - * @param codePoint The code point that is about to be inserted. - * @param spaceState The space state at start of this batch edit. + * @param inputTransaction The transaction in progress. * @param isFromSuggestionStrip Whether this code point is coming from the suggestion strip. * @return whether we should swap the space instead of removing it. */ - private boolean maybeStripSpace(final SettingsValues settingsValues, - final int code, final int spaceState, final boolean isFromSuggestionStrip) { - if (Constants.CODE_ENTER == code && SpaceState.SWAP_PUNCTUATION == spaceState) { + private boolean maybeStripSpace(final InputTransaction inputTransaction, + final boolean isFromSuggestionStrip) { + if (Constants.CODE_ENTER == inputTransaction.mKeyCode && + SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { mConnection.removeTrailingSpace(); return false; } - if ((SpaceState.WEAK == spaceState || SpaceState.SWAP_PUNCTUATION == spaceState) + if ((SpaceState.WEAK == inputTransaction.mSpaceState + || SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) && isFromSuggestionStrip) { - if (settingsValues.isUsuallyPrecededBySpace(code)) return false; - if (settingsValues.isUsuallyFollowedBySpace(code)) return true; + if (inputTransaction.mSettingsValues.isUsuallyPrecededBySpace( + inputTransaction.mKeyCode)) { + return false; + } + if (inputTransaction.mSettingsValues.isUsuallyFollowedBySpace( + inputTransaction.mKeyCode)) { + return true; + } mConnection.removeTrailingSpace(); } return false; @@ -1205,11 +1201,7 @@ public final class InputLogic { timeStampInSeconds); } - public void performUpdateSuggestionStripSync(final SettingsValues settingsValues, - // TODO: Remove this argument - final LatinIME.UIHandler handler) { - handler.cancelUpdateSuggestionStrip(); - + public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) { // Check if we have a suggestion engine attached. if (mSuggest == null || !settingsValues.isSuggestionsRequested()) { if (mWordComposer.isComposingWord()) { @@ -1229,11 +1221,15 @@ public final class InputLogic { SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - final SuggestedWords suggestedWordsWithMaybeOlderSuggestions = - mLatinIME.maybeRetrieveOlderSuggestions( - mWordComposer.getTypedWord(), suggestedWords, - mSuggestedWords); - holder.set(suggestedWordsWithMaybeOlderSuggestions); + final String typedWord = mWordComposer.getTypedWord(); + // Show new suggestions if we have at least one. Otherwise keep the old + // suggestions with the new typed word. Exception: if the length of the + // typed word is <= 1 (after a deletion typically) we clear old suggestions. + if (suggestedWords.size() > 1 || typedWord.length() <= 1) { + holder.set(suggestedWords); + } else { + holder.set(retrieveOlderSuggestions(typedWord, mSuggestedWords)); + } } } ); @@ -1304,7 +1300,10 @@ public final class InputLogic { SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } - if (!isResumableWord(settingsValues, typedWord)) return; + if (!isResumableWord(settingsValues, typedWord)) { + mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); + return; + } int i = 0; for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) { for (final String s : span.getSuggestions()) { @@ -1485,7 +1484,9 @@ public final class InputLogic { */ private int getActualCapsMode(final SettingsValues settingsValues, final int keyboardShiftMode) { - if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) return keyboardShiftMode; + if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) { + return keyboardShiftMode; + } final int auto = getCurrentAutoCapsState(settingsValues); if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) { return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED; @@ -1623,15 +1624,13 @@ public final class InputLogic { * This will clear the composing word, reset the last composed word, clear the suggestion * strip and tell the input connection about it so that it can refresh its caches. * - * @param settingsValues the current values of the settings. * @param newSelStart the new selection start, in java characters. * @param newSelEnd the new selection end, in java characters. * @param clearSuggestionStrip whether this method should clear the suggestion strip. */ // TODO: how is this different from startInput ?! - // TODO: remove all references to this in LatinIME and make this private - public void resetEntireInputState(final SettingsValues settingsValues, - final int newSelStart, final int newSelEnd, final boolean clearSuggestionStrip) { + private void resetEntireInputState(final int newSelStart, final int newSelEnd, + final boolean clearSuggestionStrip) { final boolean shouldFinishComposition = mWordComposer.isComposingWord(); resetComposingState(true /* alsoResetLastComposedWord */); if (clearSuggestionStrip) { @@ -1649,8 +1648,7 @@ public final class InputLogic { * * @param alsoResetLastComposedWord whether to also reset the last composed word. */ - // TODO: remove all references to this in LatinIME and make this private. - public void resetComposingState(final boolean alsoResetLastComposedWord) { + private void resetComposingState(final boolean alsoResetLastComposedWord) { mWordComposer.reset(); if (alsoResetLastComposedWord) { mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; @@ -1658,6 +1656,27 @@ public final class InputLogic { } /** + * Make a {@link com.android.inputmethod.latin.SuggestedWords} object containing a typed word + * and obsolete suggestions. + * See {@link com.android.inputmethod.latin.SuggestedWords#getTypedWordAndPreviousSuggestions( + * String, com.android.inputmethod.latin.SuggestedWords)}. + * @param typedWord The typed word as a string. + * @param previousSuggestedWords The previously suggested words. + * @return Obsolete suggestions with the newly typed word. + */ + private SuggestedWords retrieveOlderSuggestions(final String typedWord, + final SuggestedWords previousSuggestedWords) { + final SuggestedWords oldSuggestedWords = + previousSuggestedWords.isPunctuationSuggestions() ? SuggestedWords.EMPTY + : previousSuggestedWords; + final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = + SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); + return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, + false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, + true /* isObsoleteSuggestions */, false /* isPrediction */); + } + + /** * Gets a chunk of text with or the auto-correction indicator underline span as appropriate. * * This method looks at the old state of the auto-correction indicator to put or not put @@ -1674,9 +1693,8 @@ public final class InputLogic { * @param text the text on which to maybe apply the span. * @return the same text, with the auto-correction underline span if that's appropriate. */ - // TODO: remove all references to this in LatinIME and make this private. Also, shouldn't - // this go in some *Utils class instead? - public CharSequence getTextWithUnderline(final String text) { + // TODO: Shouldn't this go in some *Utils class instead? + private CharSequence getTextWithUnderline(final String text) { return mIsAutoCorrectionIndicatorOn ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(mLatinIME, text) : text; @@ -1710,6 +1728,7 @@ public final class InputLogic { * @param settingsValues the current values of the settings. * @param codePoint the code point to send. */ + // TODO: replace these two parameters with an InputTransaction private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) { if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_sendKeyCodePoint(codePoint); @@ -1741,8 +1760,7 @@ public final class InputLogic { * * @param settingsValues the current values of the settings. */ - // TODO: Make this private. - public void promotePhantomSpace(final SettingsValues settingsValues) { + private void promotePhantomSpace(final SettingsValues settingsValues) { if (settingsValues.shouldInsertSpacesAutomatically() && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces && !mConnection.textBeforeCursorLooksLikeURL()) { @@ -1848,7 +1866,8 @@ public final class InputLogic { final LatinIME.UIHandler handler) { // Complete any pending suggestions query first if (handler.hasPendingUpdateSuggestions()) { - performUpdateSuggestionStripSync(settingsValues, handler); + handler.cancelUpdateSuggestionStrip(); + performUpdateSuggestionStripSync(settingsValues); } final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); @@ -1892,8 +1911,7 @@ public final class InputLogic { * @param commitType the type of the commit, as one of LastComposedWord.COMMIT_TYPE_* * @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none. */ - // TODO: Make this private - public void commitChosenWord(final SettingsValues settingsValues, final String chosenWord, + private void commitChosenWord(final SettingsValues settingsValues, final String chosenWord, final int commitType, final String separatorString) { final SuggestedWords suggestedWords = mSuggestedWords; final CharSequence chosenWordWithSuggestions = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index c87dd1589..11d369282 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -42,8 +42,6 @@ public final class DebugSettings extends PreferenceFragment public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; public static final String PREF_STATISTICS_LOGGING = "enable_logging"; - public static final String PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG = - "use_only_personalization_dictionary_for_debug"; public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = "pref_key_preview_show_up_start_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 77968f79a..50fbbb19f 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -84,7 +84,6 @@ public final class SettingsValues { public final float mAutoCorrectionThreshold; public final boolean mCorrectionEnabled; public final int mSuggestionVisibility; - public final boolean mUseOnlyPersonalizationDictionaryForDebug; public final int mDisplayOrientation; private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds; @@ -168,8 +167,6 @@ public final class SettingsValues { prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, ResourceUtils.getFloatFromFraction( res, R.fraction.config_key_preview_dismiss_end_scale)); - mUseOnlyPersonalizationDictionaryForDebug = prefs.getBoolean( - DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false); mDisplayOrientation = res.getConfiguration().orientation; mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>(); final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo( @@ -390,8 +387,6 @@ public final class SettingsValues { sb.append("" + mCorrectionEnabled); sb.append("\n mSuggestionVisibility = "); sb.append("" + mSuggestionVisibility); - sb.append("\n mUseOnlyPersonalizationDictionaryForDebug = "); - sb.append("" + mUseOnlyPersonalizationDictionaryForDebug); sb.append("\n mDisplayOrientation = "); sb.append("" + mDisplayOrientation); sb.append("\n mAppWorkarounds = "); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 4ef562d8f..43cb11b14 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -35,7 +35,6 @@ import android.widget.RelativeLayout; import android.widget.TextView; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; @@ -281,8 +280,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final MoreKeysPanel.Controller mMoreSuggestionsController = new MoreKeysPanel.Controller() { @Override - public void onDismissMoreKeysPanel(final MoreKeysPanel panel) { - mMainKeyboardView.onDismissMoreKeysPanel(panel); + public void onDismissMoreKeysPanel() { + mMainKeyboardView.onDismissMoreKeysPanel(); } @Override @@ -291,7 +290,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } @Override - public void onCancelMoreKeysPanel(final MoreKeysPanel panel) { + public void onCancelMoreKeysPanel() { dismissMoreSuggestionsPanel(); } }; @@ -312,7 +311,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick } boolean showMoreSuggestions() { - final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard(); + final Keyboard parentKeyboard = mMainKeyboardView.getKeyboard(); if (parentKeyboard == null) { return false; } @@ -320,6 +319,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (!layoutHelper.mMoreSuggestionsAvailable) { return false; } + // Dismiss another {@link MoreKeysPanel} that may be being showed, for example + // {@link MoreKeysKeyboardView}. + mMainKeyboardView.onDismissMoreKeysPanel(); + // Dismiss all key previews and sliding key input preview that may be being showed. + mMainKeyboardView.dismissAllKeyPreviews(); + mMainKeyboardView.dismissSlidingKeyInputPreview(); final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index ef1d0f42c..2bb30a2ba 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -27,6 +27,7 @@ import android.text.TextUtils; import android.util.Log; import android.view.inputmethod.InputMethodSubtype; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.latin.R; @@ -42,6 +43,7 @@ public final class AdditionalSubtypeUtils { // This utility class is not publicly instantiable. } + @UsedForTesting public static boolean isAdditionalSubtype(final InputMethodSubtype subtype) { return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE); } diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index e7932b5a6..b9d526b5f 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -507,4 +507,44 @@ public final class StringUtils { return codePointCount(casedText) == 1 ? casedText.codePointAt(0) : CODE_UNSPECIFIED; } + + @UsedForTesting + public static class Stringizer<E> { + public String stringize(final E element) { + return element != null ? element.toString() : "null"; + } + + @UsedForTesting + public final String join(final E[] array) { + return joinStringArray(toStringArray(array), null /* delimiter */); + } + + @UsedForTesting + public final String join(final E[] array, final String delimiter) { + return joinStringArray(toStringArray(array), delimiter); + } + + protected String[] toStringArray(final E[] array) { + final String[] stringArray = new String[array.length]; + for (int index = 0; index < array.length; index++) { + stringArray[index] = stringize(array[index]); + } + return stringArray; + } + + protected String joinStringArray(final String[] stringArray, final String delimiter) { + if (stringArray == null) { + return "null"; + } + if (delimiter == null) { + return Arrays.toString(stringArray); + } + final StringBuilder sb = new StringBuilder(); + for (int index = 0; index < stringArray.length; index++) { + sb.append(index == 0 ? "[" : delimiter); + sb.append(stringArray[index]); + } + return sb + "]"; + } + } } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 3b3da96cc..9657b9d65 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -31,15 +31,12 @@ LOCAL_CFLAGS += -Werror -Wall -Wextra -Weffc++ -Wformat=2 -Wcast-qual -Wcast-ali -Wwrite-strings -Wfloat-equal -Wpointer-arith -Winit-self -Wredundant-decls \ -Woverloaded-virtual -Wstrict-null-sentinel -Wsign-promo -Wno-system-headers -ifeq ($(TARGET_ARCH), arm) -ifeq ($(TARGET_GCC_VERSION), 4.6) -LOCAL_CFLAGS += -Winline -endif # TARGET_GCC_VERSION -endif # TARGET_ARCH - # To suppress compiler warnings for unused variables/functions used for debug features etc. LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function +# For C++11 +LOCAL_CFLAGS += -std=c++11 + include $(LOCAL_PATH)/NativeFileList.mk LOCAL_SRC_FILES := \ @@ -64,7 +61,7 @@ LOCAL_MODULE := libjni_latinime_common_static LOCAL_MODULE_TAGS := optional LOCAL_SDK_VERSION := 14 -LOCAL_NDK_STL_VARIANT := stlport_static +LOCAL_NDK_STL_VARIANT := gnustl_static include $(BUILD_STATIC_LIBRARY) ###################################### @@ -87,7 +84,7 @@ LOCAL_MODULE := libjni_latinime LOCAL_MODULE_TAGS := optional LOCAL_SDK_VERSION := 14 -LOCAL_NDK_STL_VARIANT := stlport_static +LOCAL_NDK_STL_VARIANT := gnustl_static LOCAL_LDFLAGS += -ldl include $(BUILD_SHARED_LIBRARY) diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk index 82237dc24..2ee4caa42 100644 --- a/native/jni/NativeFileList.mk +++ b/native/jni/NativeFileList.mk @@ -32,7 +32,6 @@ LATIN_IME_CORE_SRC_FILES := \ digraph_utils.cpp \ error_type_utils.cpp \ multi_bigram_map.cpp \ - suggestions_output_utils.cpp \ word_property.cpp) \ $(addprefix suggest/core/layout/, \ additional_proximity_chars.cpp \ @@ -42,6 +41,9 @@ LATIN_IME_CORE_SRC_FILES := \ proximity_info_state_utils.cpp) \ suggest/core/policy/weighting.cpp \ suggest/core/session/dic_traverse_session.cpp \ + $(addprefix suggest/core/result/, \ + suggestion_results.cpp \ + suggestions_output_utils.cpp) \ $(addprefix suggest/policyimpl/dictionary/, \ header/header_policy.cpp \ header/header_read_write_utils.cpp \ diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 7a7816d5a..eec354abc 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -25,6 +25,7 @@ #include "jni_common.h" #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/dictionary/word_property.h" +#include "suggest/core/result/suggestion_results.h" #include "suggest/core/suggest_options.h" #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h" #include "utils/char_utils.h" @@ -46,15 +47,16 @@ 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::StructurePolicyPtr dictionaryStructureWithBufferPolicy = + DictionaryStructureWithBufferPolicy::StructurePolicyPtr dictionaryStructureWithBufferPolicy( DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy( sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize), - isUpdatable == JNI_TRUE); - if (!dictionaryStructureWithBufferPolicy.get()) { + isUpdatable == JNI_TRUE)); + if (!dictionaryStructureWithBufferPolicy) { return 0; } - Dictionary *const dictionary = new Dictionary(env, dictionaryStructureWithBufferPolicy); + Dictionary *const dictionary = + new Dictionary(env, std::move(dictionaryStructureWithBufferPolicy)); PROF_END(66); PROF_CLOSE; return reinterpret_cast<jlong>(dictionary); @@ -138,15 +140,20 @@ static int latinime_BinaryDictionary_getFormatVersion(JNIEnv *env, jclass clazz, return headerPolicy->getFormatVersionNumber(); } -static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict, +static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict, jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions, - jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray, - jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray, - jintArray outputAutoCommitFirstWordConfidenceArray) { + jintArray prevWordCodePointsForBigrams, jintArray outSuggestionCount, + jintArray outCodePointsArray, jintArray outScoresArray, jintArray outSpaceIndicesArray, + jintArray outTypesArray, jintArray outAutoCommitFirstWordConfidenceArray) { Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); - if (!dictionary) return 0; + // Assign 0 to outSuggestionCount here in case of returning earlier in this method. + int count = 0; + env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count); + if (!dictionary) { + return; + } ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo); DicTraverseSession *traverseSession = reinterpret_cast<DicTraverseSession *>(dicTraverseSession); @@ -161,7 +168,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j const jsize prevWordCodePointsLength = prevWordCodePointsForBigrams ? env->GetArrayLength(prevWordCodePointsForBigrams) : 0; int prevWordCodePointsInternal[prevWordCodePointsLength]; - int *prevWordCodePoints = 0; + int *prevWordCodePoints = nullptr; env->GetIntArrayRegion(xCoordinatesArray, 0, inputSize, xCoordinates); env->GetIntArrayRegion(yCoordinatesArray, 0, inputSize, yCoordinates); env->GetIntArrayRegion(timesArray, 0, inputSize, times); @@ -180,26 +187,26 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j // Output values /* By the way, let's check the output array length here to make sure */ - const jsize outputCodePointsLength = env->GetArrayLength(outputCodePointsArray); + const jsize outputCodePointsLength = env->GetArrayLength(outCodePointsArray); if (outputCodePointsLength != (MAX_WORD_LENGTH * MAX_RESULTS)) { AKLOGE("Invalid outputCodePointsLength: %d", outputCodePointsLength); ASSERT(false); - return 0; + return; } - const jsize scoresLength = env->GetArrayLength(scoresArray); + const jsize scoresLength = env->GetArrayLength(outScoresArray); if (scoresLength != MAX_RESULTS) { AKLOGE("Invalid scoresLength: %d", scoresLength); ASSERT(false); - return 0; + return; } int outputCodePoints[outputCodePointsLength]; int scores[scoresLength]; - const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray); + const jsize spaceIndicesLength = env->GetArrayLength(outSpaceIndicesArray); int spaceIndices[spaceIndicesLength]; - const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); + const jsize outputTypesLength = env->GetArrayLength(outTypesArray); int outputTypes[outputTypesLength]; const jsize outputAutoCommitFirstWordConfidenceLength = - env->GetArrayLength(outputAutoCommitFirstWordConfidenceArray); + env->GetArrayLength(outAutoCommitFirstWordConfidenceArray); // We only use the first result, as obviously we will only ever autocommit the first one ASSERT(outputAutoCommitFirstWordConfidenceLength == 1); int outputAutoCommitFirstWordConfidence[outputAutoCommitFirstWordConfidenceLength]; @@ -209,26 +216,30 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j memset(outputTypes, 0, sizeof(outputTypes)); memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence)); - int count; if (givenSuggestOptions.isGesture() || inputSize > 0) { + // TODO: Use SuggestionResults to return suggestions. count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints, prevWordCodePointsLength, commitPoint, &givenSuggestOptions, outputCodePoints, scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); } else { - count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength, - outputCodePoints, scores, outputTypes); + SuggestionResults suggestionResults(MAX_RESULTS); + dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength, + &suggestionResults); + suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray, + outScoresArray, outSpaceIndicesArray, outTypesArray, + outAutoCommitFirstWordConfidenceArray); + return; } // Copy back the output values - env->SetIntArrayRegion(outputCodePointsArray, 0, outputCodePointsLength, outputCodePoints); - env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores); - env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices); - env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes); - env->SetIntArrayRegion(outputAutoCommitFirstWordConfidenceArray, 0, + env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count); + env->SetIntArrayRegion(outCodePointsArray, 0, outputCodePointsLength, outputCodePoints); + env->SetIntArrayRegion(outScoresArray, 0, scoresLength, scores); + env->SetIntArrayRegion(outSpaceIndicesArray, 0, spaceIndicesLength, spaceIndices); + env->SetIntArrayRegion(outTypesArray, 0, outputTypesLength, outputTypes); + env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0, outputAutoCommitFirstWordConfidenceLength, outputAutoCommitFirstWordConfidence); - - return count; } static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict, @@ -495,7 +506,7 @@ static const JNINativeMethod sMethods[] = { }, { const_cast<char *>("getSuggestionsNative"), - const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"), + const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I[I)V"), reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions) }, { diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index 22cc4c02b..4c57af0ba 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -344,21 +344,18 @@ static inline void prof_out(void) { #define MAX_POINTER_COUNT 1 #define MAX_POINTER_COUNT_G 2 -template<typename T> AK_FORCE_INLINE const T &min(const T &a, const T &b) { return a < b ? a : b; } -template<typename T> AK_FORCE_INLINE const T &max(const T &a, const T &b) { return a > b ? a : b; } - // DEBUG #define INPUTLENGTH_FOR_DEBUG (-1) #define MIN_OUTPUT_INDEX_FOR_DEBUG (-1) #define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \ - TypeName() + TypeName() = delete #define DISALLOW_COPY_CONSTRUCTOR(TypeName) \ - TypeName(const TypeName&) + TypeName(const TypeName&) = delete #define DISALLOW_ASSIGNMENT_OPERATOR(TypeName) \ - void operator=(const TypeName&) + void operator=(const TypeName&) = delete #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ DISALLOW_COPY_CONSTRUCTOR(TypeName); \ diff --git a/native/jni/src/suggest/core/dicnode/dic_node.cpp b/native/jni/src/suggest/core/dicnode/dic_node.cpp index de088c7d0..73855977e 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node.cpp @@ -25,7 +25,7 @@ DicNode::DicNode(const DicNode &dicNode) #endif mDicNodeProperties(dicNode.mDicNodeProperties), mDicNodeState(dicNode.mDicNodeState), mIsCachedForNextSuggestion(dicNode.mIsCachedForNextSuggestion), mIsUsed(dicNode.mIsUsed), - mReleaseListener(0) { + mReleaseListener(nullptr) { /* empty */ } diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 558667eb0..b812f8ff4 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -93,7 +93,7 @@ class DicNode { mProfiler(), #endif mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false), - mIsUsed(false), mReleaseListener(0) {} + mIsUsed(false), mReleaseListener(nullptr) {} DicNode(const DicNode &dicNode); DicNode &operator=(const DicNode &dicNode); diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h index 7461f0cc6..1f02731a5 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h @@ -17,6 +17,7 @@ #ifndef LATINIME_DIC_NODE_PRIORITY_QUEUE_H #define LATINIME_DIC_NODE_PRIORITY_QUEUE_H +#include <algorithm> #include <queue> #include <vector> @@ -49,7 +50,7 @@ class DicNodePriorityQueue : public DicNodeReleaseListener { AK_FORCE_INLINE void setMaxSize(const int maxSize) { ASSERT(maxSize <= mCapacity); - mMaxSize = min(maxSize, mCapacity); + mMaxSize = std::min(maxSize, mCapacity); } AK_FORCE_INLINE void clearAndResizeToCapacity() { 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 71bcab6cb..a6ea68c29 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -16,6 +16,7 @@ #include "suggest/core/dicnode/dic_node_utils.h" +#include <algorithm> #include <cstring> #include "suggest/core/dicnode/dic_node.h" @@ -117,7 +118,7 @@ namespace latinime { } actualLength0 = i + 1; } - actualLength0 = min(actualLength0, MAX_WORD_LENGTH); + actualLength0 = std::min(actualLength0, MAX_WORD_LENGTH); memmove(dest, src0, actualLength0 * sizeof(dest[0])); if (!src1 || length1 == 0) { return actualLength0; @@ -129,7 +130,7 @@ namespace latinime { } actualLength1 = i + 1; } - actualLength1 = min(actualLength1, MAX_WORD_LENGTH - actualLength0); + actualLength1 = std::min(actualLength1, MAX_WORD_LENGTH - actualLength0); memmove(&dest[actualLength0], src1, actualLength1 * sizeof(dest[0])); return actualLength0 + actualLength1; } diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h index 8493b6a8b..c31c056f5 100644 --- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h +++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h @@ -17,6 +17,7 @@ #ifndef LATINIME_DIC_NODES_CACHE_H #define LATINIME_DIC_NODES_CACHE_H +#include <algorithm> #include <stdint.h> #include "defines.h" @@ -51,7 +52,7 @@ class DicNodesCache { // We want to use the max capacity for the current active dic node queue. mActiveDicNodes->clearAndResizeToCapacity(); // nextActiveSize is used to limit the next iteration's active dic node size. - const int nextActiveSizeFittingToTheCapacity = min(nextActiveSize, getCacheCapacity()); + const int nextActiveSizeFittingToTheCapacity = std::min(nextActiveSize, getCacheCapacity()); mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity); mTerminalDicNodes->clearAndResize(terminalSize); // We want to use the max capacity for the cached dic nodes that will be used for the diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h index fc6851099..abafc0edf 100644 --- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h +++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h @@ -17,6 +17,7 @@ #ifndef LATINIME_DIC_NODE_STATE_OUTPUT_H #define LATINIME_DIC_NODE_STATE_OUTPUT_H +#include <algorithm> #include <cstring> // for memmove() #include <stdint.h> @@ -49,7 +50,8 @@ class DicNodeStateOutput { void addMergedNodeCodePoints(const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) { if (mergedNodeCodePoints) { - const int additionalCodePointCount = min(static_cast<int>(mergedNodeCodePointCount), + const int additionalCodePointCount = std::min( + static_cast<int>(mergedNodeCodePointCount), MAX_WORD_LENGTH - mOutputtedCodePointCount); memmove(&mCodePointsBuf[mOutputtedCodePointCount], mergedNodeCodePoints, additionalCodePointCount * sizeof(mCodePointsBuf[0])); 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 e7108d976..7868f7853 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 @@ -17,6 +17,7 @@ #ifndef LATINIME_DIC_NODE_STATE_PREVWORD_H #define LATINIME_DIC_NODE_STATE_PREVWORD_H +#include <algorithm> #include <cstring> // for memset() and memmove() #include <stdint.h> @@ -69,7 +70,7 @@ class DicNodeStatePrevWord { const int prevWordNodePos, const int *const src0, const int16_t length0, const int *const src1, const int16_t length1, const int prevWordSecondWordFirstInputIndex, const int lastInputIndex) { - mPrevWordCount = min(prevWordCount, static_cast<int16_t>(MAX_RESULTS)); + mPrevWordCount = std::min(prevWordCount, static_cast<int16_t>(MAX_RESULTS)); mPrevWordProbability = prevWordProbability; mPrevWordPtNodePos = prevWordNodePos; int twoWordsLen = 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 11c201e52..18b7d736a 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 @@ -17,6 +17,7 @@ #ifndef LATINIME_DIC_NODE_STATE_SCORING_H #define LATINIME_DIC_NODE_STATE_SCORING_H +#include <algorithm> #include <stdint.h> #include "defines.h" @@ -199,7 +200,7 @@ class DicNodeStateScoring { mNormalizedCompoundDistance = mSpatialDistance + mLanguageDistance; } else { mNormalizedCompoundDistance = (mSpatialDistance + mLanguageDistance) - / static_cast<float>(max(1, totalInputIndex)); + / static_cast<float>(std::max(1, totalInputIndex)); } } }; diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp index d0b96b0fe..f793363a8 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp @@ -14,16 +14,18 @@ * limitations under the License. */ -#include <cstring> - #define LOG_TAG "LatinIME: bigram_dictionary.cpp" #include "bigram_dictionary.h" +#include <algorithm> +#include <cstring> + #include "defines.h" #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/core/result/suggestion_results.h" #include "utils/char_utils.h" namespace latinime { @@ -39,71 +41,13 @@ BigramDictionary::BigramDictionary( BigramDictionary::~BigramDictionary() { } -void BigramDictionary::addWordBigram(int *word, int length, int probability, int *bigramProbability, - int *bigramCodePoints, int *outputTypes) const { - if (length >= MAX_WORD_LENGTH) { - length = MAX_WORD_LENGTH - 1; - } - word[length] = 0; - if (DEBUG_DICT_FULL) { -#ifdef FLAG_DBG - char s[length + 1]; - for (int i = 0; i <= length; i++) s[i] = static_cast<char>(word[i]); - AKLOGI("Bigram: Found word = %s, freq = %d :", s, probability); -#endif - } - - // Find the right insertion point - int insertAt = 0; - while (insertAt < MAX_RESULTS) { - if (probability > bigramProbability[insertAt] || (bigramProbability[insertAt] == probability - && length < CharUtils::getCodePointCount(MAX_WORD_LENGTH, - bigramCodePoints + insertAt * MAX_WORD_LENGTH))) { - break; - } - insertAt++; - } - if (DEBUG_DICT_FULL) { - AKLOGI("Bigram: InsertAt -> %d MAX_RESULTS: %d", insertAt, MAX_RESULTS); - } - if (insertAt >= MAX_RESULTS) { - return; - } - // Shift result buffers to insert the new entry. - memmove(bigramProbability + (insertAt + 1), bigramProbability + insertAt, - (MAX_RESULTS - insertAt - 1) * sizeof(bigramProbability[0])); - memmove(outputTypes + (insertAt + 1), outputTypes + insertAt, - (MAX_RESULTS - insertAt - 1) * sizeof(outputTypes[0])); - memmove(bigramCodePoints + (insertAt + 1) * MAX_WORD_LENGTH, - bigramCodePoints + insertAt * MAX_WORD_LENGTH, - (MAX_RESULTS - insertAt - 1) * sizeof(bigramCodePoints[0]) * MAX_WORD_LENGTH); - // Put the result. - bigramProbability[insertAt] = probability; - outputTypes[insertAt] = Dictionary::KIND_PREDICTION; - int *dest = bigramCodePoints + insertAt * MAX_WORD_LENGTH; - while (length--) { - *dest++ = *word++; - } - *dest = 0; // NULL terminate - if (DEBUG_DICT_FULL) { - AKLOGI("Bigram: Added word at %d", insertAt); - } -} - /* Parameters : * prevWord: the word before, the one for which we need to look up bigrams. * prevWordLength: its length. - * outBigramCodePoints: an array for output, at the same format as outwords for getSuggestions. - * outBigramProbability: an array to output frequencies. - * outputTypes: an array to output types. - * This method returns the number of bigrams this word has, for backward compatibility. + * outSuggestionResults: SuggestionResults to put the predictions. */ -int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength, - int *const outBigramCodePoints, int *const outBigramProbability, - int *const outputTypes) const { - // TODO: remove unused arguments, and refrain from storing stuff in members of this class - // TODO: have "in" arguments before "out" ones, and make out args explicit in the name - +void BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength, + SuggestionResults *const outSuggestionResults) const { int pos = getBigramListPositionForWord(prevWord, prevWordLength, false /* forceLowerCaseSearch */); // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams @@ -113,11 +57,10 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng true /* forceLowerCaseSearch */); } // If still no bigrams, we really don't have them! - if (NOT_A_DICT_POS == pos) return 0; + if (NOT_A_DICT_POS == pos) return; - int bigramCount = 0; int unigramProbability = 0; - int bigramBuffer[MAX_WORD_LENGTH]; + int bigramCodePoints[MAX_WORD_LENGTH]; BinaryDictionaryBigramsIterator bigramsIt( mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos); while (bigramsIt.hasNext()) { @@ -127,7 +70,7 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng } const int codePointCount = mDictionaryStructurePolicy-> getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(), - MAX_WORD_LENGTH, bigramBuffer, &unigramProbability); + MAX_WORD_LENGTH, bigramCodePoints, &unigramProbability); if (codePointCount <= 0) { continue; } @@ -138,11 +81,8 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng // here, but it can't get too bad. const int probability = mDictionaryStructurePolicy->getProbability( unigramProbability, bigramsIt.getProbability()); - addWordBigram(bigramBuffer, codePointCount, probability, outBigramProbability, - outBigramCodePoints, outputTypes); - ++bigramCount; + outSuggestionResults->addPrediction(bigramCodePoints, codePointCount, probability); } - return min(bigramCount, MAX_RESULTS); } // Returns a pointer to the start of the bigram list. diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h index 8af7ee75d..12aaf20d3 100644 --- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h +++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h @@ -22,21 +22,20 @@ namespace latinime { class DictionaryStructureWithBufferPolicy; +class SuggestionResults; class BigramDictionary { public: BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy); - int getPredictions(const int *word, int length, int *outBigramCodePoints, - int *outBigramProbability, int *outputTypes) const; + void getPredictions(const int *word, int length, + SuggestionResults *const outSuggestionResults) const; int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const; ~BigramDictionary(); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramDictionary); - void addWordBigram(int *word, int length, int probability, int *bigramProbability, - int *bigramCodePoints, int *outputTypes) const; int getBigramListPositionForWord(const int *prevWord, const int prevWordLength, const bool forceLowerCaseSearch) const; diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index 035232f7a..ffa96e167 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -34,9 +34,9 @@ namespace latinime { const int Dictionary::HEADER_ATTRIBUTE_BUFFER_SIZE = 32; -Dictionary::Dictionary(JNIEnv *env, const DictionaryStructureWithBufferPolicy::StructurePolicyPtr - &dictionaryStructureWithBufferPolicy) - : mDictionaryStructureWithBufferPolicy(dictionaryStructureWithBufferPolicy), +Dictionary::Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr + dictionaryStructureWithBufferPolicy) + : mDictionaryStructureWithBufferPolicy(std::move(dictionaryStructureWithBufferPolicy)), mBigramDictionary(new BigramDictionary(mDictionaryStructureWithBufferPolicy.get())), mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())), mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) { @@ -53,7 +53,7 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession if (suggestOptions->isGesture()) { DicTraverseSession::initSessionInstance( traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); - result = mGestureSuggest.get()->getSuggestions(proximityInfo, traverseSession, xcoordinates, + result = mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, outputScores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); if (DEBUG_DICT) { @@ -63,7 +63,7 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } else { DicTraverseSession::initSessionInstance( traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); - result = mTypingSuggest.get()->getSuggestions(proximityInfo, traverseSession, xcoordinates, + result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, outputScores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); @@ -74,12 +74,11 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } } -int Dictionary::getBigrams(const int *word, int length, int *outWords, int *outputScores, - int *outputTypes) const { +void Dictionary::getPredictions(const int *word, int length, + SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - if (length <= 0) return 0; - return mBigramDictionary.get()->getPredictions(word, length, outWords, outputScores, - outputTypes); + if (length <= 0) return; + mBigramDictionary->getPredictions(word, length, outSuggestionResults); } int Dictionary::getProbability(const int *word, int length) const { @@ -95,7 +94,7 @@ int Dictionary::getProbability(const int *word, int length) const { int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1, int length1) const { TimeKeeper::setCurrentTime(); - return mBigramDictionary.get()->getBigramProbability(word0, length0, word1, length1); + return mBigramDictionary->getBigramProbability(word0, length0, word1, length1); } void Dictionary::addUnigramWord(const int *const word, const int length, const int probability, @@ -103,7 +102,7 @@ void Dictionary::addUnigramWord(const int *const word, const int length, const i const int shortcutProbability, const bool isNotAWord, const bool isBlacklisted, const int timestamp) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy.get()->addUnigramWord(word, length, probability, + mDictionaryStructureWithBufferPolicy->addUnigramWord(word, length, probability, shortcutTargetCodePoints, shortcutLength, shortcutProbability, isNotAWord, isBlacklisted, timestamp); } @@ -111,48 +110,48 @@ void Dictionary::addUnigramWord(const int *const word, const int length, const i void Dictionary::addBigramWords(const int *const word0, const int length0, const int *const word1, const int length1, const int probability, const int timestamp) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy.get()->addBigramWords(word0, length0, word1, length1, + mDictionaryStructureWithBufferPolicy->addBigramWords(word0, length0, word1, length1, probability, timestamp); } void Dictionary::removeBigramWords(const int *const word0, const int length0, const int *const word1, const int length1) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy.get()->removeBigramWords(word0, length0, word1, length1); + mDictionaryStructureWithBufferPolicy->removeBigramWords(word0, length0, word1, length1); } void Dictionary::flush(const char *const filePath) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy.get()->flush(filePath); + mDictionaryStructureWithBufferPolicy->flush(filePath); } void Dictionary::flushWithGC(const char *const filePath) { TimeKeeper::setCurrentTime(); - mDictionaryStructureWithBufferPolicy.get()->flushWithGC(filePath); + mDictionaryStructureWithBufferPolicy->flushWithGC(filePath); } bool Dictionary::needsToRunGC(const bool mindsBlockByGC) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy.get()->needsToRunGC(mindsBlockByGC); + return mDictionaryStructureWithBufferPolicy->needsToRunGC(mindsBlockByGC); } void Dictionary::getProperty(const char *const query, const int queryLength, char *const outResult, const int maxResultLength) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy.get()->getProperty(query, queryLength, outResult, + return mDictionaryStructureWithBufferPolicy->getProperty(query, queryLength, outResult, maxResultLength); } const WordProperty Dictionary::getWordProperty(const int *const codePoints, const int codePointCount) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy.get()->getWordProperty( + return mDictionaryStructureWithBufferPolicy->getWordProperty( codePoints, codePointCount); } int Dictionary::getNextWordAndNextToken(const int token, int *const outCodePoints) { TimeKeeper::setCurrentTime(); - return mDictionaryStructureWithBufferPolicy.get()->getNextWordAndNextToken( + return mDictionaryStructureWithBufferPolicy->getNextWordAndNextToken( token, outCodePoints); } diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index c58be8475..2dea9fff8 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -18,6 +18,7 @@ #define LATINIME_DICTIONARY_H #include <stdint.h> +#include <memory> #include "defines.h" #include "jni.h" @@ -26,13 +27,13 @@ #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 DictionaryStructureWithBufferPolicy; class DicTraverseSession; class ProximityInfo; +class SuggestionResults; class SuggestOptions; class WordProperty; @@ -58,8 +59,8 @@ class Dictionary { static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; static const int KIND_FLAG_EXACT_MATCH = 0x40000000; - Dictionary(JNIEnv *env, const DictionaryStructureWithBufferPolicy::StructurePolicyPtr - &dictionaryStructureWithBufferPolicy); + Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr + dictionaryStructureWithBufferPolicy); int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, @@ -67,8 +68,8 @@ class Dictionary { const SuggestOptions *const suggestOptions, int *outWords, int *outputScores, int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const; - int getBigrams(const int *word, int length, int *outWords, int *outputScores, - int *outputTypes) const; + void getPredictions(const int *word, int length, + SuggestionResults *const outSuggestionResults) const; int getProbability(const int *word, int length) const; @@ -108,8 +109,8 @@ class Dictionary { private: DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary); - typedef ExclusiveOwnershipPointer<BigramDictionary> BigramDictionaryPtr; - typedef ExclusiveOwnershipPointer<SuggestInterface> SuggestInterfacePtr; + typedef std::unique_ptr<BigramDictionary> BigramDictionaryPtr; + typedef std::unique_ptr<SuggestInterface> SuggestInterfacePtr; static const int HEADER_ATTRIBUTE_BUFFER_SIZE; diff --git a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp index 5f9b8f3e2..bb2ce5012 100644 --- a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp @@ -84,7 +84,7 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = } /** - * Returns the digraph for the input composite glyph codepoint, or 0 if none exists. + * Returns the digraph for the input composite glyph codepoint, or nullptr if none exists. * compositeGlyphCodePoint: the method returns the digraph corresponding to this codepoint. */ /* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForCodePoint( @@ -96,17 +96,17 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = return digraph; } } - return 0; + return nullptr; } /** - * Returns the digraph for the input composite glyph codepoint, or 0 if none exists. + * Returns the digraph for the input composite glyph codepoint, or nullptr if none exists. * digraphType: the type of digraphs supported. * compositeGlyphCodePoint: the method returns the digraph corresponding to this codepoint. */ /* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForDigraphTypeAndCodePoint( const DigraphUtils::DigraphType digraphType, const int compositeGlyphCodePoint) { - const DigraphUtils::digraph_t *digraphs = 0; + const DigraphUtils::digraph_t *digraphs = nullptr; const int compositeGlyphLowerCodePoint = CharUtils::toLowerCase(compositeGlyphCodePoint); const int digraphsSize = DigraphUtils::getAllDigraphsForDigraphTypeAndReturnSize(digraphType, &digraphs); @@ -115,7 +115,7 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = return &digraphs[i]; } } - return 0; + return nullptr; } } // namespace latinime diff --git a/native/jni/src/suggest/core/layout/proximity_info.cpp b/native/jni/src/suggest/core/layout/proximity_info.cpp index ee8e59ef9..8b3ae4db8 100644 --- a/native/jni/src/suggest/core/layout/proximity_info.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info.cpp @@ -18,6 +18,7 @@ #include "suggest/core/layout/proximity_info.h" +#include <algorithm> #include <cstring> #include <cmath> @@ -63,7 +64,7 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, static_cast<float>(mostCommonKeyWidth))), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), - KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), + KEY_COUNT(std::min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), KEYBOARD_HYPOTENUSE(hypotf(KEYBOARD_WIDTH, KEYBOARD_HEIGHT)), HAS_TOUCH_POSITION_CORRECTION_DATA(keyCount > 0 && keyXCoordinates && keyYCoordinates 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 40c3448ef..2919904e5 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp @@ -18,6 +18,7 @@ #include "suggest/core/layout/proximity_info_state.h" +#include <algorithm> #include <cstring> // for memset() and memmove() #include <sstream> // for debug prints #include <vector> @@ -171,7 +172,7 @@ float ProximityInfoState::getPointToKeyLength( const int keyId = mProximityInfo->getKeyIndexOf(codePoint); if (keyId != NOT_AN_INDEX) { const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; - return min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength); + return std::min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength); } if (CharUtils::isIntentionalOmissionCodePoint(codePoint)) { return 0.0f; 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 e941e43d8..9abd69a66 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state.h @@ -43,7 +43,7 @@ class ProximityInfoState { // Defined here // ///////////////////////////////////////// AK_FORCE_INLINE ProximityInfoState() - : mProximityInfo(0), mMaxPointToKeyLength(0.0f), mAverageSpeed(0.0f), + : mProximityInfo(nullptr), mMaxPointToKeyLength(0.0f), mAverageSpeed(0.0f), mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0), mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0), mIsContinuousSuggestionPossible(false), mHasBeenUpdatedByGeometricInput(false), diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp index bc4ca8e9e..867f59843 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp @@ -16,6 +16,7 @@ #include "suggest/core/layout/proximity_info_state_utils.h" +#include <algorithm> #include <cmath> #include <cstring> // for memset() #include <sstream> // for debug prints @@ -240,7 +241,7 @@ namespace latinime { // Calculate velocity by using distances and durations of // ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and // backward. - const int forwardNumPoints = min(inputSize - 1, + const int forwardNumPoints = std::min(inputSize - 1, index + ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION); for (int j = index; j < forwardNumPoints; ++j) { if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) { @@ -250,7 +251,7 @@ namespace latinime { xCoordinates[j + 1], yCoordinates[j + 1]); duration += times[j + 1] - times[j]; } - const int backwardNumPoints = max(0, + const int backwardNumPoints = std::max(0, index - ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION); for (int j = index - 1; j >= backwardNumPoints; --j) { if (i > 0 && j < (*sampledInputIndice)[i - 1]) { @@ -272,7 +273,7 @@ namespace latinime { // Direction calculation. sampledDirections->resize(sampledInputSize - 1); - for (int i = max(0, lastSavedInputSize - 1); i < sampledInputSize - 1; ++i) { + for (int i = std::max(0, lastSavedInputSize - 1); i < sampledInputSize - 1; ++i) { (*sampledDirections)[i] = getDirection(sampledInputXs, sampledInputYs, i, i + 1); } return averageSpeed; @@ -609,7 +610,7 @@ namespace latinime { const int inputIndex, const int keyId) { if (keyId != NOT_AN_INDEX) { const int index = inputIndex * keyCount + keyId; - return min((*sampledNormalizedSquaredLengthCache)[index], maxPointToKeyLength); + return std::min((*sampledNormalizedSquaredLengthCache)[index], maxPointToKeyLength); } // If the char is not a key on the keyboard then return the max length. return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); @@ -650,13 +651,13 @@ namespace latinime { } if (i == 0) { - skipProbability *= min(1.0f, + skipProbability *= std::min(1.0f, nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT + ProximityInfoParams::NEAREST_DISTANCE_BIAS); // Promote the first point skipProbability *= ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY; } else if (i == sampledInputSize - 1) { - skipProbability *= min(1.0f, + skipProbability *= std::min(1.0f, nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST + ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST); // Promote the last point @@ -667,17 +668,17 @@ namespace latinime { && speedRate < (*sampledSpeedRates)[i + 1] - ProximityInfoParams::SPEED_MARGIN) { if (currentAngle < ProximityInfoParams::CORNER_ANGLE_THRESHOLD) { - skipProbability *= min(1.0f, speedRate + skipProbability *= std::min(1.0f, speedRate * ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY); } else { // If the angle is small enough, we promote this point more. (e.g. pit vs put) - skipProbability *= min(1.0f, + skipProbability *= std::min(1.0f, speedRate * ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY + ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY); } } - skipProbability *= min(1.0f, + skipProbability *= std::min(1.0f, speedRate * nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT + ProximityInfoParams::NEAREST_DISTANCE_BIAS); @@ -707,10 +708,10 @@ namespace latinime { // (1.0f - skipProbability). const float inputCharProbability = 1.0f - skipProbability; - const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F + const float speedxAngleRate = std::min(speedRate * currentAngle / M_PI_F * ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DEVIATION, ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDARD_DEVIATION); - const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance + const float speedxNearestKeyDistanceRate = std::min(speedRate * nearestKeyDistance * ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DEVIATION, ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDARD_DEVIATION); const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate @@ -827,7 +828,7 @@ namespace latinime { // Decrease key probabilities of points which don't have the highest probability of that key // among nearby points. Probabilities of the first point and the last point are not suppressed. - for (int i = max(start, 1); i < sampledInputSize; ++i) { + for (int i = std::max(start, 1); i < sampledInputSize; ++i) { for (int j = i + 1; j < sampledInputSize; ++j) { if (!suppressCharProbabilities( mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j, @@ -835,7 +836,7 @@ namespace latinime { break; } } - for (int j = i - 1; j >= max(start, 0); --j) { + for (int j = i - 1; j >= std::max(start, 0); --j) { if (!suppressCharProbabilities( mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j, charProbabilities)) { @@ -878,7 +879,7 @@ namespace latinime { if (i >= lastSavedInputSize) { (*sampledSearchKeySets)[i].reset(); } - for (int j = max(i, lastSavedInputSize); j < sampledInputSize; ++j) { + for (int j = std::max(i, lastSavedInputSize); j < sampledInputSize; ++j) { // TODO: Investigate if this is required. This may not fail. if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) { break; @@ -929,7 +930,7 @@ namespace latinime { (*charProbabilities)[index0][NOT_AN_INDEX] += suppression; // Add the probability of the same key nearby index1 - const float probabilityGain = min(suppression + const float probabilityGain = std::min(suppression * ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN, (*charProbabilities)[index1][NOT_AN_INDEX] * ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN); diff --git a/native/jni/src/suggest/core/layout/proximity_info_utils.h b/native/jni/src/suggest/core/layout/proximity_info_utils.h index 0e28560fc..310bbdb62 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_utils.h @@ -100,6 +100,10 @@ class ProximityInfoUtils { const float dotProduct = ray1x * ray2x + ray1y * ray2y; const float lineLengthSqr = GeometryUtils::SQUARE_FLOAT(ray2x) + GeometryUtils::SQUARE_FLOAT(ray2y); + if (lineLengthSqr <= 0.0f) { + // Return point to the point distance. + return getSquaredDistanceFloat(x, y, x1, y1); + } const float projectionLengthSqr = dotProduct / lineLengthSqr; float projectionX; @@ -125,7 +129,7 @@ class ProximityInfoUtils { struct NormalDistribution { public: NormalDistribution(const float u, const float sigma) - : mU(u), mSigma(sigma), + : mU(u), mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F * GeometryUtils::SQUARE_FLOAT(sigma))), mPreComputedExponentPart(-1.0f / (2.0f * GeometryUtils::SQUARE_FLOAT(sigma))) {} @@ -139,7 +143,6 @@ class ProximityInfoUtils { private: DISALLOW_IMPLICIT_CONSTRUCTORS(NormalDistribution); const float mU; // mean value - const float mSigma; // standard deviation const float mPreComputedNonExpPart; // = 1 / sqrt(2 * PI * sigma^2) const float mPreComputedExponentPart; // = -1 / (2 * sigma^2) }; // struct NormalDistribution @@ -167,6 +170,12 @@ class ProximityInfoUtils { const int mostCommonKeyWidthSquare = mostCommonKeyWidth * mostCommonKeyWidth; int insertPos = 0; proximities[insertPos++] = primaryKey; + if (x == NOT_A_COORDINATE || y == NOT_A_COORDINATE) { + for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { + proximities[i] = NOT_A_CODE_POINT; + } + return; + } const int startIndex = getStartIndexFromCoordinates(x, y, cellHeight, cellWidth, gridWidth); if (startIndex >= 0) { for (int i = 0; i < MAX_PROXIMITY_CHARS_SIZE; ++i) { diff --git a/native/jni/src/suggest/core/layout/touch_position_correction_utils.h b/native/jni/src/suggest/core/layout/touch_position_correction_utils.h index 9130e87d3..14074c13d 100644 --- a/native/jni/src/suggest/core/layout/touch_position_correction_utils.h +++ b/native/jni/src/suggest/core/layout/touch_position_correction_utils.h @@ -17,6 +17,8 @@ #ifndef LATINIME_TOUCH_POSITION_CORRECTION_UTILS_H #define LATINIME_TOUCH_POSITION_CORRECTION_UTILS_H +#include <algorithm> + #include "defines.h" #include "suggest/core/layout/proximity_info_params.h" @@ -34,7 +36,7 @@ class TouchPositionCorrectionUtils { static const float R2 = 1.0f; const float x = normalizedSquaredDistance; if (!isTouchPositionCorrectionEnabled) { - return min(C, x); + return std::min(C, x); } // factor is a piecewise linear function like: 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 38e8ff183..b6dc7d006 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 @@ -17,9 +17,10 @@ #ifndef LATINIME_DICTIONARY_STRUCTURE_POLICY_H #define LATINIME_DICTIONARY_STRUCTURE_POLICY_H +#include <memory> + #include "defines.h" #include "suggest/core/dictionary/word_property.h" -#include "utils/exclusive_ownership_pointer.h" namespace latinime { @@ -35,7 +36,7 @@ class DictionaryShortcutsStructurePolicy; */ class DictionaryStructureWithBufferPolicy { public: - typedef ExclusiveOwnershipPointer<DictionaryStructureWithBufferPolicy> StructurePolicyPtr; + typedef std::unique_ptr<DictionaryStructureWithBufferPolicy> StructurePolicyPtr; virtual ~DictionaryStructureWithBufferPolicy() {} diff --git a/native/jni/src/suggest/core/result/suggested_word.h b/native/jni/src/suggest/core/result/suggested_word.h new file mode 100644 index 000000000..48b29d6a6 --- /dev/null +++ b/native/jni/src/suggest/core/result/suggested_word.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_SUGGESTED_WORD_H +#define LATINIME_SUGGESTED_WORD_H + +#include <vector> + +#include "defines.h" +#include "suggest/core/dictionary/dictionary.h" + +namespace latinime { + +class SuggestedWord { + public: + class Comparator { + public: + bool operator()(const SuggestedWord &left, const SuggestedWord &right) { + if (left.getScore() != right.getScore()) { + return left.getScore() < right.getScore(); + } + return left.getCodePointCount() > right.getCodePointCount(); + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(Comparator); + }; + + SuggestedWord(const int *const codePoints, const int codePointCount, + const int score, const int type, const int indexToPartialCommit, + const int autoCommitFirstWordConfidence) + : mCodePoints(codePoints, codePoints + codePointCount), mScore(score), + mType(type), mIndexToPartialCommit(indexToPartialCommit), + mAutoCommitFirstWordConfidence(autoCommitFirstWordConfidence) {} + + const int *getCodePoint() const { + return &mCodePoints.at(0); + } + + int getCodePointCount() const { + return mCodePoints.size(); + } + + int getScore() const { + return mScore; + } + + int getType() const { + return mType; + } + + int getIndexToPartialCommit() const { + return mIndexToPartialCommit; + } + + int getAutoCommitFirstWordConfidence() const { + return mAutoCommitFirstWordConfidence; + } + + private: + DISALLOW_DEFAULT_CONSTRUCTOR(SuggestedWord); + + std::vector<int> mCodePoints; + int mScore; + int mType; + int mIndexToPartialCommit; + int mAutoCommitFirstWordConfidence; +}; +} // namespace latinime +#endif /* LATINIME_SUGGESTED_WORD_H */ diff --git a/native/jni/src/suggest/core/result/suggestion_results.cpp b/native/jni/src/suggest/core/result/suggestion_results.cpp new file mode 100644 index 000000000..2be757d83 --- /dev/null +++ b/native/jni/src/suggest/core/result/suggestion_results.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/core/result/suggestion_results.h" + +namespace latinime { + +void SuggestionResults::outputSuggestions(JNIEnv *env, jintArray outSuggestionCount, + jintArray outputCodePointsArray, jintArray outScoresArray, jintArray outSpaceIndicesArray, + jintArray outTypesArray, jintArray outAutoCommitFirstWordConfidenceArray) { + int outputIndex = 0; + while (!mSuggestedWords.empty()) { + const SuggestedWord &suggestedWord = mSuggestedWords.top(); + suggestedWord.getCodePointCount(); + const int start = outputIndex * MAX_WORD_LENGTH; + env->SetIntArrayRegion(outputCodePointsArray, start, suggestedWord.getCodePointCount(), + suggestedWord.getCodePoint()); + if (suggestedWord.getCodePointCount() < MAX_WORD_LENGTH) { + const int terminal = 0; + env->SetIntArrayRegion(outputCodePointsArray, start + suggestedWord.getCodePointCount(), + 1 /* len */, &terminal); + } + const int score = suggestedWord.getScore(); + env->SetIntArrayRegion(outScoresArray, outputIndex, 1 /* len */, &score); + const int indexToPartialCommit = suggestedWord.getIndexToPartialCommit(); + env->SetIntArrayRegion(outSpaceIndicesArray, outputIndex, 1 /* len */, + &indexToPartialCommit); + const int type = suggestedWord.getType(); + env->SetIntArrayRegion(outTypesArray, outputIndex, 1 /* len */, &type); + if (mSuggestedWords.size() == 1) { + const int autoCommitFirstWordConfidence = + suggestedWord.getAutoCommitFirstWordConfidence(); + env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0 /* start */, + 1 /* len */, &autoCommitFirstWordConfidence); + } + ++outputIndex; + mSuggestedWords.pop(); + } + env->SetIntArrayRegion(outSuggestionCount, 0 /* start */, 1 /* len */, &outputIndex); +} + +void SuggestionResults::addPrediction(const int *const codePoints, const int codePointCount, + const int probability) { + if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH + || probability == NOT_A_PROBABILITY) { + // Invalid word. + return; + } + // Use probability as a score of the word. + const int score = probability; + if (getSuggestionCount() >= mMaxSuggestionCount) { + const SuggestedWord &mWorstSuggestion = mSuggestedWords.top(); + if (score > mWorstSuggestion.getScore() || (score == mWorstSuggestion.getScore() + && codePointCount < mWorstSuggestion.getCodePointCount())) { + mSuggestedWords.pop(); + } else { + return; + } + } + mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score, + Dictionary::KIND_PREDICTION, NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE)); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/core/result/suggestion_results.h b/native/jni/src/suggest/core/result/suggestion_results.h new file mode 100644 index 000000000..0b841ca19 --- /dev/null +++ b/native/jni/src/suggest/core/result/suggestion_results.h @@ -0,0 +1,53 @@ +/* + * 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_SUGGESTION_RESULTS_H +#define LATINIME_SUGGESTION_RESULTS_H + +#include <queue> +#include <vector> + +#include "defines.h" +#include "jni.h" +#include "suggest/core/result/suggested_word.h" + +namespace latinime { + +class SuggestionResults { + public: + explicit SuggestionResults(const int maxSuggestionCount) + : mMaxSuggestionCount(maxSuggestionCount), mSuggestedWords() {} + + // Returns suggestion count. + void outputSuggestions(JNIEnv *env, jintArray outSuggestionCount, jintArray outCodePointsArray, + jintArray outScoresArray, jintArray outSpaceIndicesArray, jintArray outTypesArray, + jintArray outAutoCommitFirstWordConfidenceArray); + + void addPrediction(const int *const codePoints, const int codePointCount, const int score); + + int getSuggestionCount() const { + return mSuggestedWords.size(); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionResults); + + const int mMaxSuggestionCount; + std::priority_queue< + SuggestedWord, std::vector<SuggestedWord>, SuggestedWord::Comparator> mSuggestedWords; +}; +} // namespace latinime +#endif // LATINIME_SUGGESTION_RESULTS_H diff --git a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp index 07c2e6ea9..19912f2ac 100644 --- a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.cpp +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp @@ -14,7 +14,9 @@ * limitations under the License. */ -#include "suggest/core/dictionary/suggestions_output_utils.h" +#include "suggest/core/result/suggestions_output_utils.h" + +#include <algorithm> #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_utils.h" @@ -36,7 +38,7 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; #if DEBUG_EVALUATE_MOST_PROBABLE_STRING const int terminalSize = 0; #else - const int terminalSize = min(MAX_RESULTS, + const int terminalSize = std::min(MAX_RESULTS, static_cast<int>(traverseSession->getDicTraverseCache()->terminalSize())); #endif DicNode terminals[MAX_RESULTS]; // Avoiding non-POD variable length array @@ -245,12 +247,12 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; // shortcut entry's score == its base entry's score - 1 shortcutScore = finalScore; // Protection against int underflow - shortcutScore = max(S_INT_MIN + 1, shortcutScore) - 1; + shortcutScore = std::max(S_INT_MIN + 1, shortcutScore) - 1; kind = Dictionary::KIND_SHORTCUT; } outputTypes[outputWordIndex] = kind; outputScores[outputWordIndex] = shortcutScore; - outputScores[outputWordIndex] = max(S_INT_MIN + 1, shortcutScore) - 1; + outputScores[outputWordIndex] = std::max(S_INT_MIN + 1, shortcutScore) - 1; const int startIndex2 = outputWordIndex * MAX_WORD_LENGTH; DicNodeUtils::appendTwoWords(0, 0, shortcutTarget, shortcutTargetStringLength, &outputCodePoints[startIndex2]); diff --git a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h index d456a545f..d456a545f 100644 --- a/native/jni/src/suggest/core/dictionary/suggestions_output_utils.h +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h 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 6e4dda44d..b718fb57a 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -59,8 +59,8 @@ class DicTraverseSession { } AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache) - : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(0), - mDictionary(0), mSuggestOptions(0), mDicNodesCache(usesLargeCache), + : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(nullptr), + mDictionary(nullptr), mSuggestOptions(nullptr), mDicNodesCache(usesLargeCache), mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 56acc2dc1..c3b670337 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -21,11 +21,11 @@ #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/dictionary.h" #include "suggest/core/dictionary/digraph_utils.h" -#include "suggest/core/dictionary/suggestions_output_utils.h" #include "suggest/core/layout/proximity_info.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" #include "suggest/core/policy/traversal.h" #include "suggest/core/policy/weighting.h" +#include "suggest/core/result/suggestions_output_utils.h" #include "suggest/core/session/dic_traverse_session.h" namespace latinime { diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h index b1d12ad9a..c42986ad6 100644 --- a/native/jni/src/suggest/core/suggest.h +++ b/native/jni/src/suggest/core/suggest.h @@ -42,9 +42,9 @@ class Weighting; class Suggest : public SuggestInterface { public: AK_FORCE_INLINE Suggest(const SuggestPolicy *const suggestPolicy) - : TRAVERSAL(suggestPolicy ? suggestPolicy->getTraversal() : 0), - SCORING(suggestPolicy ? suggestPolicy->getScoring() : 0), - WEIGHTING(suggestPolicy ? suggestPolicy->getWeighting() : 0) {} + : TRAVERSAL(suggestPolicy ? suggestPolicy->getTraversal() : nullptr), + SCORING(suggestPolicy ? suggestPolicy->getScoring() : nullptr), + WEIGHTING(suggestPolicy ? suggestPolicy->getWeighting() : nullptr) {} AK_FORCE_INLINE virtual ~Suggest() {} int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints, int inputSize, int commitPoint, 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 7c7b05ca8..ecc9fdab1 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp @@ -16,6 +16,8 @@ #include "suggest/policyimpl/dictionary/header/header_policy.h" +#include <algorithm> + namespace latinime { // Note that these are corresponding definitions in Java side in DictionaryHeader. @@ -72,7 +74,7 @@ void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *out outValue[1] = '\0'; return; } - const int terminalIndex = min(static_cast<int>(it->second.size()), outValueSize - 1); + const int terminalIndex = std::min(static_cast<int>(it->second.size()), outValueSize - 1); for (int i = 0; i < terminalIndex; ++i) { outValue[i] = it->second[i]; } 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 index ae863af57..f2fa5b75b 100644 --- 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 @@ -31,8 +31,7 @@ class Ver4ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { public: Ver4ShortcutListPolicy(ShortcutDictContent *const shortcutDictContent, const TerminalPositionLookupTable *const terminalPositionLookupTable) - : mShortcutDictContent(shortcutDictContent), - mTerminalPositionLookupTable(terminalPositionLookupTable) {} + : mShortcutDictContent(shortcutDictContent) {} ~Ver4ShortcutListPolicy() {} @@ -104,7 +103,6 @@ class Ver4ShortcutListPolicy : public DictionaryShortcutsStructurePolicy { 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 index 04f119803..79bcf6fa4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp @@ -41,7 +41,7 @@ namespace latinime { if (isUpdatable) { AKLOGE("One file dictionaries don't support updating. path: %s", path); ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } return newPolicyforFileDict(path, bufOffset, size); } @@ -55,13 +55,13 @@ namespace latinime { getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath); // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of // MmappedBufferPtr if the instance has the responsibility. - MmappedBuffer::MmappedBufferPtr mmappedBuffer = MmappedBuffer::openBuffer(headerFilePath, - isUpdatable); - if (!mmappedBuffer.get()) { - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + MmappedBuffer::MmappedBufferPtr mmappedBuffer( + MmappedBuffer::openBuffer(headerFilePath, isUpdatable)); + if (!mmappedBuffer) { + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } - switch (FormatUtils::detectFormatVersion(mmappedBuffer.get()->getBuffer(), - mmappedBuffer.get()->getBufferSize())) { + switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), + mmappedBuffer->getBufferSize())) { case FormatUtils::VERSION_2: AKLOGE("Given path is a directory but the format is version 2. path: %s", path); break; @@ -72,25 +72,25 @@ namespace latinime { 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); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } - const Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = - Ver4DictBuffers::openVer4DictBuffers(dictPath, mmappedBuffer); - if (!dictBuffers.get() || !dictBuffers.get()->isValid()) { + Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( + Ver4DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer))); + if (!dictBuffers || !dictBuffers->isValid()) { AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", path); ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new Ver4PatriciaTriePolicy(dictBuffers)); + new Ver4PatriciaTriePolicy(std::move(dictBuffers))); } default: AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path); break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr @@ -98,16 +98,16 @@ namespace latinime { 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); + MmappedBuffer::MmappedBufferPtr mmappedBuffer( + MmappedBuffer::openBuffer(path, bufOffset, size, false /* isUpdatable */)); + if (!mmappedBuffer) { + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } - switch (FormatUtils::detectFormatVersion(mmappedBuffer.get()->getBuffer(), - mmappedBuffer.get()->getBufferSize())) { + switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), + mmappedBuffer->getBufferSize())) { case FormatUtils::VERSION_2: return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new PatriciaTriePolicy(mmappedBuffer)); + new PatriciaTriePolicy(std::move(mmappedBuffer))); case FormatUtils::VERSION_4: AKLOGE("Given path is a file but the format is version 4. path: %s", path); break; @@ -116,7 +116,7 @@ namespace latinime { break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(0); + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); } /* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h index 45ab52931..9454ddf33 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h @@ -21,7 +21,6 @@ #include "defines.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" -#include "utils/exclusive_ownership_pointer.h" namespace latinime { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h index 6a2345a05..11a40de55 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h @@ -37,12 +37,11 @@ class DicNodeVector; class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: - 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()), + PatriciaTriePolicy(MmappedBuffer::MmappedBufferPtr mmappedBuffer) + : mMmappedBuffer(std::move(mmappedBuffer)), + mHeaderPolicy(mMmappedBuffer->getBuffer(), FormatUtils::VERSION_2), + mDictRoot(mMmappedBuffer->getBuffer() + mHeaderPolicy.getSize()), + mDictBufferSize(mMmappedBuffer->getBufferSize() - mHeaderPolicy.getSize()), mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot), mPtNodeReader(mDictRoot, mDictBufferSize, &mBigramListPolicy, &mShortcutListPolicy), mPtNodeArrayReader(mDictRoot, mDictBufferSize), 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 index 9064b7e72..215642234 100644 --- 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 @@ -31,14 +31,14 @@ class SingleDictContent : public DictContent { 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, + mExpandableContentBuffer(mMmappedBuffer ? mMmappedBuffer->getBuffer() : nullptr, + mMmappedBuffer ? mMmappedBuffer->getBufferSize() : 0, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mIsValid(mMmappedBuffer.get() != 0) {} + mIsValid(mMmappedBuffer) {} SingleDictContent() - : mMmappedBuffer(0), mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), - mIsValid(true) {} + : mMmappedBuffer(nullptr), + mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mIsValid(true) {} virtual ~SingleDictContent() {} 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 index a82e3f50a..fb6c88eef 100644 --- 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 @@ -38,25 +38,25 @@ class SparseTableDictContent : public DictContent { MmappedBuffer::openBuffer(dictPath, lookupTableFileName, isUpdatable)), mAddressTableBuffer( MmappedBuffer::openBuffer(dictPath, addressTableFileName, isUpdatable)), - mContentBuffer(MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), + mContentBuffer( + MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), mExpandableLookupTableBuffer( - mLookupTableBuffer.get() ? mLookupTableBuffer.get()->getBuffer() : 0, - mLookupTableBuffer.get() ? mLookupTableBuffer.get()->getBufferSize() : 0, + mLookupTableBuffer ? mLookupTableBuffer->getBuffer() : nullptr, + mLookupTableBuffer ? mLookupTableBuffer->getBufferSize() : 0, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mExpandableAddressTableBuffer( - mAddressTableBuffer.get() ? mAddressTableBuffer.get()->getBuffer() : 0, - mAddressTableBuffer.get() ? mAddressTableBuffer.get()->getBufferSize() : 0, + mAddressTableBuffer ? mAddressTableBuffer->getBuffer() : nullptr, + mAddressTableBuffer ? mAddressTableBuffer->getBufferSize() : 0, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableContentBuffer(mContentBuffer.get() ? mContentBuffer.get()->getBuffer() : 0, - mContentBuffer.get() ? mContentBuffer.get()->getBufferSize() : 0, + mExpandableContentBuffer(mContentBuffer ? mContentBuffer->getBuffer() : nullptr, + mContentBuffer ? mContentBuffer->getBufferSize() : 0, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, sparseTableBlockSize, sparseTableDataSize), - mIsValid(mLookupTableBuffer.get() != 0 && mAddressTableBuffer.get() != 0 - && mContentBuffer.get() != 0) {} + mIsValid(mLookupTableBuffer && mAddressTableBuffer && mContentBuffer) {} SparseTableDictContent(const int sparseTableBlockSize, const int sparseTableDataSize) - : mLookupTableBuffer(0), mAddressTableBuffer(0), mContentBuffer(0), + : mLookupTableBuffer(), mAddressTableBuffer(), mContentBuffer(), mExpandableLookupTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mExpandableAddressTableBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mExpandableContentBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp index 59dedee72..9319ea982 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp @@ -27,15 +27,15 @@ namespace latinime { /* static */ Ver4DictBuffers::Ver4DictBuffersPtr Ver4DictBuffers::openVer4DictBuffers( - const char *const dictPath, const MmappedBuffer::MmappedBufferPtr &headerBuffer) { - if (!headerBuffer.get()) { + const char *const dictPath, MmappedBuffer::MmappedBufferPtr headerBuffer) { + if (!headerBuffer) { ASSERT(false); AKLOGE("The header buffer must be valid to open ver4 dict buffers."); - return Ver4DictBuffersPtr(0); + return Ver4DictBuffersPtr(nullptr); } // TODO: take only dictDirPath, and open both header and trie files in the constructor below - return Ver4DictBuffersPtr(new Ver4DictBuffers( - dictPath, headerBuffer, headerBuffer.get()->isUpdatable())); + const bool isUpdatable = headerBuffer->isUpdatable(); + return Ver4DictBuffersPtr(new Ver4DictBuffers(dictPath, std::move(headerBuffer), isUpdatable)); } bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, @@ -113,27 +113,25 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, } Ver4DictBuffers::Ver4DictBuffers(const char *const dictPath, - const MmappedBuffer::MmappedBufferPtr &headerBuffer, const bool isUpdatable) - : mHeaderBuffer(headerBuffer), + MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable) + : mHeaderBuffer(std::move(headerBuffer)), mDictBuffer(MmappedBuffer::openBuffer(dictPath, Ver4DictConstants::TRIE_FILE_EXTENSION, isUpdatable)), - mHeaderPolicy(headerBuffer.get()->getBuffer(), FormatUtils::VERSION_4), - mExpandableHeaderBuffer(headerBuffer.get() ? headerBuffer.get()->getBuffer() : 0, + mHeaderPolicy(mHeaderBuffer->getBuffer(), FormatUtils::VERSION_4), + mExpandableHeaderBuffer(mHeaderBuffer ? mHeaderBuffer->getBuffer() : nullptr, mHeaderPolicy.getSize(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableTrieBuffer(mDictBuffer.get() ? mDictBuffer.get()->getBuffer() : 0, - mDictBuffer.get() ? mDictBuffer.get()->getBufferSize() : 0, + mExpandableTrieBuffer(mDictBuffer ? mDictBuffer->getBuffer() : nullptr, + mDictBuffer ? mDictBuffer->getBufferSize() : 0, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mTerminalPositionLookupTable(dictPath, isUpdatable), - mProbabilityDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), - isUpdatable), - mBigramDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), - isUpdatable), + 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(), + : mHeaderBuffer(nullptr), mDictBuffer(nullptr), mHeaderPolicy(), mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mExpandableTrieBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mTerminalPositionLookupTable(), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h index 776bb9882..ab756bb41 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h @@ -17,6 +17,8 @@ #ifndef LATINIME_VER4_DICT_BUFFER_H #define LATINIME_VER4_DICT_BUFFER_H +#include <memory> + #include "defines.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h" @@ -31,10 +33,10 @@ namespace latinime { class Ver4DictBuffers { public: - typedef ExclusiveOwnershipPointer<Ver4DictBuffers> Ver4DictBuffersPtr; + typedef std::unique_ptr<Ver4DictBuffers> Ver4DictBuffersPtr; static Ver4DictBuffersPtr openVer4DictBuffers(const char *const dictDirPath, - const MmappedBuffer::MmappedBufferPtr &headerBuffer); + MmappedBuffer::MmappedBufferPtr headerBuffer); static AK_FORCE_INLINE Ver4DictBuffersPtr createVer4DictBuffers( const HeaderPolicy *const headerPolicy) { @@ -42,7 +44,7 @@ class Ver4DictBuffers { } AK_FORCE_INLINE bool isValid() const { - return mHeaderBuffer.get() && mDictBuffer.get() && mHeaderPolicy.isValid() + return mHeaderBuffer && mDictBuffer && mHeaderPolicy.isValid() && mProbabilityDictContent.isValid() && mTerminalPositionLookupTable.isValid() && mBigramDictContent.isValid() && mShortcutDictContent.isValid(); } @@ -118,7 +120,7 @@ class Ver4DictBuffers { DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers); Ver4DictBuffers(const char *const dictDirPath, - const MmappedBuffer::MmappedBufferPtr &headerBuffer, const bool isUpdatable); + const MmappedBuffer::MmappedBufferPtr headerBuffer, const bool isUpdatable); Ver4DictBuffers(const HeaderPolicy *const headerPolicy); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index 4d1b0dadb..1a38a27ff 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -135,7 +135,7 @@ int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) con if (ptNodeParams.isDeleted()) { return NOT_A_DICT_POS; } - return mBuffers.get()->getShortcutDictContent()->getShortcutListHeadPos( + return mBuffers->getShortcutDictContent()->getShortcutListHeadPos( ptNodeParams.getTerminalId()); } @@ -147,7 +147,7 @@ int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) cons if (ptNodeParams.isDeleted()) { return NOT_A_DICT_POS; } - return mBuffers.get()->getBigramDictContent()->getBigramListHeadPos( + return mBuffers->getBigramDictContent()->getBigramListHeadPos( ptNodeParams.getTerminalId()); } @@ -155,7 +155,7 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len 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()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary."); return false; } @@ -205,7 +205,7 @@ bool Ver4PatriciaTriePolicy::addUnigramWord(const int *const word, const int len bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int length0, const int *const word1, const int length1, const int probability, const int timestamp) { - if (!mBuffers.get()->isUpdatable()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); return false; } @@ -243,7 +243,7 @@ bool Ver4PatriciaTriePolicy::addBigramWords(const int *const word0, const int le bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int length0, const int *const word1, const int length1) { - if (!mBuffers.get()->isUpdatable()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary."); return false; } @@ -276,7 +276,7 @@ bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int } void Ver4PatriciaTriePolicy::flush(const char *const filePath) { - if (!mBuffers.get()->isUpdatable()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); return; } @@ -287,7 +287,7 @@ void Ver4PatriciaTriePolicy::flush(const char *const filePath) { } void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { - if (!mBuffers.get()->isUpdatable()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary."); return; } @@ -298,11 +298,11 @@ void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { } bool Ver4PatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const { - if (!mBuffers.get()->isUpdatable()) { + if (!mBuffers->isUpdatable()) { AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary."); return false; } - if (mBuffers.get()->isNearSizeLimit()) { + if (mBuffers->isNearSizeLimit()) { // Additional buffer size is near the limit. return true; } else if (mHeaderPolicy->getExtendedRegionSize() + mDictBuffer->getUsedAdditionalBufferSize() @@ -354,7 +354,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code std::vector<int> codePointVector(ptNodeParams.getCodePoints(), ptNodeParams.getCodePoints() + ptNodeParams.getCodePointCount()); const ProbabilityEntry probabilityEntry = - mBuffers.get()->getProbabilityDictContent()->getProbabilityEntry( + mBuffers->getProbabilityDictContent()->getProbabilityEntry( ptNodeParams.getTerminalId()); const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); // Fetch bigram information. @@ -362,9 +362,9 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code const int bigramListPos = getBigramsPositionOfPtNode(ptNodePos); if (bigramListPos != NOT_A_DICT_POS) { int bigramWord1CodePoints[MAX_WORD_LENGTH]; - const BigramDictContent *const bigramDictContent = mBuffers.get()->getBigramDictContent(); + const BigramDictContent *const bigramDictContent = mBuffers->getBigramDictContent(); const TerminalPositionLookupTable *const terminalPositionLookupTable = - mBuffers.get()->getTerminalPositionLookupTable(); + mBuffers->getTerminalPositionLookupTable(); bool hasNext = true; int readingPos = bigramListPos; while (hasNext) { @@ -400,7 +400,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code if (shortcutPos != NOT_A_DICT_POS) { int shortcutTarget[MAX_WORD_LENGTH]; const ShortcutDictContent *const shortcutDictContent = - mBuffers.get()->getShortcutDictContent(); + mBuffers->getShortcutDictContent(); bool hasNext = true; while (hasNext) { int shortcutTargetLength = 0; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h index 639c153a1..cffb1f64d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -37,17 +37,16 @@ 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(), mHeaderPolicy), + Ver4PatriciaTriePolicy(Ver4DictBuffers::Ver4DictBuffersPtr buffers) + : mBuffers(std::move(buffers)), mHeaderPolicy(mBuffers->getHeaderPolicy()), + mDictBuffer(mBuffers->getWritableTrieBuffer()), + mBigramPolicy(mBuffers->getMutableBigramDictContent(), + mBuffers->getTerminalPositionLookupTable(), mHeaderPolicy), + mShortcutPolicy(mBuffers->getMutableShortcutDictContent(), + mBuffers->getTerminalPositionLookupTable()), + mNodeReader(mDictBuffer, mBuffers->getProbabilityDictContent(), mHeaderPolicy), mPtNodeArrayReader(mDictBuffer), mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader, &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy), @@ -132,7 +131,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { static const int MARGIN_TO_REFUSE_DYNAMIC_OPERATIONS; static const int MIN_DICT_SIZE_TO_REFUSE_DYNAMIC_OPERATIONS; - Ver4DictBuffers::Ver4DictBuffersPtr mBuffers; + const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers; const HeaderPolicy *const mHeaderPolicy; BufferWithExtendableBuffer *const mDictBuffer; Ver4BigramListPolicy mBigramPolicy; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp index 3907c84a0..2b1f60e5d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -67,7 +67,7 @@ bool Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeAr unigramCount, bigramCount, 0 /* extendedRegionSize */, &headerBuffer)) { return false; } - return dictBuffers.get()->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); + return dictBuffers->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer); } bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, 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 faef72079..4459e86a3 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 @@ -48,17 +48,17 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = const std::vector<int> localeAsCodePointVector, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { HeaderPolicy headerPolicy(FormatUtils::VERSION_4, localeAsCodePointVector, attributeMap); - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = - Ver4DictBuffers::createVer4DictBuffers(&headerPolicy); + Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( + Ver4DictBuffers::createVer4DictBuffers(&headerPolicy)); headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, 0 /* unigramCount */, 0 /* bigramCount */, - 0 /* extendedRegionSize */, dictBuffers.get()->getWritableHeaderBuffer()); + 0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer()); if (!DynamicPtWritingUtils::writeEmptyDictionary( - dictBuffers.get()->getWritableTrieBuffer(), 0 /* rootPos */)) { + dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); return false; } - return dictBuffers.get()->flush(dirPath); + return dictBuffers->flush(dirPath); } /* static */ bool DictFileWritingUtils::flushAllHeaderAndBodyToFile(const char *const filePath, 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 35e05d77a..bac4d4eba 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 @@ -16,6 +16,7 @@ #include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h" +#include <algorithm> #include <cmath> #include <stdlib.h> @@ -72,7 +73,7 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT headerPolicy->getForgettingCurveDurationToLevelDown()); return sProbabilityTable.getProbability( headerPolicy->getForgettingCurveProbabilityValuesTableId(), historicalInfo->getLevel(), - min(max(elapsedTimeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT)); + std::min(std::max(elapsedTimeStepCount, 0), MAX_ELAPSED_TIME_STEP_COUNT)); } /* static */ int ForgettingCurveUtils::getProbability(const int unigramProbability, @@ -80,11 +81,11 @@ const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityT if (unigramProbability == NOT_A_PROBABILITY) { return NOT_A_PROBABILITY; } else if (bigramProbability == NOT_A_PROBABILITY) { - return min(backoff(unigramProbability), MAX_PROBABILITY); + return std::min(backoff(unigramProbability), MAX_PROBABILITY); } else { // TODO: Investigate better way to handle bigram probability. - return min(max(unigramProbability, bigramProbability + MULTIPLIER_TWO_IN_PROBABILITY_SCALE), - MAX_PROBABILITY); + return std::min(std::max(unigramProbability, + bigramProbability + MULTIPLIER_TWO_IN_PROBABILITY_SCALE), MAX_PROBABILITY); } } @@ -183,7 +184,7 @@ ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTables() { -1.0f * static_cast<float>(timeStepCount) / static_cast<float>(MAX_ELAPSED_TIME_STEP_COUNT + 1)); mTables[tableId][level][timeStepCount] = - min(max(static_cast<int>(probability), 1), MAX_PROBABILITY); + std::min(std::max(static_cast<int>(probability), 1), MAX_PROBABILITY); } } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp index e88d6e0a9..d3e0c237f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp @@ -33,7 +33,7 @@ namespace latinime { const int mmapFd = open(path, O_RDONLY); if (mmapFd < 0) { AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno); - return MmappedBufferPtr(0); + return MmappedBufferPtr(nullptr); } const int pagesize = sysconf(_SC_PAGESIZE); const int offset = bufferOffset % pagesize; @@ -45,13 +45,13 @@ namespace latinime { if (mmappedBuffer == MAP_FAILED) { AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); close(mmapFd); - return MmappedBufferPtr(0); + return MmappedBufferPtr(nullptr); } uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset; if (!buffer) { AKLOGE("DICT: buffer is null"); close(mmapFd); - return MmappedBufferPtr(0); + return MmappedBufferPtr(nullptr); } return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, mmapFd, isUpdatable)); @@ -61,7 +61,7 @@ namespace latinime { const char *const path, const bool isUpdatable) { const int fileSize = FileUtils::getFileSize(path); if (fileSize == -1) { - return MmappedBufferPtr(0); + return MmappedBufferPtr(nullptr); } else if (fileSize == 0) { return MmappedBufferPtr(new MmappedBuffer(isUpdatable)); } else { @@ -76,7 +76,7 @@ namespace latinime { const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath, fileName); if (filePathLength >= filePathBufferSize) { - return 0; + return MmappedBufferPtr(nullptr); } return openBuffer(filePath, isUpdatable); } 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 73a733b0c..f73716c8e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h @@ -17,16 +17,16 @@ #ifndef LATINIME_MMAPPED_BUFFER_H #define LATINIME_MMAPPED_BUFFER_H +#include <memory> #include <stdint.h> #include "defines.h" -#include "utils/exclusive_ownership_pointer.h" namespace latinime { class MmappedBuffer { public: - typedef ExclusiveOwnershipPointer<MmappedBuffer> MmappedBufferPtr; + typedef std::unique_ptr<const MmappedBuffer> MmappedBufferPtr; static MmappedBufferPtr openBuffer(const char *const path, const int bufferOffset, const int bufferSize, const bool isUpdatable); @@ -60,8 +60,8 @@ class MmappedBuffer { // 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) {} + : mBuffer(nullptr), mBufferSize(0), mMmappedBuffer(nullptr), mAlignedSize(0), + mMmapFd(0), mIsUpdatable(isUpdatable) {} DISALLOW_IMPLICIT_CONSTRUCTORS(MmappedBuffer); diff --git a/native/jni/src/suggest/policyimpl/utils/edit_distance.h b/native/jni/src/suggest/policyimpl/utils/edit_distance.h index 0871c37ce..4cfd0b3f3 100644 --- a/native/jni/src/suggest/policyimpl/utils/edit_distance.h +++ b/native/jni/src/suggest/policyimpl/utils/edit_distance.h @@ -17,6 +17,8 @@ #ifndef LATINIME_EDIT_DISTANCE_H #define LATINIME_EDIT_DISTANCE_H +#include <algorithm> + #include "defines.h" #include "suggest/policyimpl/utils/edit_distance_policy.h" @@ -38,13 +40,13 @@ class EditDistance { for (int i = 0; i < beforeLength; ++i) { for (int j = 0; j < afterLength; ++j) { - dp[(afterLength + 1) * (i + 1) + (j + 1)] = min( + dp[(afterLength + 1) * (i + 1) + (j + 1)] = std::min( dp[(afterLength + 1) * i + (j + 1)] + policy->getInsertionCost(i, j), - min(dp[(afterLength + 1) * (i + 1) + j] + policy->getDeletionCost(i, j), - dp[(afterLength + 1) * i + j] - + policy->getSubstitutionCost(i, j))); + std::min( + dp[(afterLength + 1) * (i + 1) + j] + policy->getDeletionCost(i, j), + dp[(afterLength + 1) * i + j] + policy->getSubstitutionCost(i, j))); if (policy->allowTransposition(i, j)) { - dp[(afterLength + 1) * (i + 1) + (j + 1)] = min( + dp[(afterLength + 1) * (i + 1) + (j + 1)] = std::min( dp[(afterLength + 1) * (i + 1) + (j + 1)], dp[(afterLength + 1) * (i - 1) + (j - 1)] + policy->getTranspositionCost(i, j)); diff --git a/native/jni/src/utils/autocorrection_threshold_utils.cpp b/native/jni/src/utils/autocorrection_threshold_utils.cpp index 1f8ee0814..349786a27 100644 --- a/native/jni/src/utils/autocorrection_threshold_utils.cpp +++ b/native/jni/src/utils/autocorrection_threshold_utils.cpp @@ -16,6 +16,7 @@ #include "utils/autocorrection_threshold_utils.h" +#include <algorithm> #include <cmath> #include "defines.h" @@ -99,7 +100,7 @@ const int AutocorrectionThresholdUtils::FULL_WORD_MULTIPLIER = 2; const float maxScore = score >= S_INT_MAX ? static_cast<float>(S_INT_MAX) : static_cast<float>(MAX_INITIAL_SCORE) * powf(static_cast<float>(TYPED_LETTER_MULTIPLIER), - static_cast<float>(min(beforeLength, afterLength - spaceCount))) + static_cast<float>(std::min(beforeLength, afterLength - spaceCount))) * static_cast<float>(FULL_WORD_MULTIPLIER); return (static_cast<float>(score) / maxScore) * weight; diff --git a/native/jni/src/utils/exclusive_ownership_pointer.h b/native/jni/src/utils/exclusive_ownership_pointer.h deleted file mode 100644 index 081802e8b..000000000 --- a/native/jni/src/utils/exclusive_ownership_pointer.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#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/hash_map_compat.h b/native/jni/src/utils/hash_map_compat.h index a1e982bc4..7bf35a660 100644 --- a/native/jni/src/utils/hash_map_compat.h +++ b/native/jni/src/utils/hash_map_compat.h @@ -17,18 +17,12 @@ #ifndef LATINIME_HASH_MAP_COMPAT_H #define LATINIME_HASH_MAP_COMPAT_H -// TODO: Use std::unordered_map that has been standardized in C++11 +#include <unordered_map> -#ifdef __APPLE__ -#include <ext/hash_map> -#else // __APPLE__ -#include <hash_map> -#endif // __APPLE__ +#define hash_map_compat std::unordered_map -#ifdef __SGI_STL_PORT -#define hash_map_compat stlport::hash_map -#else // __SGI_STL_PORT -#define hash_map_compat __gnu_cxx::hash_map -#endif // __SGI_STL_PORT +#if 0 // TODO: Use this instead of the above macro. +template <typename TKey, typename TValue> using hash_map_compat = std::unordered_map<TKey, TValue>; +#endif #endif // LATINIME_HASH_MAP_COMPAT_H diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java new file mode 100644 index 000000000..06139b808 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelTests.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard; + +import android.content.res.Resources; +import android.test.suitebuilder.annotation.MediumTest; +import android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.utils.RunInLocale; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +@MediumTest +public final class KeyboardLayoutSetActionLabelTests extends KeyboardLayoutSetTestsBase { + private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet, + final int elementId, final String label, final int iconId) { + final Keyboard keyboard = layoutSet.getKeyboard(elementId); + final Key enterKey = keyboard.getKey(Constants.CODE_ENTER); + assertNotNull(tag + " enter key on " + keyboard.mId, enterKey); + assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel()); + assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId()); + } + + private void doTestActionLabel(final String tag, final InputMethodSubtype subtype, + final int actionId, final int labelResId) { + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.imeOptions = actionId; + final RunInLocale<String> job = new RunInLocale<String>() { + @Override + protected String job(final Resources res) { + return res.getString(labelResId); + } + }; + final Resources res = getContext().getResources(); + final String label; + if (subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE)) { + // Using system locale. + label = res.getString(labelResId); + } else { + label = job.runInLocale(res, SubtypeLocaleUtils.getSubtypeLocale(subtype)); + } + // Test text layouts. + editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, + label, KeyboardIconsSet.ICON_UNDEFINED); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, + label, KeyboardIconsSet.ICON_UNDEFINED); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, + label, KeyboardIconsSet.ICON_UNDEFINED); + // Test phone number layouts. + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, + label, KeyboardIconsSet.ICON_UNDEFINED); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, + label, KeyboardIconsSet.ICON_UNDEFINED); + // Test normal number layout. + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, + label, KeyboardIconsSet.ICON_UNDEFINED); + // Test number password layouts. + editorInfo.inputType = + InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD; + final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo); + doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, + label, KeyboardIconsSet.ICON_UNDEFINED); + } + + private void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype, + final int actionId, final String iconName) { + final int iconId = KeyboardIconsSet.getIconId(iconName); + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.imeOptions = actionId; + // Test text layouts. + editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_ALPHABET, null /* label */, iconId); + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS, null /* label */, iconId); + doTestActionKey( + tag, layoutSet, KeyboardId.ELEMENT_SYMBOLS_SHIFTED, null /* label */, iconId); + // Test phone number layouts. + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_PHONE, null /* label */, iconId); + doTestActionKey( + tag, layoutSet, KeyboardId.ELEMENT_PHONE_SYMBOLS, null /* label */, iconId); + // Test normal number layout. + doTestActionKey(tag, layoutSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId); + // Test number password layout. + editorInfo.inputType = + InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD; + final KeyboardLayoutSet passwordSet = createKeyboardLayoutSet(subtype, editorInfo); + doTestActionKey(tag, passwordSet, KeyboardId.ELEMENT_NUMBER, null /* label */, iconId); + } + + public void testActionUnspecified() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "unspecifiled " + + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED, "enter_key"); + } + } + + public void testActionNone() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, "enter_key"); + } + } + + public void testActionGo() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "go " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_GO, R.string.label_go_key); + } + } + + public void testActionSearch() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH, "search_key"); + } + } + + public void testActionSend() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_SEND, R.string.label_send_key); + } + } + + public void testActionNext() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "next " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_NEXT, R.string.label_next_key); + } + } + + public void testActionDone() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "done " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionLabel(tag, subtype, EditorInfo.IME_ACTION_DONE, R.string.label_done_key); + } + } + + public void testActionPrevious() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "previous " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionLabel( + tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, R.string.label_previous_key); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java new file mode 100644 index 000000000..e691639a8 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard; + +import android.test.suitebuilder.annotation.SmallTest; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.ArrayList; + +@SmallTest +public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase { + private static final int NUMBER_OF_SUBTYPES = 63; + private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40; + private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2; + + private static String toString(final ArrayList<InputMethodSubtype> subtypeList) { + final StringBuilder sb = new StringBuilder(); + for (int index = 0; index < subtypeList.size(); index++) { + final InputMethodSubtype subtype = subtypeList.get(index); + sb.append(index + ": "); + sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype)); + sb.append("\n"); + } + return sb.toString(); + } + + public final void testAllSubtypesCount() { + final ArrayList<InputMethodSubtype> allSubtypesList = getAllSubtypesList(); + assertEquals(toString(allSubtypesList), NUMBER_OF_SUBTYPES, allSubtypesList.size()); + } + + public final void testAsciiCapableSubtypesCount() { + final ArrayList<InputMethodSubtype> asciiCapableSubtypesList = + getAsciiCapableSubtypesList(); + assertEquals(toString(asciiCapableSubtypesList), + NUMBER_OF_ASCII_CAPABLE_SUBTYPES, asciiCapableSubtypesList.size()); + } + + public final void testAdditionalSubtypesCount() { + final ArrayList<InputMethodSubtype> additionalSubtypesList = getAdditionalSubtypesList(); + assertEquals(toString(additionalSubtypesList), + NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES, additionalSubtypesList.size()); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index 9939a4335..0993c4b67 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -41,9 +41,6 @@ import java.util.Locale; @SmallTest public class KeyboardLayoutSetTestsBase extends AndroidTestCase { - private static final int NUMBER_OF_SUBTYPES = 63; - private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 40; - private static final KeyboardTheme DEFAULT_KEYBOARD_THEME = KeyboardSwitcher.KEYBOARD_THEMES[KeyboardSwitcher.THEME_INDEX_DEFAULT]; @@ -51,6 +48,8 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase { private final ArrayList<InputMethodSubtype> mAllSubtypesList = CollectionUtils.newArrayList(); private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = CollectionUtils.newArrayList(); + private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = + CollectionUtils.newArrayList(); private Context mThemeContext; private int mScreenMetrics; @@ -68,6 +67,10 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase { final int subtypeCount = imi.getSubtypeCount(); for (int index = 0; index < subtypeCount; index++) { final InputMethodSubtype subtype = imi.getSubtypeAt(index); + if (AdditionalSubtypeUtils.isAdditionalSubtype(subtype)) { + mAdditionalSubtypesList.add(subtype); + continue; + } mAllSubtypesList.add(subtype); if (InputMethodSubtypeCompatUtils.isAsciiCapable(subtype)) { mAsciiCapableSubtypesList.add(subtype); @@ -75,30 +78,21 @@ public class KeyboardLayoutSetTestsBase extends AndroidTestCase { } } - protected final boolean isPhone() { - return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE - || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE; + protected final ArrayList<InputMethodSubtype> getAllSubtypesList() { + return mAllSubtypesList; } - private static String toString(final ArrayList<InputMethodSubtype> subtypeList) { - final StringBuilder sb = new StringBuilder(); - for (int index = 0; index < subtypeList.size(); index++) { - final InputMethodSubtype subtype = subtypeList.get(index); - sb.append((index + 1) + ": "); - sb.append(SubtypeLocaleUtils.getSubtypeNameForLogging(subtype)); - sb.append("\n"); - } - return sb.toString(); + protected final ArrayList<InputMethodSubtype> getAsciiCapableSubtypesList() { + return mAsciiCapableSubtypesList; } - public final void testAllSubtypesCount() { - assertEquals(toString(mAllSubtypesList), - NUMBER_OF_SUBTYPES, mAllSubtypesList.size()); + protected final ArrayList<InputMethodSubtype> getAdditionalSubtypesList() { + return mAdditionalSubtypesList; } - public final void testAsciiCapableSubtypesCount() { - assertEquals(toString(mAsciiCapableSubtypesList), - NUMBER_OF_ASCII_CAPABLE_SUBTYPES, mAsciiCapableSubtypesList.size()); + protected final boolean isPhone() { + return mScreenMetrics == Constants.SCREEN_METRICS_SMALL_PHONE + || mScreenMetrics == Constants.SCREEN_METRICS_LARGE_PHONE; } protected final InputMethodSubtype getSubtype(final Locale locale, diff --git a/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java new file mode 100644 index 000000000..be3ed12de --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/AlphabetShifted.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout; + +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.LayoutBase; +import com.android.inputmethod.latin.Constants; + +import java.util.Locale; + +/** + * The generic upper case alphabet keyboard layout. + */ +public final class AlphabetShifted extends LayoutBase { + public static ExpectedKey[][] getAlphabet(final ExpectedKey[][] lowerCaseKeyboard, + final Locale locale) { + final ExpectedKey[][] upperCaseKeyboard = ExpectedKeyboardBuilder.toUpperCase( + lowerCaseKeyboard, locale); + return new ExpectedKeyboardBuilder(upperCaseKeyboard) + .replaceKeyOfAll(SHIFT_KEY, SHIFTED_SHIFT_KEY) + .build(); + } + + // Icon id. + private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId("shift_key_shifted"); + + // Functional key. + private static final ExpectedKey SHIFTED_SHIFT_KEY = key( + ICON_SHIFTED_SHIFT, Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY); +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java new file mode 100644 index 000000000..f7179b739 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/Qwerty.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout; + +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.LayoutBase; + +/** + * The QWERTY alphabet keyboard. + */ +public final class Qwerty extends LayoutBase { + public static ExpectedKey[][] getAlphabet(final boolean isPhone) { + return toCommonAlphabet(ALPHABET_COMMON, isPhone); + } + + private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder(10, 9, 7, 3) + .setLabelsOfRow(1, "q", "w", "e", "r", "t", "y", "u", "i", "o", "p") + .setMoreKeysOf("q", "1") + .setMoreKeysOf("w", "2") + .setMoreKeysOf("e", "3") + .setMoreKeysOf("r", "4") + .setMoreKeysOf("t", "5") + .setMoreKeysOf("y", "6") + .setMoreKeysOf("u", "7") + .setMoreKeysOf("i", "8") + .setMoreKeysOf("o", "9") + .setMoreKeysOf("p", "0") + .setLabelsOfRow(2, "a", "s", "d", "f", "g", "h", "j", "k", "l") + .setLabelsOfRow(3, "z", "x", "c", "v", "b", "n", "m") + .build(); +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java new file mode 100644 index 000000000..03d7f07e9 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout; + +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.LayoutBase; +import com.android.inputmethod.latin.Constants; + +/** + * The symbols keyboard layout. + */ +public final class Symbols extends LayoutBase { + public static ExpectedKey[][] getSymbols(final boolean isPhone) { + return isPhone ? toPhoneSymbol(SYMBOLS_COMMON) : toTabletSymbols(SYMBOLS_COMMON); + } + + // Functional keys. + public static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL); + public static final ExpectedKey SYMBOLS_SHIFT_KEY = key("= \\ <", Constants.CODE_SHIFT); + public static final ExpectedKey TABLET_SYMBOLS_SHIFT_KEY = key("~ [ <", Constants.CODE_SHIFT); + + // Common symbols keyboard layout. + public static final ExpectedKey[][] SYMBOLS_COMMON = new ExpectedKeyboardBuilder(10, 9, 7, 5) + .setLabelsOfRow(1, "1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + // 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 + .setMoreKeysOf("1", "\u00B9", "\u00BD", "\u2153", "\u00BC", "\u215B") + // U+00B2: "²" SUPERSCRIPT TWO + // U+2154: "⅔" VULGAR FRACTION TWO THIRDS + .setMoreKeysOf("2", "\u00B2", "\u2154") + // U+00B3: "³" SUPERSCRIPT THREE + // U+00BE: "¾" VULGAR FRACTION THREE QUARTERS + // U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS + .setMoreKeysOf("3", "\u00B3", "\u00BE", "\u215C") + // U+2074: "⁴" SUPERSCRIPT FOUR + .setMoreKeysOf("4", "\u2074") + // U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS + .setMoreKeysOf("5", "\u215D") + // U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS + .setMoreKeysOf("7", "\u215E") + // U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N + // U+2205: "∅" EMPTY SET + .setMoreKeysOf("0", "\u207F", "\u2205") + .setLabelsOfRow(2, "@", "#", "$", "%", "&", "-", "+", "(", ")") + // U+00A2: "¢" CENT SIGN + // U+00A3: "£" POUND SIGN + // U+20AC: "€" EURO SIGN + // U+00A5: "¥" YEN SIGN + // U+20B1: "₱" PESO SIGN + .setMoreKeysOf("$", "\u00A2", "\u00A3", "\u20AC", "\u00A5", "\u20B1") + // U+2030: "‰" PER MILLE SIGN + .setMoreKeysOf("%", "\u2030") + // U+2013: "–" EN DASH + // U+2014: "—" EM DASH + // U+00B7: "·" MIDDLE DOT + .setMoreKeysOf("-", "_", "\u2013", "\u2014", "\u00B7") + // U+00B1: "±" PLUS-MINUS SIGN + .setMoreKeysOf("+", "\u00B1") + .setMoreKeysOf("(", "<", "{", "[") + .setMoreKeysOf(")", ">", "}", "]") + .setLabelsOfRow(3, "*", "\"", "'", ":", ";", "!", "?") + // U+2020: "†" DAGGER + // U+2021: "‡" DOUBLE DAGGER + // U+2605: "★" BLACK STAR + .setMoreKeysOf("*", "\u2020", "\u2021", "\u2605") + // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK + // U+201C: "“" LEFT DOUBLE QUOTATION MARK + // U+201D: "”" RIGHT DOUBLE QUOTATION MARK + // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + .setMoreKeysOf("\"", "\u201E", "\u201C", "\u201D", "\u00AB", "\u00BB") + // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK + // U+2018: "‘" LEFT SINGLE QUOTATION MARK + // U+2019: "’" RIGHT SINGLE QUOTATION MARK + // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK + // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + .setMoreKeysOf("'", "\u201A", "\u2018", "\u2019", "\u2039", "\u203A") + // U+00A1: "¡" INVERTED EXCLAMATION MARK + .setMoreKeysOf("!", "\u00A1") + // U+00BF: "¿" INVERTED QUESTION MARK + .setMoreKeysOf("?", "\u00BF") + .setLabelsOfRow(4, "_", "/", " ", ",", ".") + // U+2026: "…" HORIZONTAL ELLIPSIS + .setMoreKeysOf(".", "\u2026") + .build(); + + private static ExpectedKey[][] toPhoneSymbol(final ExpectedKey[][] common) { + return new ExpectedKeyboardBuilder(common) + .addKeysOnTheLeftOfRow(3, Symbols.SYMBOLS_SHIFT_KEY) + .addKeysOnTheRightOfRow(3, DELETE_KEY) + .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY) + .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)) + .build(); + } + + private static ExpectedKey[][] toTabletSymbols(final ExpectedKey[][] common) { + return new ExpectedKeyboardBuilder(common) + .addKeysOnTheLeftOfRow(3, + key("\\"), key("=")) + .addKeysOnTheRightOfRow(1, DELETE_KEY) + .addKeysOnTheRightOfRow(2, ENTER_KEY) + .addKeysOnTheLeftOfRow(3, Symbols.TABLET_SYMBOLS_SHIFT_KEY) + .addKeysOnTheRightOfRow(3, Symbols.TABLET_SYMBOLS_SHIFT_KEY) + .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY) + .addKeysOnTheRightOfRow(4, EMOJI_KEY) + .build(); + } + + // Helper method to add currency symbols for Euro. + public static ExpectedKeyboardBuilder euro(final ExpectedKeyboardBuilder builder) { + return builder + // U+20AC: "€" EURO SIGN + // U+00A2: "¢" CENT SIGN + // U+00A3: "£" POUND SIGN + // U+00A5: "¥" YEN SIGN + // U+20B1: "₱" PESO SIGN + .replaceKeyOfLabel("$", key("\u20AC", + moreKey("\u00A2"), moreKey("\u00A3"), moreKey("$"), + moreKey("\u00A5"), moreKey("\u20B1"))); + } + + // Helper method to add single quotes "more keys". + // "9LLR" means "9-low/Left quotation marks, Left/Right-pointing angle quotation marks". + public static ExpectedKeyboardBuilder singleQuotes9LLR(final ExpectedKeyboardBuilder builder) { + return builder + // U+2019: "’" RIGHT SINGLE QUOTATION MARK + // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK + // U+2018: "‘" LEFT SINGLE QUOTATION MARK + // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK + // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + .setMoreKeysOf("'", "\u2019", "\u201A", "\u2018", "\u2039", "\u203A"); + } + + // Helper method to add single quotes "more keys". + // "9LLR" means "9-low/Left quotation marks, Right/Left-pointing angle quotation marks". + public static ExpectedKeyboardBuilder singleQuotes9LRL(final ExpectedKeyboardBuilder builder) { + return builder + // U+2019: "’" RIGHT SINGLE QUOTATION MARK + // U+201A: "‚" SINGLE LOW-9 QUOTATION MARK + // U+2018: "‘" LEFT SINGLE QUOTATION MARK + // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK + .setMoreKeysOf("'", "\u2019", "\u201A", "\u2018", "\u203A", "\u2039"); + } + + // Helper method to add double quotes "more keys". + // "9LLR" means "9-low/Left quotation marks, Left/Right-pointing angle quotation marks". + public static ExpectedKeyboardBuilder doubleQuotes9LLR(final ExpectedKeyboardBuilder builder) { + return builder + // U+201D: "”" RIGHT DOUBLE QUOTATION MARK + // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK + // U+201C: "“" LEFT DOUBLE QUOTATION MARK + // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + .setMoreKeysOf("\"", "\u201D", "\u201E", "\u201C", "\u00AB", "\u00BB"); + } + + // Helper method to add double quotes "more keys". + // "9LLR" means "9-low/Left quotation marks, Right/Left-pointing angle quotation marks". + public static ExpectedKeyboardBuilder doubleQuotes9LRL(final ExpectedKeyboardBuilder builder) { + return builder + // U+201D: "”" RIGHT DOUBLE QUOTATION MARK + // U+201E: "„" DOUBLE LOW-9 QUOTATION MARK + // U+201C: "“" LEFT DOUBLE QUOTATION MARK + // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + .setMoreKeysOf("\"", "\u201D", "\u201E", "\u201C", "\u00BB", "\u00AB"); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java new file mode 100644 index 000000000..368f9db46 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout; + +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.LayoutBase; +import com.android.inputmethod.latin.Constants; + +/** + * The symbols shifted keyboard layout. + */ +public final class SymbolsShifted extends LayoutBase { + public static ExpectedKey[][] getSymbolsShifted(final boolean isPhone) { + return isPhone ? toPhoneSymbolsShifted(SYMBOLS_SHIFTED_COMMON) + : toTabletSymbolsShifted(SYMBOLS_SHIFTED_COMMON); + } + + // Functional key. + public static final ExpectedKey BACK_TO_SYMBOLS_KEY = key("?123", Constants.CODE_SHIFT); + + // Common symbols shifted keyboard layout. + public static final ExpectedKey[][] SYMBOLS_SHIFTED_COMMON = + new ExpectedKeyboardBuilder(10, 9, 7, 5) + // U+0060: "`" GRAVE ACCENT + // U+2022: "•" BULLET + // U+221A: "√" SQUARE ROOT + // U+03C0: "π" GREEK SMALL LETTER PI + // U+00F7: "÷" DIVISION SIGN + // U+00D7: "×" MULTIPLICATION SIGN + // U+00B6: "¶" PILCROW SIGN + // U+2206: "∆" INCREMENT + .setLabelsOfRow(1, + "~", "\u0060", "|", "\u2022", "\u221A", + "\u03C0", "\u00F7", "\u00D7", "\u00B6", "\u2206") + // U+2022: "•" BULLET + // U+266A: "♪" EIGHTH NOTE + // U+2665: "♥" BLACK HEART SUIT + // U+2660: "♠" BLACK SPADE SUIT + // U+2666: "♦" BLACK DIAMOND SUIT + // U+2663: "♣" BLACK CLUB SUIT + .setMoreKeysOf("\u2022", "\u266A", "\u2665", "\u2660", "\u2666", "\u2663") + // U+03C0: "π" GREEK SMALL LETTER PI + // U+03A0: "Π" GREEK CAPITAL LETTER PI + .setMoreKeysOf("\u03C0", "\u03A0") + // U+00B6: "¶" PILCROW SIGN + // U+00A7: "§" SECTION SIGN + .setMoreKeysOf("\u00B6", "\u00A7") + // U+00A3: "£" POUND SIGN + // U+00A2: "¢" CENT SIGN + // U+20AC: "€" EURO SIGN + // U+00A5: "¥" YEN SIGN + // U+00B0: "°" DEGREE SIGN + .setLabelsOfRow(2, + "\u00A3", "\u00A2", "\u20AC", "\u00A5", "^", + "\u00B0", "=", "{", "}") + // U+2191: "↑" UPWARDS ARROW + // U+2193: "↓" DOWNWARDS ARROW + // U+2190: "←" LEFTWARDS ARROW + // U+2192: "→" RIGHTWARDS ARROW + .setMoreKeysOf("^", "\u2191", "\u2193", "\u2190", "\u2192") + // U+00B0: "°" DEGREE SIGN + // U+2032: "′" PRIME + // U+2033: "″" DOUBLE PRIME + .setMoreKeysOf("\u00B0", "\u2032", "\u2033") + // U+2260: "≠" NOT EQUAL TO + // U+2248: "≈" ALMOST EQUAL TO + // U+221E: "∞" INFINITY + .setMoreKeysOf("=", "\u2260", "\u2248", "\u221E") + // U+00A9: "©" COPYRIGHT SIGN + // U+00AE: "®" REGISTERED SIGN + // U+2122: "™" TRADE MARK SIGN + // U+2105: "℅" CARE OF + .setLabelsOfRow(3, + "\\", "\u00A9", "\u00AE", "\u2122", "\u2105", + "[", "]") + .setLabelsOfRow(4, + "<", ">", " ", ",", ".") + // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK + // U+2264: "≤" LESS-THAN OR EQUAL TO + // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + .setMoreKeysOf("<", "\u2039", "\u2264", "\u00AB") + // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + // U+2265: "≥" GREATER-THAN EQUAL TO + // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + .setMoreKeysOf(">", "\u203A", "\u2265", "\u00BB") + // U+2026: "…" HORIZONTAL ELLIPSIS + .setMoreKeysOf(".", "\u2026") + .build(); + + private static ExpectedKey[][] toPhoneSymbolsShifted(final ExpectedKey[][] common) { + return new ExpectedKeyboardBuilder(common) + .addKeysOnTheLeftOfRow(3, BACK_TO_SYMBOLS_KEY) + .addKeysOnTheRightOfRow(3, DELETE_KEY) + .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY) + .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)) + .build(); + } + + private static ExpectedKey[][] toTabletSymbolsShifted(final ExpectedKey[][] common) { + return new ExpectedKeyboardBuilder(common) + // U+00BF: "¿" INVERTED QUESTION MARK + // U+00A1: "¡" INVERTED EXCLAMATION MARK + .addKeysOnTheRightOfRow(3, + key("\u00A1"), key("\u00BF")) + .addKeysOnTheRightOfRow(1, DELETE_KEY) + .addKeysOnTheRightOfRow(2, ENTER_KEY) + .addKeysOnTheLeftOfRow(3, BACK_TO_SYMBOLS_KEY) + .addKeysOnTheRightOfRow(3, BACK_TO_SYMBOLS_KEY) + .addKeysOnTheLeftOfRow(4, Symbols.ALPHABET_KEY) + .addKeysOnTheRightOfRow(4, EMOJI_KEY) + .build(); + } + + // Helper method to add currency symbols for Euro. + public static ExpectedKeyboardBuilder euro(final ExpectedKeyboardBuilder builder) { + return builder + // U+00A5: "¥" YEN SIGN + // U+00A2: "¢" CENT SIGN + .replaceKeyOfLabel("\u00A5", key("\u00A2")) + // U+20AC: "€" EURO SIGN + // U+00A2: "¢" CENT SIGN + .replaceKeyOfLabel("\u20AC", key("$", moreKey("\u00A2"))) + // U+00A2: "¢" CENT SIGN + // U+00A5: "¥" YEN SIGN + .replaceKeyOfLabel("\u00A2", key("\u00A5")); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java new file mode 100644 index 000000000..45449b762 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractKeyboardBuilder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import java.util.Arrays; + +/** + * This class builds a keyboard that is a two dimensional array of elements <code>E</code>. + * + * A keyboard consists of array of rows, and a row consists of array of elements. Each row may have + * different number of elements. A element of a keyboard can be specified by a row number and a + * column number, both numbers starts from 1. + * + * @param <E> the type of a keyboard element. A keyboard element must be an immutable object. + */ +abstract class AbstractKeyboardBuilder<E> { + // A building array of rows. + private final E[][] mRows; + + // Returns an instance of default element. + abstract E defaultElement(); + // Returns an <code>E</code> array instance of the <code>size</code>. + abstract E[] newArray(final int size); + // Returns an <code>E[]</code> array instance of the <code>size</code>. + abstract E[][] newArrayOfArray(final int size); + + /** + * Construct a builder filled with the default element. + * @param dimensions the integer array of each row's size. + */ + AbstractKeyboardBuilder(final int ... dimensions) { + mRows = newArrayOfArray(dimensions.length); + for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) { + mRows[rowIndex] = newArray(dimensions[rowIndex]); + Arrays.fill(mRows[rowIndex], defaultElement()); + } + } + + /** + * Construct a builder from template keyboard. This builder has the same dimensions and + * elements of <code>rows</rows>. + * @param rows the template keyboard rows. The elements of the <code>rows</code> will be + * shared with this builder. Therefore a element must be an immutable object. + */ + AbstractKeyboardBuilder(final E[][] rows) { + mRows = newArrayOfArray(rows.length); + for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { + final E[] row = rows[rowIndex]; + mRows[rowIndex] = Arrays.copyOf(row, row.length); + } + } + + /** + * Return current constructing keyboard. + * @return the array of the array of the element being constructed. + */ + E[][] build() { + return mRows; + } + + /** + * Get the current contents of the specified row. + * @param row the row number to get the contents. + * @return the array of elements at row number <code>row</code>. + * @throws {@link RuntimeException} if <code>row</code> is illegal. + */ + E[] getRowAt(final int row) { + final int rowIndex = row - 1; + if (rowIndex < 0 || rowIndex >= mRows.length) { + throw new RuntimeException("Illegal row number: " + row); + } + return mRows[rowIndex]; + } + + /** + * Set an array of elements to the specified row. + * @param row the row number to set <code>elements</code>. + * @param elements the array of elements to set at row number <code>row</code>. + * @throws {@link RuntimeException} if <code>row</code> is illegal. + */ + void setRowAt(final int row, final E[] elements) { + final int rowIndex = row - 1; + if (rowIndex < 0 || rowIndex >= mRows.length) { + throw new RuntimeException("Illegal row number: " + row); + } + mRows[rowIndex] = elements; + } + + /** + * Set or insert an element at specified position. + * @param row the row number to set or insert the <code>element</code>. + * @param column the column number to set or insert the <code>element</code>. + * @param element the element to set or insert at <code>row,column</code>. + * @param insert if true, the <code>element</code> is inserted at <code>row,column</code>. + * Otherwise the <code>element</code> replace the element at <code>row,column</code>. + * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal. + */ + void setElementAt(final int row, final int column, final E element, final boolean insert) { + final E[] elements = getRowAt(row); + final int columnIndex = column - 1; + if (insert) { + if (columnIndex < 0 || columnIndex >= elements.length + 1) { + throw new RuntimeException("Illegal column number: " + column); + } + final E[] newElements = Arrays.copyOf(elements, elements.length + 1); + // Shift the remaining elements. + System.arraycopy(newElements, columnIndex, newElements, columnIndex + 1, + elements.length - columnIndex); + // Insert the element at <code>row,column</code>. + newElements[columnIndex] = element; + // Replace the current row with one. + setRowAt(row, newElements); + return; + } + if (columnIndex < 0 || columnIndex >= elements.length) { + throw new RuntimeException("Illegal column number: " + column); + } + elements[columnIndex] = element; + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java new file mode 100644 index 000000000..577f43e17 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ActualKeyboardBuilder.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.keyboard.internal.MoreKeySpec; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.StringUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +/** + * This class builds an actual keyboard for unit test. + */ +public final class ActualKeyboardBuilder extends AbstractKeyboardBuilder<Key> { + // Comparator to sort {@link Key}s from top-left to bottom-right order. + private static final Comparator<Key> ROW_COLUMN_COMPARATOR = new Comparator<Key>() { + @Override + public int compare(final Key lhs, final Key rhs) { + if (lhs.getY() < rhs.getY()) return -1; + if (lhs.getY() > rhs.getY()) return 1; + if (lhs.getX() < rhs.getX()) return -1; + if (lhs.getX() > rhs.getX()) return 1; + return 0; + } + }; + + private static ArrayList<Key> filterOutSpacerAndSortKeys(final Key[] keys) { + final ArrayList<Key> filteredKeys = CollectionUtils.newArrayList(); + for (final Key key : keys) { + if (key.isSpacer()) { + continue; + } + filteredKeys.add(key); + } + Collections.sort(filteredKeys, ROW_COLUMN_COMPARATOR); + return filteredKeys; + } + + /** + * Create the keyboard that consists of the array of rows of the actual keyboard's keys. + * @param keys the array of keys of the actual keyboard. + * @return the actual keyboard grouped with rows. + */ + public static Key[][] buildKeyboard(final Key[] keys) { + // Filter out spacer and sort keys from top-left to bottom-right order to prepare to + // create rows. + final ArrayList<Key> sortedKeys = filterOutSpacerAndSortKeys(keys); + + // Grouping keys into rows. + final ArrayList<ArrayList<Key>> rows = CollectionUtils.newArrayList(); + ArrayList<Key> elements = CollectionUtils.newArrayList(); + int lastY = sortedKeys.get(0).getY(); + for (final Key key : sortedKeys) { + if (lastY != key.getY()) { + // A new row is starting. + lastY = key.getY(); + rows.add(elements); + elements = CollectionUtils.newArrayList(); + } + elements.add(key); + } + rows.add(elements); // Add the last row. + + // Calculate each dimension of rows and create a builder. + final int[] dimensions = new int[rows.size()]; + for (int rowIndex = 0; rowIndex < dimensions.length; rowIndex++) { + dimensions[rowIndex] = rows.get(rowIndex).size(); + } + final ActualKeyboardBuilder builder = new ActualKeyboardBuilder(dimensions); + + for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) { + final int row = rowIndex + 1; + final ArrayList<Key> rowKeys = rows.get(rowIndex); + builder.setRowAt(row, rowKeys.toArray(new Key[rowKeys.size()])); + } + return builder.build(); + } + + private ActualKeyboardBuilder(final int ... dimensions) { + super(dimensions); + } + + @Override + Key defaultElement() { return null; } + + @Override + Key[] newArray(final int size) { return new Key[size]; } + + @Override + Key[][] newArrayOfArray(final int size) { return new Key[size][]; } + + // Helper class to create concise representation from the key specification. + static class MoreKeySpecStringizer extends StringUtils.Stringizer<MoreKeySpec> { + static final MoreKeySpecStringizer STRINGIZER = new MoreKeySpecStringizer(); + + @Override + public String stringize(final MoreKeySpec spec) { + return toString(spec.mLabel, spec.mIconId, spec.mOutputText, spec.mCode); + } + + static String toString(final String label, final int iconId, final String outputText, + final int code) { + final String visual = (iconId != KeyboardIconsSet.ICON_UNDEFINED) + ? KeyboardIconsSet.getIconName(iconId) : label; + final String output; + if (code == Constants.CODE_OUTPUT_TEXT) { + output = outputText; + } else if (code < Constants.CODE_SPACE) { + output = Constants.printableCode(code); + } else { + output = StringUtils.newSingleCodePointString(code); + } + if (visual.equals(output)) { + return visual; + } + return visual + "|" + output; + } + } + + // Helper class to create concise representation from the key. + static class KeyStringizer extends StringUtils.Stringizer<Key> { + static final KeyStringizer STRINGIZER = new KeyStringizer(); + + @Override + public String stringize(final Key key) { + if (key == null) { + return "NULL"; + } + if (key.isSpacer()) { + return "SPACER"; + } + final StringBuilder sb = new StringBuilder(); + sb.append(MoreKeySpecStringizer.toString( + key.getLabel(), key.getIconId(), key.getOutputText(), key.getCode())); + final MoreKeySpec[] moreKeys = key.getMoreKeys(); + if (moreKeys == null) { + return sb.toString(); + } + sb.append("^"); + sb.append(MoreKeySpecStringizer.STRINGIZER.join(moreKeys)); + return sb.toString(); + } + } + + /** + * Convert the key to human readable string. + * @param key the key to be converted to string. + * @return the human readable representation of <code>key</code>. + */ + public static String toString(final Key key) { + return KeyStringizer.STRINGIZER.stringize(key); + } + + /** + * Convert the keyboard row to human readable string. + * @param keys the keyboard row to be converted to string. + * @return the human readable representation of <code>keys</code>. + */ + public static String toString(final Key[] keys) { + return KeyStringizer.STRINGIZER.join(keys); + } + + // Helper class to create concise representation from the array of the key. + static class KeyArrayStringizer extends StringUtils.Stringizer<Key[]> { + static final KeyArrayStringizer STRINGIZER = new KeyArrayStringizer(); + + @Override + public String stringize(final Key[] keyArray) { + return KeyStringizer.STRINGIZER.join(keyArray); + } + } + + /** + * Convert the keyboard to human readable string. + * @param rows the keyboard to be converted to string. + * @return the human readable representation of <code>rows</code>. + */ + public static String toString(final Key[][] rows) { + return KeyArrayStringizer.STRINGIZER.join(rows, "\n" /* delimiter */); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java new file mode 100644 index 000000000..e22d75cd7 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.internal.MoreKeySpec; + +import java.util.Arrays; +import java.util.Locale; + +/** + * This class represents an expected key. + */ +public class ExpectedKey { + static ExpectedKey EMPTY_KEY = newInstance(""); + + // A key that has a string label and may have "more keys". + static ExpectedKey newInstance(final String label, final ExpectedKey ... moreKeys) { + return newInstance(label, label, moreKeys); + } + + // A key that has a string label and a different output text and may have "more keys". + static ExpectedKey newInstance(final String label, final String outputText, + final ExpectedKey ... moreKeys) { + return newInstance(ExpectedKeyVisual.newInstance(label), + ExpectedKeyOutput.newInstance(outputText), moreKeys); + } + + // A key that has a string label and a code point output and may have "more keys". + static ExpectedKey newInstance(final String label, final int code, + final ExpectedKey ... moreKeys) { + return newInstance(ExpectedKeyVisual.newInstance(label), + ExpectedKeyOutput.newInstance(code), moreKeys); + } + + // A key that has an icon and a code point output and may have "more keys". + static ExpectedKey newInstance(final int iconId, final int code, + final ExpectedKey ... moreKeys) { + return newInstance(ExpectedKeyVisual.newInstance(iconId), + ExpectedKeyOutput.newInstance(code), moreKeys); + } + + static ExpectedKey newInstance(final ExpectedKeyVisual visual, final ExpectedKeyOutput output, + final ExpectedKey ... moreKeys) { + if (moreKeys.length == 0) { + return new ExpectedKey(visual, output); + } + return new ExpectedKeyWithMoreKeys(visual, output, moreKeys); + } + + private static final ExpectedKey[] EMPTY_KEYS = new ExpectedKey[0]; + + // The expected visual outlook of this key. + private final ExpectedKeyVisual mVisual; + // The expected output of this key. + private final ExpectedKeyOutput mOutput; + + public final ExpectedKeyVisual getVisual() { + return mVisual; + } + + public final ExpectedKeyOutput getOutput() { + return mOutput; + } + + public ExpectedKey[] getMoreKeys() { + // This key has no "more keys". + return EMPTY_KEYS; + } + + protected ExpectedKey(final ExpectedKeyVisual visual, final ExpectedKeyOutput output) { + mVisual = visual; + mOutput = output; + } + + public ExpectedKey toUpperCase(Locale locale) { + return newInstance(mVisual.toUpperCase(locale), mOutput.toUpperCase(locale)); + } + + public boolean equalsTo(final Key key) { + // This key has no "more keys". + return mVisual.equalsTo(key) && mOutput.equalsTo(key) && key.getMoreKeys() == null; + } + + public boolean equalsTo(final MoreKeySpec moreKeySpec) { + return mVisual.equalsTo(moreKeySpec) && mOutput.equalsTo(moreKeySpec); + } + + @Override + public boolean equals(final Object object) { + if (object instanceof ExpectedKey) { + final ExpectedKey key = (ExpectedKey)object; + return mVisual.equalsTo(key.mVisual) && mOutput.equalsTo(key.mOutput) + && Arrays.equals(getMoreKeys(), key.getMoreKeys()); + } + return false; + } + + private static int hashCode(final Object ... objects) { + return Arrays.hashCode(objects); + } + + @Override + public int hashCode() { + return hashCode(mVisual, mOutput, getMoreKeys()); + } + + @Override + public String toString() { + if (mVisual.equalsTo(mOutput)) { + return mVisual.toString(); + } + return mVisual + "|" + mOutput; + } + + /** + * This class represents an expected key that has "more keys". + */ + private static final class ExpectedKeyWithMoreKeys extends ExpectedKey { + private final ExpectedKey[] mMoreKeys; + + ExpectedKeyWithMoreKeys(final ExpectedKeyVisual visual, + final ExpectedKeyOutput output, final ExpectedKey ... moreKeys) { + super(visual, output); + mMoreKeys = moreKeys; + } + + @Override + public ExpectedKey toUpperCase(final Locale locale) { + final ExpectedKey[] upperCaseMoreKeys = new ExpectedKey[mMoreKeys.length]; + for (int i = 0; i < mMoreKeys.length; i++) { + upperCaseMoreKeys[i] = mMoreKeys[i].toUpperCase(locale); + } + return newInstance(getVisual().toUpperCase(locale), getOutput().toUpperCase(locale), + upperCaseMoreKeys); + } + + @Override + public ExpectedKey[] getMoreKeys() { + return mMoreKeys; + } + + @Override + public boolean equalsTo(final Key key) { + if (getVisual().equalsTo(key) && getOutput().equalsTo(key)) { + final MoreKeySpec[] moreKeys = key.getMoreKeys(); + // This key should have at least one "more key". + if (moreKeys == null || moreKeys.length != mMoreKeys.length) { + return false; + } + for (int index = 0; index < moreKeys.length; index++) { + if (!mMoreKeys[index].equalsTo(moreKeys[index])) { + return false; + } + } + return true; + } + return false; + } + + @Override + public boolean equalsTo(final MoreKeySpec moreKeySpec) { + // MoreKeySpec has no "more keys". + return false; + } + + @Override + public String toString() { + return super.toString() + "^" + Arrays.toString(mMoreKeys); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java new file mode 100644 index 000000000..1be51e60b --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.internal.MoreKeySpec; +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.utils.StringUtils; + +import java.util.Locale; + +/** + * This class represents an expected output of a key. + * + * There are two types of expected output, an integer code point and a string output text. + */ +abstract class ExpectedKeyOutput { + static ExpectedKeyOutput newInstance(final int code) { + return new Code(code); + } + + static ExpectedKeyOutput newInstance(final String outputText) { + // If the <code>outputText</code> is one code point string, use {@link CodePoint} object. + if (StringUtils.codePointCount(outputText) == 1) { + return new Code(outputText.codePointAt(0)); + } + return new Text(outputText); + } + + abstract ExpectedKeyOutput toUpperCase(final Locale locale); + abstract boolean equalsTo(final String text); + abstract boolean equalsTo(final Key key); + abstract boolean equalsTo(final MoreKeySpec moreKeySpec); + abstract boolean equalsTo(final ExpectedKeyOutput output); + + /** + * This class represents an integer code point. + */ + private static class Code extends ExpectedKeyOutput { + // UNICODE code point or a special negative value defined in {@link Constants}. + private final int mCode; + + Code(final int code) { mCode = code; } + + @Override + ExpectedKeyOutput toUpperCase(final Locale locale) { + if (Constants.isLetterCode(mCode)) { + final String codeString = StringUtils.newSingleCodePointString(mCode); + // A letter may have an upper case counterpart that consists of multiple code + // points, for instance the upper case of "ß" is "SS". + return newInstance(codeString.toUpperCase(locale)); + } + // A special negative value has no upper case. + return this; + } + + @Override + boolean equalsTo(final String text) { + return StringUtils.codePointCount(text) == 1 && text.codePointAt(0) == mCode; + } + + @Override + boolean equalsTo(final Key key) { + return mCode == key.getCode(); + } + + @Override + boolean equalsTo(final MoreKeySpec moreKeySpec) { + return mCode == moreKeySpec.mCode; + } + + @Override + boolean equalsTo(final ExpectedKeyOutput output) { + return (output instanceof Code) && mCode == ((Code)output).mCode; + } + + @Override + public String toString() { + return Constants.isLetterCode(mCode) ? StringUtils.newSingleCodePointString(mCode) + : Constants.printableCode(mCode); + } + } + + /** + * This class represents a string output text. + */ + private static class Text extends ExpectedKeyOutput { + private final String mText; + + Text(final String text) { mText = text; } + + @Override + ExpectedKeyOutput toUpperCase(final Locale locale) { + return newInstance(mText.toUpperCase(locale)); + } + + @Override + boolean equalsTo(final String text) { + return text.equals(text); + } + + @Override + boolean equalsTo(final Key key) { + return key.getCode() == Constants.CODE_OUTPUT_TEXT + && mText.equals(key.getOutputText()); + } + + @Override + boolean equalsTo(final MoreKeySpec moreKeySpec) { + return moreKeySpec.mCode == Constants.CODE_OUTPUT_TEXT + && mText.equals(moreKeySpec.mOutputText); + } + + @Override + boolean equalsTo(final ExpectedKeyOutput output) { + return (output instanceof Text) && mText == ((Text)output).mText; + } + + @Override + public String toString() { + return mText; + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java new file mode 100644 index 000000000..0a0da32b6 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.keyboard.internal.MoreKeySpec; + +import java.util.Locale; + +/** + * This class represents an expected visual outlook of a key. + * + * There are two types of expected visual, an integer icon id and a string label. + */ +abstract class ExpectedKeyVisual { + static ExpectedKeyVisual newInstance(final String label) { + return new Label(label); + } + + static ExpectedKeyVisual newInstance(final int iconId) { + return new Icon(iconId); + } + + abstract ExpectedKeyVisual toUpperCase(final Locale locale); + abstract boolean equalsTo(final String text); + abstract boolean equalsTo(final Key key); + abstract boolean equalsTo(final MoreKeySpec moreKeySpec); + abstract boolean equalsTo(final ExpectedKeyOutput output); + abstract boolean equalsTo(final ExpectedKeyVisual visual); + + /** + * This class represents an integer icon id. + */ + private static class Icon extends ExpectedKeyVisual { + private final int mIconId; + + Icon(final int iconId) { + mIconId = iconId; + } + + @Override + ExpectedKeyVisual toUpperCase(final Locale locale) { + return this; + } + + @Override + boolean equalsTo(final String text) { + return false; + } + + @Override + boolean equalsTo(final Key key) { + return mIconId == key.getIconId(); + } + + @Override + boolean equalsTo(final MoreKeySpec moreKeySpec) { + return mIconId == moreKeySpec.mIconId; + } + + @Override + boolean equalsTo(final ExpectedKeyOutput output) { + return false; + } + + @Override + boolean equalsTo(final ExpectedKeyVisual visual) { + return (visual instanceof Icon) && mIconId == ((Icon)visual).mIconId; + } + + @Override + public String toString() { + return KeyboardIconsSet.getIconName(mIconId); + } + } + + /** + * This class represents a string label. + */ + private static class Label extends ExpectedKeyVisual { + private final String mLabel; + + Label(final String label) { mLabel = label; } + + @Override + ExpectedKeyVisual toUpperCase(final Locale locale) { + return new Label(mLabel.toUpperCase(locale)); + } + + @Override + boolean equalsTo(final String text) { + return mLabel.equals(text); + } + + @Override + boolean equalsTo(final Key key) { + return mLabel.equals(key.getLabel()); + } + + @Override + boolean equalsTo(final MoreKeySpec moreKeySpec) { + return mLabel.equals(moreKeySpec.mLabel); + } + + @Override + boolean equalsTo(final ExpectedKeyOutput output) { + return output.equalsTo(mLabel); + } + + @Override + boolean equalsTo(final ExpectedKeyVisual visual) { + return (visual instanceof Label) && mLabel.equals(((Label)visual).mLabel); + } + + @Override + public String toString() { + return mLabel; + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java new file mode 100644 index 000000000..61288f048 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyboardBuilder.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import java.util.Arrays; +import java.util.Locale; + +/** + * This class builds an expected keyboard for unit test. + */ +public final class ExpectedKeyboardBuilder extends AbstractKeyboardBuilder<ExpectedKey> { + public ExpectedKeyboardBuilder(final int ... dimensions) { + super(dimensions); + } + + public ExpectedKeyboardBuilder(final ExpectedKey[][] rows) { + super(rows); + } + + @Override + protected ExpectedKey defaultElement() { + return ExpectedKey.EMPTY_KEY; + } + + @Override + ExpectedKey[] newArray(final int size) { + return new ExpectedKey[size]; + } + + @Override + ExpectedKey[][] newArrayOfArray(final int size) { + return new ExpectedKey[size][]; + } + + @Override + public ExpectedKey[][] build() { + return super.build(); + } + + // A replacement job to be performed. + interface ReplaceJob { + // Returns a {@link ExpectedKey} object to replace. + ExpectedKey replace(final ExpectedKey oldKey); + // Return true if replacing should be stopped at first occurrence. + boolean stopAtFirstOccurrence(); + } + + // Replace key(s) that has the specified visual. + private void replaceKeyOf(final ExpectedKeyVisual visual, final ReplaceJob job) { + int replacedCount = 0; + final ExpectedKey[][] rows = build(); + for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { + final ExpectedKey[] keys = rows[rowIndex]; + for (int columnIndex = 0; columnIndex < keys.length; columnIndex++) { + if (keys[columnIndex].getVisual().equalsTo(visual)) { + keys[columnIndex] = job.replace(keys[columnIndex]); + replacedCount++; + if (job.stopAtFirstOccurrence()) { + return; + } + } + } + } + if (replacedCount == 0) { + throw new RuntimeException( + "Can't find key that has visual: " + visual + " in\n" + toString(rows)); + } + } + + /** + * Set the row with specified keys that have specified labels. + * @param row the row number to set keys. + * @param labels the label texts of the keys. + * @return this builder. + */ + public ExpectedKeyboardBuilder setLabelsOfRow(final int row, final String ... labels) { + final ExpectedKey[] keys = new ExpectedKey[labels.length]; + for (int columnIndex = 0; columnIndex < labels.length; columnIndex++) { + keys[columnIndex] = ExpectedKey.newInstance(labels[columnIndex]); + } + setRowAt(row, keys); + return this; + } + + /** + * Set the "more keys" of the key that has the specified label. + * @param label the label of the key to set the "more keys". + * @param moreKeys the array of labels of the "more keys" to be set. + * @return this builder. + */ + public ExpectedKeyboardBuilder setMoreKeysOf(final String label, final String ... moreKeys) { + final ExpectedKey[] expectedMoreKeys = new ExpectedKey[moreKeys.length]; + for (int index = 0; index < moreKeys.length; index++) { + expectedMoreKeys[index] = ExpectedKey.newInstance(moreKeys[index]); + } + setMoreKeysOf(label, expectedMoreKeys); + return this; + } + + /** + * Set the "more keys" of the key that has the specified label. + * @param label the label of the key to set the "more keys". + * @param moreKeys the array of "more key" to be set. + * @return this builder. + */ + public ExpectedKeyboardBuilder setMoreKeysOf(final String label, + final ExpectedKey ... moreKeys) { + setMoreKeysOf(ExpectedKeyVisual.newInstance(label), moreKeys); + return this; + } + + /** + * Set the "more keys" of the key that has the specified icon. + * @param iconId the icon id of the key to set the "more keys". + * @param moreKeys the array of "more key" to be set. + * @return this builder. + */ + public ExpectedKeyboardBuilder setMoreKeysOf(final int iconId, final ExpectedKey ... moreKeys) { + setMoreKeysOf(ExpectedKeyVisual.newInstance(iconId), moreKeys); + return this; + } + + private void setMoreKeysOf(final ExpectedKeyVisual visual, final ExpectedKey[] moreKeys) { + replaceKeyOf(visual, new ReplaceJob() { + @Override + public ExpectedKey replace(final ExpectedKey oldKey) { + return ExpectedKey.newInstance(oldKey.getVisual(), oldKey.getOutput(), moreKeys); + } + @Override + public boolean stopAtFirstOccurrence() { + return true; + } + }); + } + + /** + * Insert the keys at specified position. + * @param row the row number to insert the <code>keys</code>. + * @param column the column number to insert the <code>keys</code>. + * @param keys the array of keys to insert at <code>row,column</code>. + * @return this builder. + * @throws {@link RuntimeException} if <code>row</code> or <code>column</code> is illegal. + */ + public ExpectedKeyboardBuilder insertKeysAtRow(final int row, final int column, + final ExpectedKey ... keys) { + for (int index = 0; index < keys.length; index++) { + setElementAt(row, column + index, keys[index], true /* insert */); + } + return this; + } + + /** + * Add the keys on the left most of the row. + * @param row the row number to add the <code>keys</code>. + * @param keys the array of keys to add on the left most of the row. + * @return this builder. + * @throws {@link RuntimeException} if <code>row</code> is illegal. + */ + public ExpectedKeyboardBuilder addKeysOnTheLeftOfRow(final int row, + final ExpectedKey ... keys) { + // Keys should be inserted from the last to preserve the order. + for (int index = keys.length - 1; index >= 0; index--) { + setElementAt(row, 1, keys[index], true /* insert */); + } + return this; + } + + /** + * Add the keys on the right most of the row. + * @param row the row number to add the <code>keys</code>. + * @param keys the array of keys to add on the right most of the row. + * @return this builder. + * @throws {@link RuntimeException} if <code>row</code> is illegal. + */ + public ExpectedKeyboardBuilder addKeysOnTheRightOfRow(final int row, + final ExpectedKey ... keys) { + final int rightEnd = getRowAt(row).length + 1; + insertKeysAtRow(row, rightEnd, keys); + return this; + } + + /** + * Replace the most top-left key that has the specified label with the new key. + * @param label the label of the key to set <code>newKey</code>. + * @param newKey the key to be set. + * @return this builder. + */ + public ExpectedKeyboardBuilder replaceKeyOfLabel(final String label, final ExpectedKey newKey) { + final ExpectedKeyVisual visual = ExpectedKeyVisual.newInstance(label); + replaceKeyOf(visual, new ReplaceJob() { + @Override + public ExpectedKey replace(final ExpectedKey oldKey) { + return newKey; + } + @Override + public boolean stopAtFirstOccurrence() { + return true; + } + }); + return this; + } + + /** + * Replace the all specified keys with the new key. + * @param key the key to be replaced by <code>newKey</code>. + * @param newKey the key to be set. + * @return this builder. + */ + public ExpectedKeyboardBuilder replaceKeyOfAll(final ExpectedKey key, + final ExpectedKey newKey) { + replaceKeyOf(key.getVisual(), new ReplaceJob() { + @Override + public ExpectedKey replace(final ExpectedKey oldKey) { + return newKey; + } + @Override + public boolean stopAtFirstOccurrence() { + return false; + } + }); + return this; + } + + /** + * Returns new keyboard instance that has upper case keys of the specified keyboard. + * @param rows the lower case keyboard. + * @param locale the locale used to convert cases. + * @return the upper case keyboard. + */ + public static ExpectedKey[][] toUpperCase(final ExpectedKey[][] rows, final Locale locale) { + final ExpectedKey[][] upperCaseRows = new ExpectedKey[rows.length][]; + for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { + final ExpectedKey[] lowerCaseKeys = rows[rowIndex]; + final ExpectedKey[] upperCaseKeys = new ExpectedKey[lowerCaseKeys.length]; + for (int columnIndex = 0; columnIndex < lowerCaseKeys.length; columnIndex++) { + upperCaseKeys[columnIndex] = lowerCaseKeys[columnIndex].toUpperCase(locale); + } + upperCaseRows[rowIndex] = upperCaseKeys; + } + return upperCaseRows; + } + + /** + * Convert the keyboard to human readable string. + * @param rows the keyboard to be converted to string. + * @return the human readable representation of <code>rows</code>. + */ + public static String toString(final ExpectedKey[][] rows) { + final StringBuilder sb = new StringBuilder(); + for (int rowIndex = 0; rowIndex < rows.length; rowIndex++) { + if (rowIndex > 0) { + sb.append("\n"); + } + sb.append(Arrays.toString(rows[rowIndex])); + } + return sb.toString(); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java new file mode 100644 index 000000000..1aeb8c0cd --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/LayoutBase.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.expected; + +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.latin.Constants; + +/** + * Base class to create an expected keyboard for unit test. + */ +public class LayoutBase { + // Those helper methods have a lower case name to be readable when defining expected keyboard + // layouts. + + // Helper method to create {@link ExpectedKey} object that has the label. + public static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) { + return ExpectedKey.newInstance(label, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object that has the label and the output text. + public static ExpectedKey key(final String label, final String outputText, + final ExpectedKey ... moreKeys) { + return ExpectedKey.newInstance(label, outputText, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object that has the label and the output code. + public static ExpectedKey key(final String label, final int code, + final ExpectedKey ... moreKeys) { + return ExpectedKey.newInstance(label, code, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object that has the icon and the output code. + public static ExpectedKey key(final int iconId, final int code, + final ExpectedKey ... moreKeys) { + return ExpectedKey.newInstance(iconId, code, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object that has new "more keys". + public static ExpectedKey key(final ExpectedKey key, final ExpectedKey ... moreKeys) { + return ExpectedKey.newInstance(key.getVisual(), key.getOutput(), moreKeys); + } + + // Helper method to create {@link ExpectedKey} object for "more key" that has the label. + public static ExpectedKey moreKey(final String label) { + return ExpectedKey.newInstance(label); + } + + // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the + // output text. + public static ExpectedKey moreKey(final String label, final String outputText) { + return ExpectedKey.newInstance(label, outputText); + } + + // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the + // output code. + public static ExpectedKey moreKey(final String label, final int code) { + return ExpectedKey.newInstance(label, code); + } + + // Icon ids. + private static final int ICON_SHIFT = KeyboardIconsSet.getIconId("shift_key"); + private static final int ICON_DELETE = KeyboardIconsSet.getIconId("delete_key"); + private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId("settings_key"); + private static final int ICON_ENTER = KeyboardIconsSet.getIconId("enter_key"); + private static final int ICON_EMOJI = KeyboardIconsSet.getIconId("emoji_key"); + + // Functional keys. + public static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK); + public static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT, Constants.CODE_SHIFT); + public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE); + public static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL); + public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS); + public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER); + public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI); + + // Punctuation more keys for phone form factor. + public static final String[] PHONE_PUNCTUATION_MORE_KEYS = { + ";", "/", "(", ")", "#", "!", ",", "?", + "&", "%", "+", "\"", "-", ":", "'", "@" + }; + + // Punctuation more keys for tablet form factor. + public static final String[] TABLET_PUNCTUATION_MORE_KEYS = { + ";", "/", "(", ")", "#", "'", ",", + "&", "%", "+", "\"", "-", ":", "@" + }; + + private static ExpectedKeyboardBuilder toPhoneAlphabet(final ExpectedKeyboardBuilder builder) { + return builder + .addKeysOnTheLeftOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY)) + .addKeysOnTheRightOfRow(3, DELETE_KEY) + .setLabelsOfRow(4, ",", " ", ".") + .setMoreKeysOf(",", SETTINGS_KEY) + .setMoreKeysOf(".", PHONE_PUNCTUATION_MORE_KEYS) + .addKeysOnTheLeftOfRow(4, SYMBOLS_KEY) + .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + } + + // Helper method to create alphabet layout for tablet by adding special function keys except + // shift key. + public static ExpectedKeyboardBuilder toTabletAlphabetWithoutShiftKeys( + final ExpectedKeyboardBuilder builder) { + return builder + // U+00BF: "¿" INVERTED QUESTION MARK + // U+00A1: "¡" INVERTED EXCLAMATION MARK + .addKeysOnTheRightOfRow(3, + key("!", moreKey("\u00A1")), key("?", moreKey("\u00BF"))) + .addKeysOnTheRightOfRow(1, DELETE_KEY) + .addKeysOnTheRightOfRow(2, ENTER_KEY) + .setLabelsOfRow(4, "/", " ", ",", ".") + .setMoreKeysOf(".", TABLET_PUNCTUATION_MORE_KEYS) + .addKeysOnTheLeftOfRow(4, SYMBOLS_KEY, SETTINGS_KEY) + .addKeysOnTheRightOfRow(4, EMOJI_KEY); + } + + // Helper method to create alphabet layout by adding special function keys. + public static ExpectedKey[][] toCommonAlphabet(final ExpectedKey[][] common, + final boolean isPhone) { + final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(common); + if (isPhone) { + toPhoneAlphabet(builder); + } else { + toTabletAlphabetWithoutShiftKeys(builder); + builder.addKeysOnTheLeftOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY)) + .addKeysOnTheRightOfRow(3, key(SHIFT_KEY, CAPSLOCK_MORE_KEY)); + } + return builder.build(); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java new file mode 100644 index 000000000..427e7de49 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/LayoutTestsBase.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.util.Log; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase; +import com.android.inputmethod.keyboard.layout.AlphabetShifted; +import com.android.inputmethod.keyboard.layout.Symbols; +import com.android.inputmethod.keyboard.layout.SymbolsShifted; +import com.android.inputmethod.keyboard.layout.expected.ActualKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; +import com.android.inputmethod.keyboard.layout.expected.LayoutBase; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.Arrays; +import java.util.Locale; + +/** + * Base class for keyboard layout unit test. + */ +abstract class LayoutTestsBase extends KeyboardLayoutSetTestsBase { + private InputMethodSubtype mSubtype; + private String mLogTag; + private KeyboardLayoutSet mKeyboardLayoutSet; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mSubtype = getSubtype(getTestLocale(), getTestKeyboardLayout()); + mLogTag = SubtypeLocaleUtils.getSubtypeNameForLogging(mSubtype) + "/" + + (isPhone() ? "phone" : "tablet"); + mKeyboardLayoutSet = createKeyboardLayoutSet(mSubtype, null /* editorInfo */); + } + + // Those helper methods have a lower case name to be readable when defining expected keyboard + // layouts. + + // Helper method to create {@link ExpectedKey} object that has the label. + static ExpectedKey key(final String label, final ExpectedKey ... moreKeys) { + return LayoutBase.key(label, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object that has the label and the output text. + static ExpectedKey key(final String label, final String outputText, + final ExpectedKey ... moreKeys) { + return LayoutBase.key(label, outputText, moreKeys); + } + + // Helper method to create {@link ExpectedKey} object for "more key" that has the label. + static ExpectedKey moreKey(final String label) { + return LayoutBase.moreKey(label); + } + + // Helper method to create {@link ExpectedKey} object for "more key" that has the label and the + // output text. + static ExpectedKey moreKey(final String label, final String outputText) { + return LayoutBase.moreKey(label, outputText); + } + + // Locale for testing subtype. + abstract Locale getTestLocale(); + + // Keyboard layout name for testing subtype. + abstract String getTestKeyboardLayout(); + + // Alphabet keyboard for testing subtype. + abstract ExpectedKey[][] getAlphabet(final boolean isPhone); + + // Alphabet automatic shifted keyboard for testing subtype. + ExpectedKey[][] getAlphabetAutomaticShifted(final boolean isPhone) { + return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale()); + } + + // Alphabet manual shifted keyboard for testing subtype. + ExpectedKey[][] getAlphabetManualShifted(final boolean isPhone) { + return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale()); + } + + // Alphabet shift locked keyboard for testing subtype. + ExpectedKey[][] getAlphabetShiftLocked(final boolean isPhone) { + return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale()); + } + + // Alphabet shift lock shifted keyboard for testing subtype. + ExpectedKey[][] getAlphabetShiftLockShifted(final boolean isPhone) { + return AlphabetShifted.getAlphabet(getAlphabet(isPhone), getTestLocale()); + } + + // Symbols keyboard for testing subtype. + ExpectedKey[][] getSymbols(final boolean isPhone) { + return Symbols.getSymbols(isPhone); + } + + // Symbols shifted keyboard for testing subtype. + ExpectedKey[][] getSymbolsShifted(final boolean isPhone) { + return SymbolsShifted.getSymbolsShifted(isPhone); + } + + // TODO: Add phone, phone symbols, number, number password layout tests. + + public final void testAlphabet() { + final int elementId = KeyboardId.ELEMENT_ALPHABET; + doKeyboardTests(elementId, getAlphabet(isPhone())); + } + + public final void testAlphabetAutomaticShifted() { + final int elementId = KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED; + doKeyboardTests(elementId, getAlphabetAutomaticShifted(isPhone())); + } + + public final void testAlphabetManualShifted() { + final int elementId = KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED; + doKeyboardTests(elementId, getAlphabetManualShifted(isPhone())); + } + + public final void testAlphabetShiftLocked() { + final int elementId = KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED; + doKeyboardTests(elementId, getAlphabetShiftLocked(isPhone())); + } + + public final void testAlphabetShiftLockShifted() { + final int elementId = KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED; + doKeyboardTests(elementId, getAlphabetShiftLockShifted(isPhone())); + } + + public final void testSymbols() { + final int elementId = KeyboardId.ELEMENT_SYMBOLS; + doKeyboardTests(elementId, getSymbols(isPhone())); + } + + public final void testSymbolsShifted() { + final int elementId = KeyboardId.ELEMENT_SYMBOLS_SHIFTED; + doKeyboardTests(elementId, getSymbolsShifted(isPhone())); + } + + // Comparing expected keyboard and actual keyboard. + private void doKeyboardTests(final int elementId, final ExpectedKey[][] expectedKeyboard) { + // Skip test if no keyboard is defined. + if (expectedKeyboard == null) { + return; + } + final String tag = mLogTag + "/" + KeyboardId.elementIdToName(elementId); + // Create actual keyboard object. + final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(elementId); + // Create actual keyboard to be compared with the expected keyboard. + final Key[][] actualKeyboard = ActualKeyboardBuilder.buildKeyboard(keyboard.getKeys()); + + // Dump human readable definition of expected/actual keyboards. + Log.d(tag, "expected=\n" + ExpectedKeyboardBuilder.toString(expectedKeyboard)); + Log.d(tag, "actual =\n" + ActualKeyboardBuilder.toString(actualKeyboard)); + // Test both keyboards have the same number of rows. + assertEquals(tag + " labels" + + "\nexpected=" + Arrays.deepToString(expectedKeyboard) + + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard), + expectedKeyboard.length, actualKeyboard.length); + for (int r = 0; r < actualKeyboard.length; r++) { + final int row = r + 1; + // Test both keyboards' rows have the same number of columns. + assertEquals(tag + " labels row=" + row + + "\nexpected=" + Arrays.toString(expectedKeyboard[r]) + + "\nactual =" + ActualKeyboardBuilder.toString(actualKeyboard[r]), + expectedKeyboard[r].length, actualKeyboard[r].length); + for (int c = 0; c < actualKeyboard[r].length; c++) { + final int column = c + 1; + final Key actualKey = actualKeyboard[r][c]; + final ExpectedKey expectedKey = expectedKeyboard[r][c]; + // Test both keyboards' keys have the same visual outlook and key output. + assertTrue(tag + " labels row,column=" + row + "," + column + + "\nexpected=" + expectedKey + + "\nactual =" + ActualKeyboardBuilder.toString(actualKey), + expectedKey.equalsTo(actualKey)); + } + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java new file mode 100644 index 000000000..0792a5789 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishUS.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.keyboard.layout.Qwerty; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; + +import java.util.Locale; + +/** + * en_US: English (United States)/qwerty + */ +@SmallTest +public final class TestsEnglishUS extends LayoutTestsBase { + @Override + Locale getTestLocale() { + return new Locale("en", "US"); + } + + @Override + String getTestKeyboardLayout() { + return "qwerty"; + } + + @Override + ExpectedKey[][] getAlphabet(final boolean isPhone) { + final ExpectedKey[][] keyboard = Qwerty.getAlphabet(isPhone); + final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder(keyboard); + setAccentedLetters(builder); + return builder.build(); + } + + static ExpectedKeyboardBuilder setAccentedLetters(final ExpectedKeyboardBuilder builder) { + return builder + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE + // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX + // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS + // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON + .setMoreKeysOf("e", "3", "\u00E8", "\u00E9", "\u00EA", "\u00EB", "\u0113") + // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX + // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS + // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE + // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + .setMoreKeysOf("u", "7", "\u00FB", "\u00FC", "\u00F9", "\u00FA", "\u016B") + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS + // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE + // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON + // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE + .setMoreKeysOf("i", "8", "\u00EE", "\u00EF", "\u00ED", "\u012B", "\u00EC") + // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE + // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE + // U+0153: "œ" LATIN SMALL LIGATURE OE + // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE + // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON + // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE + .setMoreKeysOf("o", + "9", "\u00F4", "\u00F6", "\u00F2", "\u00F3", "\u0153", "\u00F8", "\u014D", + "\u00F5") + // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE + // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX + // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS + // U+00E6: "æ" LATIN SMALL LETTER AE + // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE + // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE + // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON + .setMoreKeysOf("a", + "\u00E0", "\u00E1", "\u00E2", "\u00E4", "\u00E6", "\u00E3", "\u00E5", + "\u0101") + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + .setMoreKeysOf("s", "\u00DF") + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + .setMoreKeysOf("c", "\u00E7") + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + .setMoreKeysOf("n", "\u00F1"); + } +} diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index b1dd7f653..c0a55626a 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11. +LATINIME_HOST_OSNAME := $(shell uname -s) +ifneq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this + LATINIME_DICTTOOL_AOSP_LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(LATINIME_DICTTOOL_AOSP_LOCAL_PATH) LATINIME_HOST_NATIVE_LIBNAME := liblatinime-aosp-dicttool-host @@ -74,10 +78,14 @@ LOCAL_JAVA_LIBRARIES := junit LOCAL_ADDITIONAL_DEPENDENCIES := $(LATINIME_HOST_NATIVE_LIBNAME) LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_MODULE := dicttool_aosp +LOCAL_IS_HOST_MODULE := true include $(BUILD_HOST_JAVA_LIBRARY) include $(LOCAL_PATH)/etc/Android.mk +endif # Darwin - TODO: Remove this + # Clear our private variables LATINIME_DICTTOOL_AOSP_LOCAL_PATH := LATINIME_LOCAL_DIR := +LATINIME_HOST_OSNAME := diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk index 05e5841d3..95f767dc9 100644 --- a/tools/dicttool/NativeLib.mk +++ b/tools/dicttool/NativeLib.mk @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11. +LATINIME_HOST_OSNAME := $(shell uname -s) +ifneq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this + LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) @@ -33,6 +37,10 @@ endif #HOST_JDK_IS_64BIT_VERSION LOCAL_CFLAGS += -DHOST_TOOL -fPIC -Wno-deprecated LOCAL_NO_DEFAULT_COMPILER_FLAGS := true +# For C++11 +# TODO: Change this to -std=c++11 +LOCAL_CFLAGS += -std=gnu++0x + LATINIME_NATIVE_JNI_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni LATINIME_NATIVE_SRC_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni/src LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(LATINIME_NATIVE_SRC_DIR) @@ -45,9 +53,13 @@ LOCAL_SRC_FILES := \ $(addprefix $(LATINIME_NATIVE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES)) LOCAL_MODULE := $(LATINIME_HOST_NATIVE_LIBNAME) +LOCAL_IS_HOST_MODULE := true include $(BUILD_HOST_SHARED_LIBRARY) +endif # Darwin - TODO: Remove this + # Clear our private variables include $(LOCAL_PATH)/$(LATINIME_NATIVE_JNI_DIR)/CleanupNativeFileList.mk LATINIME_DIR_RELATIVE_TO_DICTTOOL := ../.. +LATINIME_HOST_OSNAME := diff --git a/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml index 4723503f1..4f29f317b 100644 --- a/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-be-rBY/donottranslate-more-keys.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- U+045E: "ў" CYRILLIC SMALL LETTER SHORT U --> <string name="keylabel_for_east_slavic_row1_9">ў</string> - <!-- U+0451: "ё" CYRILLIC SMALL LETTER IO --> - <string name="keylabel_for_east_slavic_row1_12">ё</string> <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> - <string name="keylabel_for_east_slavic_row2_1">ы</string> + <string name="keylabel_for_east_slavic_row2_2">ы</string> <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> <string name="keylabel_for_east_slavic_row2_11">э</string> <!-- U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I --> diff --git a/tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml index 0e953ff21..dcf7480bd 100644 --- a/tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> <string name="keylabel_for_east_slavic_row1_9">щ</string> - <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> - <string name="keylabel_for_east_slavic_row1_12">ъ</string> <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> - <string name="keylabel_for_east_slavic_row2_1">ы</string> + <string name="keylabel_for_east_slavic_row2_2">ы</string> <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> <string name="keylabel_for_east_slavic_row2_11">э</string> <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> @@ -40,7 +38,7 @@ <!-- U+0493: "ғ" CYRILLIC SMALL LETTER GHE WITH STROKE --> <string name="more_keys_for_cyrillic_ghe">ғ</string> <!-- U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I --> - <string name="more_keys_for_east_slavic_row2_1">і</string> + <string name="more_keys_for_east_slavic_row2_2">і</string> <!-- U+04D9: "ә" CYRILLIC SMALL LETTER SCHWA --> <string name="more_keys_for_cyrillic_a">ә</string> <!-- U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O --> diff --git a/tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml index 8d8c5fbf2..1d3922f06 100644 --- a/tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> <string name="keylabel_for_east_slavic_row1_9">щ</string> - <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> - <string name="keylabel_for_east_slavic_row1_12">ъ</string> <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> - <string name="keylabel_for_east_slavic_row2_1">ы</string> + <string name="keylabel_for_east_slavic_row2_2">ы</string> <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> <string name="keylabel_for_east_slavic_row2_11">э</string> <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> diff --git a/tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml index f62c90ffc..44244bc20 100644 --- a/tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> <string name="keylabel_for_east_slavic_row1_9">щ</string> - <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> - <string name="keylabel_for_east_slavic_row1_12">ъ</string> <!-- U+044B: "ы" CYRILLIC SMALL LETTER YERU --> - <string name="keylabel_for_east_slavic_row2_1">ы</string> + <string name="keylabel_for_east_slavic_row2_2">ы</string> <!-- U+044D: "э" CYRILLIC SMALL LETTER E --> <string name="keylabel_for_east_slavic_row2_11">э</string> <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> diff --git a/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml index 6ee34e305..f797255e5 100644 --- a/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml @@ -20,10 +20,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- U+0449: "щ" CYRILLIC SMALL LETTER SHCHA --> <string name="keylabel_for_east_slavic_row1_9">щ</string> - <!-- U+0457: "ї" CYRILLIC SMALL LETTER YI --> - <string name="keylabel_for_east_slavic_row1_12">ї</string> <!-- U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I --> - <string name="keylabel_for_east_slavic_row2_1">і</string> + <string name="keylabel_for_east_slavic_row2_2">і</string> <!-- U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE --> <string name="keylabel_for_east_slavic_row2_11">є</string> <!-- U+0438: "и" CYRILLIC SMALL LETTER I --> @@ -31,7 +29,7 @@ <!-- U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN --> <string name="more_keys_for_cyrillic_ghe">ґ</string> <!-- U+0457: "ї" CYRILLIC SMALL LETTER YI --> - <string name="more_keys_for_east_slavic_row2_1">ї</string> + <string name="more_keys_for_east_slavic_row2_2">ї</string> <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN --> <string name="more_keys_for_cyrillic_soft_sign">ъ</string> <!-- U+20B4: "₴" HRYVNIA SIGN --> 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 9cdcb4668..1ea301810 100644 --- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml @@ -44,15 +44,14 @@ <string name="more_keys_for_nordic_row2_10"></string> <string name="more_keys_for_nordic_row2_11"></string> <string name="keylabel_for_east_slavic_row1_9"></string> - <string name="keylabel_for_east_slavic_row1_12"></string> - <string name="keylabel_for_east_slavic_row2_1"></string> + <string name="keylabel_for_east_slavic_row2_2"></string> <string name="keylabel_for_east_slavic_row2_11"></string> <string name="keylabel_for_east_slavic_row3_5"></string> <string name="more_keys_for_cyrillic_u"></string> <string name="more_keys_for_cyrillic_ka"></string> <string name="more_keys_for_cyrillic_en"></string> <string name="more_keys_for_cyrillic_ghe"></string> - <string name="more_keys_for_east_slavic_row2_1"></string> + <string name="more_keys_for_east_slavic_row2_2"></string> <string name="more_keys_for_cyrillic_a"></string> <string name="more_keys_for_cyrillic_o"></string> <string name="more_keys_for_cyrillic_soft_sign"></string> |