diff options
585 files changed, 14215 insertions, 4179 deletions
diff --git a/dictionaries/ro_wordlist.combined.gz b/dictionaries/ro_wordlist.combined.gz Binary files differnew file mode 100644 index 000000000..829abd9b8 --- /dev/null +++ b/dictionaries/ro_wordlist.combined.gz diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz Binary files differindex acde9bc73..7794f7bed 100644 --- a/dictionaries/ru_wordlist.combined.gz +++ b/dictionaries/ru_wordlist.combined.gz diff --git a/java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java b/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java index 21535e421..21535e421 100644 --- a/java/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java +++ b/java-overridable/src/com/android/inputmethod/compat/AppWorkaroundsHelper.java diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java b/java-overridable/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java index df0e3f0e1..df0e3f0e1 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java +++ b/java-overridable/src/com/android/inputmethod/dictionarypack/DictionaryPackConstants.java diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java b/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java index ed817658e..ed817658e 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java +++ b/java-overridable/src/com/android/inputmethod/dictionarypack/MetadataUriGetter.java diff --git a/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java b/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java new file mode 100644 index 000000000..27b2f5012 --- /dev/null +++ b/java-overridable/src/com/android/inputmethod/latin/SpecialKeyDetector.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.view.KeyEvent; + +final class SpecialKeyDetector { + /** + * Special physical key detector + * @param context a context of this detector. + */ + public SpecialKeyDetector(final Context context) { + } + + /** + * Record a down key event. + * @param keyEvent a down key event. + */ + public void onKeyDown(final KeyEvent keyEvent) { + } + + /** + * Record an up key event. + * @param keyEvent an up key event. + */ + public void onKeyUp(final KeyEvent keyEvent) { + } +} diff --git a/java/src/com/android/inputmethod/latin/about/AboutPreferences.java b/java-overridable/src/com/android/inputmethod/latin/about/AboutPreferences.java index f60b189f1..f60b189f1 100644 --- a/java/src/com/android/inputmethod/latin/about/AboutPreferences.java +++ b/java-overridable/src/com/android/inputmethod/latin/about/AboutPreferences.java diff --git a/java/src/com/android/inputmethod/latin/define/DebugFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/DebugFlags.java index c509e8322..c509e8322 100644 --- a/java/src/com/android/inputmethod/latin/define/DebugFlags.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/DebugFlags.java diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java-overridable/src/com/android/inputmethod/latin/define/JniLibName.java index abfc36d39..abfc36d39 100644 --- a/java/src/com/android/inputmethod/latin/define/JniLibName.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/JniLibName.java diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java index d385cf840..5ab126486 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java +++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java @@ -24,21 +24,12 @@ public final class ProductionFlags { public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; /** - * When true, enable {@link InputMethodService#onUpdateCursorAnchorInfo} callback via - * {@link InputConnection#requestCursorAnchorInfo}. This flag has no effect in API Level 20 - * and prior. In general, this callback provides more detailed positional information, - * even though an explicit support is required by the editor. + * When true, enable {@link InputMethodService#onUpdateCursorAnchorInfo} callback via + * {@link InputConnection#requestUpdateCursorAnchorInfo}. This flag has no effect in API + * Level 20 and prior. In general, this callback provides detailed positional information, + * even though an explicit support is required by the editor. */ - public static final boolean ENABLE_CURSOR_ANCHOR_INFO_CALLBACK = false; - - /** - * When true, enable {@link InputMethodService#onUpdateCursor} callback via - * {@link InputConnection#requestCursorAnchorInfo}. Although this callback has been available - * since API Level 3, the callback has never been used until API Level 20. Thus it may or may - * not work well as expected. Should rely on {@link InputMethodService#onUpdateCursorAnchorInfo} - * whenever possible since it is supposed to be more reliable and accurate. - */ - public static final boolean ENABLE_CURSOR_RECT_CALLBACK = false; + public static final boolean ENABLE_CURSOR_ANCHOR_INFO_CALLBACK = true; /** * Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. diff --git a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java b/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java index 7dc120e06..7dc120e06 100644 --- a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java +++ b/java-overridable/src/com/android/inputmethod/latin/personalization/ContextualDictionaryUpdater.java diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java b/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java index c97a0d232..c97a0d232 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java +++ b/java-overridable/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdater.java diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java index 6543003e8..6543003e8 100644 --- a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java diff --git a/java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java index ec7eaf4a0..0aed41ee4 100644 --- a/java/src/com/android/inputmethod/latin/utils/FeedbackUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/FeedbackUtils.java @@ -20,11 +20,11 @@ import android.content.Context; import android.content.Intent; public class FeedbackUtils { - public static boolean isFeedbackFormSupported() { + public static boolean isHelpAndFeedbackFormSupported() { return false; } - public static void showFeedbackForm(Context context) { + public static void showHelpAndFeedbackForm(Context context) { } public static int getAboutKeyboardTitleResId() { diff --git a/java/src/com/android/inputmethod/latin/utils/FileTransforms.java b/java-overridable/src/com/android/inputmethod/latin/utils/FileTransforms.java index 9f4584ec9..9f4584ec9 100644 --- a/java/src/com/android/inputmethod/latin/utils/FileTransforms.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/FileTransforms.java diff --git a/java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java b/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java index 9ad319da6..9ad319da6 100644 --- a/java/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/MetadataFileUriGetter.java diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java index 79c19d077..79c19d077 100644 --- a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/utils/StatsUtils.java diff --git a/java/Android.mk b/java/Android.mk index b580624d7..0d12c45fe 100644 --- a/java/Android.mk +++ b/java/Android.mk @@ -17,7 +17,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SRC_FILES := $(call all-java-files-under, src ../java-overridable/src) LOCAL_PACKAGE_NAME := LatinIME @@ -25,7 +25,7 @@ LOCAL_CERTIFICATE := shared LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime -LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4 +LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4 jsr305 # Do not compress dictionary files to mmap dict data runtime LOCAL_AAPT_FLAGS := -0 .dict diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index f37f6cc4b..1797dc92c 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -33,7 +33,6 @@ <application android:label="@string/english_ime_name" android:icon="@drawable/ic_launcher_keyboard" - android:killAfterRestore="false" android:supportsRtl="true" android:allowBackup="true"> @@ -82,6 +81,7 @@ <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.USER_INITIALIZE" /> + <action android:name="android.intent.action.LOCALE_CHANGED" /> </intent-filter> </receiver> diff --git a/java/res/anim/key_preview_dismiss_holo.xml b/java/res/anim/key_preview_dismiss_holo.xml new file mode 100644 index 000000000..0bf725435 --- /dev/null +++ b/java/res/anim/key_preview_dismiss_holo.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:propertyName="scaleX" + android:duration="53" + android:valueFrom="1.00" + android:valueTo="0.94" /> + <objectAnimator + android:propertyName="scaleY" + android:duration="53" + android:valueFrom="1.00" + android:valueTo="0.94" /> +</set> diff --git a/java/res/anim/key_preview_dismiss_lxx.xml b/java/res/anim/key_preview_dismiss_lxx.xml new file mode 100644 index 000000000..326e534ec --- /dev/null +++ b/java/res/anim/key_preview_dismiss_lxx.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:propertyName="scaleX" + android:duration="53" + android:valueFrom="1.00" + android:valueTo="1.00" /> + <objectAnimator + android:propertyName="scaleY" + android:duration="53" + android:valueFrom="1.00" + android:valueTo="0.94" /> +</set> diff --git a/java/res/anim/key_preview_show_up_holo.xml b/java/res/anim/key_preview_show_up_holo.xml new file mode 100644 index 000000000..ad2e413a1 --- /dev/null +++ b/java/res/anim/key_preview_show_up_holo.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:propertyName="scaleX" + android:duration="17" + android:valueFrom="0.98" + android:valueTo="1.00" /> + <objectAnimator + android:propertyName="scaleY" + android:duration="17" + android:valueFrom="0.98" + android:valueTo="1.00" /> +</set> diff --git a/java/res/anim/key_preview_show_up_lxx.xml b/java/res/anim/key_preview_show_up_lxx.xml new file mode 100644 index 000000000..f5003499c --- /dev/null +++ b/java/res/anim/key_preview_show_up_lxx.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <objectAnimator + android:propertyName="scaleX" + android:duration="17" + android:valueFrom="1.00" + android:valueTo="1.00" /> + <objectAnimator + android:propertyName="scaleY" + android:duration="17" + android:valueFrom="0.98" + android:valueTo="1.00" /> +</set> diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differindex bbdc41116..55ef3e7f5 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_light.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_light.9.png Binary files differindex 854c849cb..9f307dc7f 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_light.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_lxx_light.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differindex 33f7d8077..17a77cccb 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_light.9.png b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_light.9.png Binary files differindex 7a7e98297..55ef3e7f5 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_light.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_active_pressed_lxx_light.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png Binary files differindex 32311666f..32311666f 100644 --- a/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_light.9.png b/java/res/drawable-hdpi/btn_keyboard_spacebar_normal_lxx_light.9.png Binary files differindex 1256b8b21..1256b8b21 100644 --- a/java/res/drawable-hdpi/sym_keyboard_spacebar_lxx_light.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_spacebar_normal_lxx_light.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..cd0d884c6 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png b/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..c05605fbf --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png diff --git a/java/res/drawable-hdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-hdpi/ic_add_circle_wht_24dp.png Binary files differnew file mode 100644 index 000000000..314c52137 --- /dev/null +++ b/java/res/drawable-hdpi/ic_add_circle_wht_24dp.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_dark.png Binary files differindex 47e673afe..e1c713a01 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_light.png Binary files differindex ad933ca01..b1fe2c0b1 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_people_activated_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_dark.png Binary files differindex a894c60b0..6369ac9a9 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_light.png Binary files differindex 2d8bdf884..dbb111ffc 100644 --- a/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_people_normal_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_dark.png Binary files differindex c9b81e1de..c4d2115e2 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_light.png Binary files differindex 1c031c0bc..b2b068763 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_places_activated_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_dark.png Binary files differindex 36fbf9150..53f13d949 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_light.png Binary files differindex 3bd317c19..90cc77d88 100644 --- a/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_places_normal_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_dark.png Binary files differindex 43d3f304c..5f23ec230 100644 --- a/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_light.png Binary files differindex 3a6b3acff..6566ba89b 100644 --- a/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_activated_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_dark.png b/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_dark.png Binary files differindex a07f6069c..6da194c10 100644 --- a/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_dark.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_dark.png diff --git a/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_light.png b/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_light.png Binary files differindex d86c1b98a..b50eba746 100644 --- a/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_light.png +++ b/java/res/drawable-hdpi/ic_emoji_recents_normal_lxx_light.png diff --git a/java/res/drawable-hdpi/ic_launcher_keyboard.png b/java/res/drawable-hdpi/ic_launcher_keyboard.png Binary files differindex 7ae00ed3f..3a01e61b9 100644 --- a/java/res/drawable-hdpi/ic_launcher_keyboard.png +++ b/java/res/drawable-hdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-hdpi/keyboard_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..e34c5666b --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_background_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..bf5450cc7 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_background_lxx_light.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differindex 306e4554c..93f300a13 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png Binary files differindex 867f5516d..e7c516a0e 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_lxx_light.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differindex b3e6ee784..b56fcf910 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png Binary files differindex 827f80f13..32005c37b 100644 --- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png +++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_lxx_light.9.png diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_dark.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..3f6a943b2 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_light.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..107cfc77d --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_suggest_strip_lxx_light.9.png diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex 2cfe1d406..b1fe2c0b1 100644 --- a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-hdpi/sym_keyboard_space_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_space_lxx_dark.png Binary files differnew file mode 100644 index 000000000..9ff640224 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_space_lxx_dark.png diff --git a/java/res/drawable-hdpi/sym_keyboard_space_lxx_light.png b/java/res/drawable-hdpi/sym_keyboard_space_lxx_light.png Binary files differnew file mode 100644 index 000000000..d43e4dd60 --- /dev/null +++ b/java/res/drawable-hdpi/sym_keyboard_space_lxx_light.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differindex 787ce45ce..765191881 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_light.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_light.9.png Binary files differindex e7a585b70..8131549e5 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_light.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_lxx_light.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differindex 7e34c6cf1..dcf814ada 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_light.9.png b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_light.9.png Binary files differindex 5a6513392..765191881 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_light.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_active_pressed_lxx_light.9.png diff --git a/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png Binary files differindex b8e56dab4..b8e56dab4 100644 --- a/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_light.9.png b/java/res/drawable-mdpi/btn_keyboard_spacebar_normal_lxx_light.9.png Binary files differindex 281f83062..281f83062 100644 --- a/java/res/drawable-mdpi/sym_keyboard_spacebar_lxx_light.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_spacebar_normal_lxx_light.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..cb2ca06d3 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png b/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..653da974c --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png diff --git a/java/res/drawable-mdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-mdpi/ic_add_circle_wht_24dp.png Binary files differnew file mode 100644 index 000000000..11363b173 --- /dev/null +++ b/java/res/drawable-mdpi/ic_add_circle_wht_24dp.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_dark.png Binary files differindex 952b570f8..bb37a63b1 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_light.png Binary files differindex d0d72dbdb..e639416cd 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_people_activated_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_dark.png Binary files differindex 1f46b9b7e..a7f816064 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_light.png Binary files differindex 8b7c1a385..d4b114c3e 100644 --- a/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_people_normal_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_dark.png Binary files differindex fadb7511f..630836b8a 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_light.png Binary files differindex 5c0e40d57..2ba430120 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_places_activated_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_dark.png Binary files differindex 5eed3d9c8..8bc3503ca 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_light.png Binary files differindex fff799823..b033c83de 100644 --- a/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_places_normal_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_dark.png Binary files differindex e6c8dc0f2..b0b683bc5 100644 --- a/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_light.png Binary files differindex 25ac3f5d9..39c22c41f 100644 --- a/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_activated_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_dark.png b/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_dark.png Binary files differindex e660891fc..b0138f5c2 100644 --- a/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_dark.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_dark.png diff --git a/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_light.png b/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_light.png Binary files differindex 614d081ce..99e788463 100644 --- a/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_light.png +++ b/java/res/drawable-mdpi/ic_emoji_recents_normal_lxx_light.png diff --git a/java/res/drawable-mdpi/ic_launcher_keyboard.png b/java/res/drawable-mdpi/ic_launcher_keyboard.png Binary files differindex cc73f3be1..574da25f5 100644 --- a/java/res/drawable-mdpi/ic_launcher_keyboard.png +++ b/java/res/drawable-mdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-mdpi/keyboard_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..012d91455 --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_background_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..91adfac47 --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_background_lxx_light.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differindex 4f6731f60..1a7dd34ed 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png Binary files differindex 14da5f919..268863e68 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_lxx_light.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differindex 9bca991b2..08d67ef6a 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png Binary files differindex 14f4b5f24..d0e46b14e 100644 --- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png +++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_lxx_light.9.png diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_dark.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..d85ca712a --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_light.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..983a6956c --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_suggest_strip_lxx_light.9.png diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex ff49d58e1..e639416cd 100644 --- a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-mdpi/sym_keyboard_space_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_space_lxx_dark.png Binary files differnew file mode 100644 index 000000000..9119a9d91 --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_space_lxx_dark.png diff --git a/java/res/drawable-mdpi/sym_keyboard_space_lxx_light.png b/java/res/drawable-mdpi/sym_keyboard_space_lxx_light.png Binary files differnew file mode 100644 index 000000000..6c495beed --- /dev/null +++ b/java/res/drawable-mdpi/sym_keyboard_space_lxx_light.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differindex 47264065a..0f3d3196b 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_light.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_light.9.png Binary files differindex dca7a3274..cef6d00c0 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_light.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differindex 8063fcd82..b5768e629 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png Binary files differindex fa32a2c6e..0f3d3196b 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png Binary files differindex ab032dc17..ab032dc17 100644 --- a/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_light.9.png b/java/res/drawable-xhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png Binary files differindex c1ed863f8..c1ed863f8 100644 --- a/java/res/drawable-xhdpi/sym_keyboard_spacebar_lxx_light.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..c3428bede --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png b/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..f795ee9af --- /dev/null +++ b/java/res/drawable-xhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-xhdpi/ic_add_circle_wht_24dp.png Binary files differnew file mode 100644 index 000000000..32a5b05ba --- /dev/null +++ b/java/res/drawable-xhdpi/ic_add_circle_wht_24dp.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_dark.png Binary files differindex cf2aeb591..538796ed1 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_light.png Binary files differindex 3ecf9d010..0d8c33bcd 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_activated_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_dark.png Binary files differindex b0a448ae0..dbde6b0d7 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_light.png Binary files differindex d71bc1cbb..aeecdee3e 100644 --- a/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_people_normal_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_dark.png Binary files differindex 33f4e0a1f..607ad8408 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_light.png Binary files differindex 194f49332..fe3f0eec5 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_activated_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_dark.png Binary files differindex 82d4ce613..bbec72f34 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_light.png Binary files differindex e1b90a919..e3f201d10 100644 --- a/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_places_normal_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_dark.png Binary files differindex 330573753..66d9c8e9f 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_light.png Binary files differindex 8c74847a2..2a97d59bd 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_activated_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_dark.png b/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_dark.png Binary files differindex b9c1a659b..f60bba4c1 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_dark.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_dark.png diff --git a/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_light.png b/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_light.png Binary files differindex 64e1c4df5..a13db7443 100644 --- a/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_light.png +++ b/java/res/drawable-xhdpi/ic_emoji_recents_normal_lxx_light.png diff --git a/java/res/drawable-xhdpi/ic_launcher_keyboard.png b/java/res/drawable-xhdpi/ic_launcher_keyboard.png Binary files differindex f2ac50dfe..17695015a 100644 --- a/java/res/drawable-xhdpi/ic_launcher_keyboard.png +++ b/java/res/drawable-xhdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-xhdpi/keyboard_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..1c3a38e10 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_background_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..a7f18b5c8 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_background_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differindex 654ccd10c..f934e33bd 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png Binary files differindex c566e3d7c..f70599e99 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differindex f5f613caa..fa5ddf2b6 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png Binary files differindex 35aaa7d7c..0da8919b2 100644 --- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png +++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_dark.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..ac20faf80 --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_light.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..7ccd1ea9d --- /dev/null +++ b/java/res/drawable-xhdpi/keyboard_suggest_strip_lxx_light.9.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex df3eba750..0d8c33bcd 100644 --- a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_space_lxx_dark.png Binary files differnew file mode 100644 index 000000000..2cab2d2fd --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_space_lxx_dark.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_lxx_light.png b/java/res/drawable-xhdpi/sym_keyboard_space_lxx_light.png Binary files differnew file mode 100644 index 000000000..621eec69a --- /dev/null +++ b/java/res/drawable-xhdpi/sym_keyboard_space_lxx_light.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differindex f49239794..927d87b58 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_light.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_light.9.png Binary files differindex 1f5f922dc..ce84bde4c 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_light.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differindex 65e455c7b..5b0854e46 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png Binary files differindex 031014360..927d87b58 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png Binary files differindex 0339de37d..0339de37d 100644 --- a/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_light.9.png b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png Binary files differindex b57cfb3d2..b57cfb3d2 100644 --- a/java/res/drawable-xxhdpi/sym_keyboard_spacebar_lxx_light.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..8e74c6723 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..1ca1ae3e9 --- /dev/null +++ b/java/res/drawable-xxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/ic_add_circle_wht_24dp.png b/java/res/drawable-xxhdpi/ic_add_circle_wht_24dp.png Binary files differnew file mode 100644 index 000000000..a22c463f9 --- /dev/null +++ b/java/res/drawable-xxhdpi/ic_add_circle_wht_24dp.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_dark.png Binary files differindex 9ca031a5a..47f655470 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_light.png Binary files differindex 6faad5c80..ef5bf5a6f 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_activated_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_dark.png Binary files differindex ed71326a9..8ebecb27a 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_light.png Binary files differindex a5516fa3a..d6369d9cc 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_people_normal_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_dark.png Binary files differindex 513198227..6ba4c3a0c 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_light.png Binary files differindex 8a4614d73..b7531321a 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_activated_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_dark.png Binary files differindex 0dfbadd66..d64581411 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_light.png Binary files differindex 2f22dfbb5..95b111d5a 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_places_normal_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_dark.png Binary files differindex 28402b8c8..1501a07ac 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_light.png Binary files differindex 92da7f2bb..dcf876a57 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_activated_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_dark.png b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_dark.png Binary files differindex 24561f9fc..5aeb79a03 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_dark.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_light.png b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_light.png Binary files differindex 96ff801b2..7b8b003df 100644 --- a/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_light.png +++ b/java/res/drawable-xxhdpi/ic_emoji_recents_normal_lxx_light.png diff --git a/java/res/drawable-xxhdpi/ic_launcher_keyboard.png b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png Binary files differindex df386e827..624c82efd 100644 --- a/java/res/drawable-xxhdpi/ic_launcher_keyboard.png +++ b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-xxhdpi/keyboard_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..8b8917012 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_background_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..847df8a6e --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_background_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differindex f5215bc56..1b92455e9 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png Binary files differindex b565ff07c..3effde383 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differindex 6d931ed42..55d633098 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png Binary files differindex 2c5ced966..4523be965 100644 --- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png +++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_dark.9.png b/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..2de0c9c42 --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_light.9.png b/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..8b495f39b --- /dev/null +++ b/java/res/drawable-xxhdpi/keyboard_suggest_strip_lxx_light.9.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex 08d4f8adc..ef5bf5a6f 100644 --- a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f05d7d238 --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_dark.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_light.png b/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_light.png Binary files differnew file mode 100644 index 000000000..94be9067c --- /dev/null +++ b/java/res/drawable-xxhdpi/sym_keyboard_space_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..eac447583 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..16d1f687f --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..78923a887 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..e57e80d05 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_active_pressed_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..0b3d796e4 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..6edd4e3d5 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_off_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..61a5efc2f --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..c60a23547 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_normal_on_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..842c6858d --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..6b033067f --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_popup_selected_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..276065e3b --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..e64147f4f --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_off_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..c1300140c --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..e433f5669 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_key_pressed_on_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..ee4d16b64 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..14cba3c24 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_normal_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..671c31f08 --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..8a6f32afe --- /dev/null +++ b/java/res/drawable-xxxhdpi/btn_keyboard_spacebar_pressed_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/ic_add_circle_white_24dp.png b/java/res/drawable-xxxhdpi/ic_add_circle_white_24dp.png Binary files differnew file mode 100644 index 000000000..a0116fafe --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_add_circle_white_24dp.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..0198bce94 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..f530ba139 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..d9022bbe3 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..89dc4cd92 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_emoticons_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..efbf51c11 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..95355c635 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_nature_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f5531ea11 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..b5085cb6a --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_nature_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..730f75d94 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..f4a250ddd --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_objects_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..4658ceac0 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..7b27829e7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_objects_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..b70f07ac5 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..7e052080b --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_people_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..c960d15e4 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..44325cf35 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_people_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..bca6bbaa7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..8f340d2df --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_places_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..a06e1d858 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..b247768f7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_places_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..3508374ba --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..82a029ee7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_recents_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..6797d7b31 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..6b622ac6d --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_recents_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_dark.png Binary files differnew file mode 100644 index 000000000..51336e93a --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_light.png Binary files differnew file mode 100644 index 000000000..2ab8fa6a7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_symbols_activated_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_dark.png b/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_dark.png Binary files differnew file mode 100644 index 000000000..e02ad6175 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_light.png b/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_light.png Binary files differnew file mode 100644 index 000000000..b17f06674 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_emoji_symbols_normal_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png b/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png Binary files differnew file mode 100644 index 000000000..39636a1b7 --- /dev/null +++ b/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png diff --git a/java/res/drawable-xxxhdpi/keyboard_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..a7dd5378b --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_background_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..ef7ab2097 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_background_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..0e08b6b95 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..fb10523d2 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_background_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..fd88668bb --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..ab1bfad7d --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_key_feedback_more_background_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..3489a9c4c --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..2e3797263 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_popup_panel_background_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_dark.9.png b/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_dark.9.png Binary files differnew file mode 100644 index 000000000..098fa0652 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_dark.9.png diff --git a/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_light.9.png b/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_light.9.png Binary files differnew file mode 100644 index 000000000..c1c48c973 --- /dev/null +++ b/java/res/drawable-xxxhdpi/keyboard_suggest_strip_lxx_light.9.png diff --git a/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_dark.png b/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_dark.png Binary files differnew file mode 100644 index 000000000..0dc783d7c --- /dev/null +++ b/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_light.png b/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_light.png Binary files differnew file mode 100644 index 000000000..f3162e422 --- /dev/null +++ b/java/res/drawable-xxxhdpi/suggestions_strip_divider_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_dark.png Binary files differnew file mode 100644 index 000000000..c8a064d87 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_light.png Binary files differnew file mode 100644 index 000000000..2d2e6e158 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_delete_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_dark.png Binary files differnew file mode 100644 index 000000000..27426da97 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_light.png Binary files differnew file mode 100644 index 000000000..4b1a69f5b --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_done_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_dark.png Binary files differnew file mode 100644 index 000000000..79d3eef5d --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_light.png Binary files differnew file mode 100644 index 000000000..a87e24028 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_go_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_dark.png Binary files differnew file mode 100644 index 000000000..26f361540 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_light.png Binary files differnew file mode 100644 index 000000000..93efb378f --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_language_switch_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_dark.png Binary files differnew file mode 100644 index 000000000..27bf68941 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_light.png Binary files differnew file mode 100644 index 000000000..c62410429 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_next_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_dark.png Binary files differnew file mode 100644 index 000000000..50e0a31d2 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_light.png Binary files differnew file mode 100644 index 000000000..3f4424927 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_previous_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_dark.png Binary files differnew file mode 100644 index 000000000..c10107125 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_light.png Binary files differnew file mode 100644 index 000000000..54e7fb05b --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_return_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_dark.png Binary files differnew file mode 100644 index 000000000..8f7dfcefc --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_light.png Binary files differnew file mode 100644 index 000000000..07d555149 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_search_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_dark.png Binary files differnew file mode 100644 index 000000000..9c12ec29f --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_light.png Binary files differnew file mode 100644 index 000000000..fc6882833 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_send_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_dark.png Binary files differnew file mode 100644 index 000000000..d728b8308 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_light.png Binary files differnew file mode 100644 index 000000000..1351710ad --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_settings_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_dark.png Binary files differnew file mode 100644 index 000000000..e75d556b4 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_light.png Binary files differnew file mode 100644 index 000000000..00521bf09 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_shift_locked_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_dark.png Binary files differnew file mode 100644 index 000000000..f47bf66e2 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_light.png Binary files differnew file mode 100644 index 000000000..fdb6cd77a --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_shift_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_dark.png Binary files differnew file mode 100644 index 000000000..26fd4e7ef --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_light.png Binary files differnew file mode 100644 index 000000000..7b0467aa2 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_dark.png Binary files differnew file mode 100644 index 000000000..1f6c92da0 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_light.png Binary files differnew file mode 100644 index 000000000..656cf99ca --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_space_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_dark.png Binary files differnew file mode 100644 index 000000000..89884698c --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_light.png Binary files differnew file mode 100644 index 000000000..aeedba6da --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_tab_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_dark.png Binary files differnew file mode 100644 index 000000000..9d5b73307 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_light.png Binary files differnew file mode 100644 index 000000000..9db910daa --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_voice_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_dark.png Binary files differnew file mode 100644 index 000000000..e233d0993 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_light.png Binary files differnew file mode 100644 index 000000000..7e0a964dc --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_voice_off_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_dark.png Binary files differnew file mode 100644 index 000000000..94de9165b --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_light.png Binary files differnew file mode 100644 index 000000000..2b13ba737 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_zwj_lxx_light.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_dark.png b/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_dark.png Binary files differnew file mode 100644 index 000000000..134bca67c --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_dark.png diff --git a/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_light.png b/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_light.png Binary files differnew file mode 100644 index 000000000..31cf75bb3 --- /dev/null +++ b/java/res/drawable-xxxhdpi/sym_keyboard_zwnj_lxx_light.png diff --git a/java/res/drawable/btn_keyboard_key_popup_action_lxx_dark.xml b/java/res/drawable/btn_keyboard_key_popup_action_lxx_dark.xml new file mode 100644 index 000000000..8b637f204 --- /dev/null +++ b/java/res/drawable/btn_keyboard_key_popup_action_lxx_dark.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_active_pressed_lxx_dark" /> + <item android:drawable="@drawable/btn_keyboard_key_active_lxx_dark" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_key_popup_action_lxx_light.xml b/java/res/drawable/btn_keyboard_key_popup_action_lxx_light.xml new file mode 100644 index 000000000..67fc52144 --- /dev/null +++ b/java/res/drawable/btn_keyboard_key_popup_action_lxx_light.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_active_pressed_lxx_light" /> + <item android:drawable="@drawable/btn_keyboard_key_active_lxx_light" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml b/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml new file mode 100644 index 000000000..d6cd2b831 --- /dev/null +++ b/java/res/drawable/btn_keyboard_key_popup_lxx_light.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" + android:drawable="@drawable/btn_keyboard_key_popup_selected_lxx_light" /> + <item android:drawable="@android:color/transparent" /> +</selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml b/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml index 5c595d9ed..e930e40e4 100644 --- a/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml +++ b/java/res/drawable/btn_keyboard_spacebar_lxx_dark.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" - android:drawable="@color/key_background_pressed_lxx_dark" /> - <item android:drawable="@color/key_background_lxx_dark" /> + android:drawable="@drawable/btn_keyboard_spacebar_pressed_lxx_dark" /> + <item android:drawable="@drawable/btn_keyboard_spacebar_normal_lxx_dark" /> </selector> diff --git a/java/res/drawable/btn_keyboard_spacebar_lxx_light.xml b/java/res/drawable/btn_keyboard_spacebar_lxx_light.xml index acd19fda4..2b059938d 100644 --- a/java/res/drawable/btn_keyboard_spacebar_lxx_light.xml +++ b/java/res/drawable/btn_keyboard_spacebar_lxx_light.xml @@ -16,6 +16,6 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" - android:drawable="@color/key_background_pressed_lxx_light" /> - <item android:drawable="@color/key_background_lxx_light" /> + android:drawable="@drawable/btn_keyboard_spacebar_pressed_lxx_light" /> + <item android:drawable="@drawable/btn_keyboard_spacebar_normal_lxx_light" /> </selector> diff --git a/java/res/layout/additional_subtype_dialog.xml b/java/res/layout/additional_subtype_dialog.xml index f97c006d6..b7804f5df 100644 --- a/java/res/layout/additional_subtype_dialog.xml +++ b/java/res/layout/additional_subtype_dialog.xml @@ -18,39 +18,60 @@ */ --> -<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:columnCount="2" +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" - android:padding="8dip"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="left|center_vertical" - style="?android:attr/textAppearanceSmall" - android:text="@string/subtype_locale" /> - <Spinner - android:id="@+id/subtype_locale_spinner" - android:layout_width="wrap_content" - android:layout_marginLeft="8dip" - android:layout_marginBottom="8dip" - android:layout_marginTop="8dip" - android:layout_gravity="fill_horizontal|center_vertical" - android:prompt="@string/subtype_locale" /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="left|center_vertical" - style="?android:attr/textAppearanceSmall" - android:text="@string/keyboard_layout_set" /> - <Spinner - android:id="@+id/keyboard_layout_set_spinner" - android:layout_width="wrap_content" - android:layout_marginLeft="8dip" - android:layout_marginBottom="8dip" - android:layout_marginTop="8dip" - android:layout_gravity="fill_horizontal|center_vertical" - android:prompt="@string/keyboard_layout_set" /> -</GridLayout> + android:padding="16dip"> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="0dp" + android:layout_weight="30" + android:layout_height="wrap_content" + android:layout_gravity="start|center_vertical" + android:gravity="start|left" + android:textAlignment="viewStart" + style="?android:attr/textAppearanceSmall" + android:text="@string/subtype_locale" /> + <Spinner + android:id="@+id/subtype_locale_spinner" + android:spinnerMode="dialog" + android:layout_width="0dp" + android:layout_weight="70" + android:layout_height="wrap_content" + android:layout_marginLeft="8dip" + android:layout_marginBottom="8dip" + android:layout_marginTop="8dip" + android:layout_gravity="fill_horizontal|center_vertical" + android:gravity="start|left" + android:prompt="@string/subtype_locale" /> + </LinearLayout> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="0dp" + android:layout_weight="30" + android:layout_height="wrap_content" + android:layout_gravity="start|center_vertical" + android:textAlignment="viewStart" + style="?android:attr/textAppearanceSmall" + android:text="@string/keyboard_layout_set" /> + <Spinner + android:id="@+id/keyboard_layout_set_spinner" + android:spinnerMode="dialog" + android:layout_width="0dp" + android:layout_weight="70" + android:layout_height="wrap_content" + android:layout_marginLeft="8dip" + android:layout_marginBottom="8dip" + android:layout_marginTop="8dip" + android:layout_gravity="fill_horizontal|center_vertical" + android:gravity="start|left" + android:prompt="@string/keyboard_layout_set" /> + </LinearLayout> +</LinearLayout> diff --git a/java/res/layout/emoji_palettes_view.xml b/java/res/layout/emoji_palettes_view.xml index a6ea38ba4..26cc042ab 100644 --- a/java/res/layout/emoji_palettes_view.xml +++ b/java/res/layout/emoji_palettes_view.xml @@ -20,10 +20,10 @@ <com.android.inputmethod.keyboard.emoji.EmojiPalettesView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/emoji_keyboard_view" - android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:orientation="vertical" style="?attr/emojiPalettesViewStyle" > <LinearLayout diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml index ff0b403d1..46551f63f 100644 --- a/java/res/layout/input_view.xml +++ b/java/res/layout/input_view.xml @@ -21,38 +21,11 @@ <com.android.inputmethod.latin.InputView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="bottom|center_horizontal" - android:orientation="vertical" > - <!-- The height of key_preview_backing view will automatically be determined by code. --> - <View - android:id="@+id/key_preview_backing" - android:layout_width="match_parent" - android:layout_height="0dp" /> - <LinearLayout + android:layout_height="wrap_content"> + <include android:id="@+id/main_keyboard_frame" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" > - - <!-- To ensure that key preview popup is correctly placed when the current system locale is - one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. --> - <com.android.inputmethod.latin.suggestions.SuggestionStripView - android:id="@+id/suggestion_strip_view" - android:layoutDirection="ltr" - android:layout_width="match_parent" - android:layout_height="@dimen/config_suggestions_strip_height" - android:gravity="center_vertical" - style="?attr/suggestionStripViewStyle" /> - - <!-- To ensure that key preview popup is correctly placed when the current system locale is - one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. --> - <com.android.inputmethod.keyboard.MainKeyboardView - android:id="@+id/keyboard_view" - android:layoutDirection="ltr" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - </LinearLayout> + layout="@layout/main_keyboard_frame" /> <include + android:id="@+id/emoji_palettes_view" layout="@layout/emoji_palettes_view" /> </com.android.inputmethod.latin.InputView> diff --git a/java/res/layout/main_keyboard_frame.xml b/java/res/layout/main_keyboard_frame.xml new file mode 100644 index 000000000..ebf746679 --- /dev/null +++ b/java/res/layout/main_keyboard_frame.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:orientation="vertical" > + + <!-- To ensure that key preview popup is correctly placed when the current system locale is + one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. --> + <com.android.inputmethod.latin.suggestions.SuggestionStripView + android:id="@+id/suggestion_strip_view" + android:layoutDirection="ltr" + android:layout_width="match_parent" + android:layout_height="@dimen/config_suggestions_strip_height" + android:gravity="center_vertical" + style="?attr/suggestionStripViewStyle" /> + + <!-- To ensure that key preview popup is correctly placed when the current system locale is + one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. --> + <com.android.inputmethod.keyboard.MainKeyboardView + android:id="@+id/keyboard_view" + android:layoutDirection="ltr" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> +</LinearLayout> diff --git a/java/res/layout/more_keys_keyboard.xml b/java/res/layout/more_keys_keyboard.xml index f3795afdc..449c00f92 100644 --- a/java/res/layout/more_keys_keyboard.xml +++ b/java/res/layout/more_keys_keyboard.xml @@ -27,5 +27,6 @@ <com.android.inputmethod.keyboard.MoreKeysKeyboardView android:id="@+id/more_keys_keyboard_view" android:layout_width="wrap_content" - android:layout_height="wrap_content" /> + android:layout_height="wrap_content" + style="?attr/moreKeysKeyboardViewStyle" /> </LinearLayout> diff --git a/java/res/xml-sw600dp/key_question_exclamation.xml b/java/res/layout/more_keys_keyboard_for_action_lxx.xml index edee5c5dd..d23faa4f0 100644 --- a/java/res/xml-sw600dp/key_question_exclamation.xml +++ b/java/res/layout/more_keys_keyboard_for_action_lxx.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2012, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -18,22 +18,15 @@ */ --> -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" > - <switch> - <case - latin:mode="email|url" - > - <Key - latin:keySpec="-" /> - </case> - <default> - <Key - latin:keySpec="\?" - latin:keyHintLabel="!" - latin:moreKeys="!" - latin:keyStyle="hasShiftedLetterHintStyle" /> - </default> - </switch> -</merge> + <com.android.inputmethod.keyboard.MoreKeysKeyboardView + android:id="@+id/more_keys_keyboard_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="?attr/moreKeysKeyboardViewForActionStyle" /> +</LinearLayout> diff --git a/java/res/layout/radio_button_preference_widget.xml b/java/res/layout/radio_button_preference_widget.xml new file mode 100644 index 000000000..ee9cda1cc --- /dev/null +++ b/java/res/layout/radio_button_preference_widget.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<RadioButton + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/radio_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="false" + android:focusable="false" /> diff --git a/java/res/layout/suggestion_divider.xml b/java/res/layout/suggestion_divider.xml index dfea017e6..a5bb6c562 100644 --- a/java/res/layout/suggestion_divider.xml +++ b/java/res/layout/suggestion_divider.xml @@ -31,4 +31,5 @@ android:longClickable="false" android:hapticFeedbackEnabled="false" android:soundEffectsEnabled="false" + android:background="@null" style="?attr/suggestionStripViewStyle" /> diff --git a/java/res/layout/suggestions_strip.xml b/java/res/layout/suggestions_strip.xml index 489477990..aefdb8cad 100644 --- a/java/res/layout/suggestions_strip.xml +++ b/java/res/layout/suggestions_strip.xml @@ -48,12 +48,13 @@ android:layout_height="match_parent" style="?attr/suggestionWordStyle" /> <include + android:id="@+id/word_to_save_divider" layout="@layout/suggestion_divider" /> <TextView android:id="@+id/hint_add_to_dictionary" android:layout_width="match_parent" android:layout_height="match_parent" - android:textAlignment="viewStart" + android:gravity="center_vertical|start" style="?attr/suggestionWordStyle" /> </LinearLayout> <!-- Provide audio and haptic feedback by ourselves based on the keyboard settings. diff --git a/java/res/layout/user_dictionary_item.xml b/java/res/layout/user_dictionary_item.xml index b8d48b56d..fe7d47ec2 100644 --- a/java/res/layout/user_dictionary_item.xml +++ b/java/res/layout/user_dictionary_item.xml @@ -29,7 +29,7 @@ android:layout_weight="1" > <TextView - android:id="@+android:id/text1" + android:id="@android:id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="marquee" @@ -38,7 +38,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" /> <TextView - android:id="@+android:id/text2" + android:id="@android:id/text2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignStart="@android:id/text1" diff --git a/java/res/anim/more_keys_keyboard_fadein.xml b/java/res/menu/add_style.xml index c781f36ad..d1cab4bb5 100644 --- a/java/res/anim/more_keys_keyboard_fadein.xml +++ b/java/res/menu/add_style.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2010, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,13 +17,10 @@ ** limitations under the License. */ --> - -<set - xmlns:android="http://schemas.android.com/apk/res/android" - android:interpolator="@android:anim/decelerate_interpolator" -> - <alpha - android:fromAlpha="0.5" - android:toAlpha="1.0" - android:duration="@integer/config_more_keys_keyboard_fadein_anim_time" /> -</set> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/action_add_style" + android:icon="@drawable/ic_add_circle_wht_24dp" + android:title="@string/add_style" + android:showAsAction="always" /> +</menu>
\ No newline at end of file diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict Binary files differindex 0f08f1735..9fdaf3148 100644 --- a/java/res/raw/main_ru.dict +++ b/java/res/raw/main_ru.dict diff --git a/java/res/anim/more_keys_keyboard_fadeout.xml b/java/res/values-az-rAZ/bools.xml index 32fae6bd8..130e52eab 100644 --- a/java/res/anim/more_keys_keyboard_fadeout.xml +++ b/java/res/values-az-rAZ/bools.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2010, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,13 +17,8 @@ ** limitations under the License. */ --> - -<set - xmlns:android="http://schemas.android.com/apk/res/android" - android:interpolator="@android:anim/accelerate_interpolator" -> - <alpha - android:fromAlpha="1.0" - android:toAlpha="0.0" - android:duration="@integer/config_more_keys_keyboard_fadeout_anim_time" /> -</set> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/xml-sw600dp/key_f1.xml b/java/res/values-bn-rIN/bools.xml index ba78a6430..130e52eab 100644 --- a/java/res/xml-sw600dp/key_f1.xml +++ b/java/res/values-bn-rIN/bools.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2012, The Android Open Source Project +** Copyright 2014, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -17,20 +17,8 @@ ** limitations under the License. */ --> - -<merge - xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" -> - <switch> - <case - latin:mode="email" - > - <Key - latin:keySpec="\@" /> - </case> - <default> - <Key - latin:keySpec="/" /> - </default> - </switch> -</merge> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-eu-rES/bools.xml b/java/res/values-eu-rES/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-eu-rES/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-gl-rES/bools.xml b/java/res/values-gl-rES/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-gl-rES/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-hy-rAM/bools.xml b/java/res/values-hy-rAM/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-hy-rAM/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-is-rIS/bools.xml b/java/res/values-is-rIS/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-is-rIS/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-kk-rKZ/bools.xml b/java/res/values-kk-rKZ/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-kk-rKZ/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-km-rKH/bools.xml b/java/res/values-km-rKH/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-km-rKH/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-kn-rIN/bools.xml b/java/res/values-kn-rIN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-kn-rIN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-ky-rKG/bools.xml b/java/res/values-ky-rKG/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-ky-rKG/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-land/config.xml b/java/res/values-land/config.xml index 5eea4c1c6..d36f6a0fc 100644 --- a/java/res/values-land/config.xml +++ b/java/res/values-land/config.xml @@ -40,14 +40,22 @@ <!-- config_more_keys_keyboard_key_height x -0.5 --> <dimen name="config_more_keys_keyboard_vertical_correction_holo">-22.4dp</dimen> <dimen name="config_key_preview_offset_holo">1.6dp</dimen> - + <dimen name="config_key_preview_height_holo">80dp</dimen> + <dimen name="config_key_preview_offset_lxx">43.6dp</dimen> + <dimen name="config_key_preview_height_lxx">122dp</dimen> <fraction name="config_key_preview_text_ratio">90%</fraction> - <fraction name="config_key_letter_ratio">65%</fraction> - <fraction name="config_key_large_letter_ratio">74%</fraction> - <fraction name="config_key_label_ratio">40%</fraction> - <fraction name="config_key_hint_letter_ratio">30%</fraction> - <fraction name="config_key_hint_label_ratio">52%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">40%</fraction> + <fraction name="config_key_letter_ratio_holo">65%</fraction> + <fraction name="config_key_letter_ratio_lxx">65%</fraction> + <fraction name="config_key_large_letter_ratio_holo">74%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">90%</fraction> + <fraction name="config_key_label_ratio_holo">40%</fraction> + <fraction name="config_key_label_ratio_lxx">40%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">30%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">30%</fraction> + <fraction name="config_key_hint_label_ratio_holo">52%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">30%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">40%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">40%</fraction> <fraction name="config_language_on_spacebar_text_ratio">40.000%</fraction> <!-- For 5-row keyboard --> diff --git a/java/res/values-land/keyboard-heights.xml b/java/res/values-land/keyboard-heights.xml index d57f96be3..02d8b14c8 100644 --- a/java/res/values-land/keyboard-heights.xml +++ b/java/res/values-land/keyboard-heights.xml @@ -33,5 +33,7 @@ <!-- Preferable keyboard height in absolute scale: 45.0mm --> <!-- Xoom --> <item>HARDWARE=stingray,265.4378</item> + <!-- Volantis --> + <item>HARDWARE=flounder,272.0</item> </string-array> </resources> diff --git a/java/res/values-lo-rLA/bools.xml b/java/res/values-lo-rLA/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-lo-rLA/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-mk-rMK/bools.xml b/java/res/values-mk-rMK/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-mk-rMK/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-ml-rIN/bools.xml b/java/res/values-ml-rIN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-ml-rIN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-mn-rMN/bools.xml b/java/res/values-mn-rMN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-mn-rMN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-mr-rIN/bools.xml b/java/res/values-mr-rIN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-mr-rIN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-ne-rNP/bools.xml b/java/res/values-ne-rNP/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-ne-rNP/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-sw600dp-land/config.xml b/java/res/values-sw600dp-land/config.xml index 6368eef41..d33af2e37 100644 --- a/java/res/values-sw600dp-land/config.xml +++ b/java/res/values-sw600dp-land/config.xml @@ -23,7 +23,7 @@ <!-- Preferable keyboard height in absolute scale: 45.0mm --> <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> <dimen name="config_default_keyboard_height">283.5dp</dimen> - <fraction name="config_min_keyboard_height">45%p</fraction> + <fraction name="config_min_keyboard_height">40%p</fraction> <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen> @@ -32,12 +32,18 @@ <fraction name="config_key_vertical_gap_holo">4.5%p</fraction> <fraction name="config_key_horizontal_gap_holo">0.9%p</fraction> - <fraction name="config_key_letter_ratio">50%</fraction> - <fraction name="config_key_large_letter_ratio">48%</fraction> - <fraction name="config_key_label_ratio">32%</fraction> - <fraction name="config_key_hint_letter_ratio">23%</fraction> - <fraction name="config_key_hint_label_ratio">34%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">29%</fraction> + <fraction name="config_key_letter_ratio_holo">50%</fraction> + <fraction name="config_key_letter_ratio_lxx">50%</fraction> + <fraction name="config_key_large_letter_ratio_holo">48%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">60%</fraction> + <fraction name="config_key_label_ratio_holo">32%</fraction> + <fraction name="config_key_label_ratio_lxx">32%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">23%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction> + <fraction name="config_key_hint_label_ratio_holo">34%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">20%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">29%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">29%</fraction> <fraction name="config_language_on_spacebar_text_ratio">30.0%</fraction> <dimen name="config_key_shifted_letter_hint_padding">4dp</dimen> diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml index 9d16e2cb9..44e0d0632 100644 --- a/java/res/values-sw600dp/config.xml +++ b/java/res/values-sw600dp/config.xml @@ -40,17 +40,20 @@ <fraction name="config_key_horizontal_gap_holo">1.565%p</fraction> <!-- config_more_keys_keyboard_key_height x -0.5 --> <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> - <dimen name="config_key_preview_offset_holo">8.0dp</dimen> - <dimen name="config_key_preview_height">94.5dp</dimen> <fraction name="config_key_preview_text_ratio">50%</fraction> - <fraction name="config_key_letter_ratio">42%</fraction> - <fraction name="config_key_large_letter_ratio">45%</fraction> - <fraction name="config_key_label_ratio">25%</fraction> - <fraction name="config_key_large_label_ratio">32%</fraction> - <fraction name="config_key_hint_letter_ratio">23%</fraction> - <fraction name="config_key_hint_label_ratio">28%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">22%</fraction> + <fraction name="config_key_letter_ratio_holo">42%</fraction> + <fraction name="config_key_letter_ratio_lxx">50%</fraction> + <fraction name="config_key_large_letter_ratio_holo">45%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">60%</fraction> + <fraction name="config_key_label_ratio_holo">25%</fraction> + <fraction name="config_key_label_ratio_lxx">32%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">23%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction> + <fraction name="config_key_hint_label_ratio_holo">28%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">20%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">22%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">22%</fraction> <fraction name="config_language_on_spacebar_text_ratio">28.0%</fraction> <dimen name="config_key_hint_letter_padding">3dp</dimen> <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen> diff --git a/java/res/values-sw768dp-land/config.xml b/java/res/values-sw768dp-land/config.xml index a1659b45a..fdb95c6cc 100644 --- a/java/res/values-sw768dp-land/config.xml +++ b/java/res/values-sw768dp-land/config.xml @@ -23,23 +23,27 @@ <!-- Preferable keyboard height in absolute scale: 58.0mm --> <!-- This config_default_keyboard_height value should match with keyboard-heights.xml --> <dimen name="config_default_keyboard_height">365.4dp</dimen> - <fraction name="config_min_keyboard_height">45%p</fraction> + <fraction name="config_min_keyboard_height">35%p</fraction> <fraction name="config_keyboard_top_padding_holo">1.896%p</fraction> <fraction name="config_keyboard_bottom_padding_holo">0.0%p</fraction> <fraction name="config_key_vertical_gap_holo">3.690%p</fraction> <fraction name="config_key_horizontal_gap_holo">1.030%p</fraction> - <dimen name="config_key_preview_offset_holo">8.0dp</dimen> <dimen name="config_more_keys_keyboard_key_height">81.9dp</dimen> - <dimen name="config_key_preview_height">107.1dp</dimen> - <fraction name="config_key_letter_ratio">43%</fraction> - <fraction name="config_key_large_letter_ratio">42%</fraction> - <fraction name="config_key_label_ratio">28%</fraction> - <fraction name="config_key_hint_letter_ratio">23%</fraction> - <fraction name="config_key_hint_label_ratio">28%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">24%</fraction> + <fraction name="config_key_letter_ratio_holo">43%</fraction> + <fraction name="config_key_letter_ratio_lxx">50%</fraction> + <fraction name="config_key_large_letter_ratio_holo">42%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">60%</fraction> + <fraction name="config_key_label_ratio_holo">28%</fraction> + <fraction name="config_key_label_ratio_lxx">32%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">23%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction> + <fraction name="config_key_hint_label_ratio_holo">28%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">20%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">24%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">24%</fraction> <fraction name="config_language_on_spacebar_text_ratio">24.00%</fraction> <!-- For 5-row keyboard --> diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml index 635061d24..13be6bedf 100644 --- a/java/res/values-sw768dp/config.xml +++ b/java/res/values-sw768dp/config.xml @@ -32,7 +32,6 @@ <fraction name="config_key_horizontal_gap_holo">1.066%p</fraction> <!-- config_more_keys_keyboard_key_height x -0.5 --> <dimen name="config_more_keys_keyboard_vertical_correction_holo">-31.5dp</dimen> - <dimen name="config_key_preview_offset_holo">8.0dp</dimen> <dimen name="config_more_keys_keyboard_key_height">63.0dp</dimen> <dimen name="config_more_keys_keyboard_key_horizontal_padding">12dp</dimen> @@ -40,15 +39,19 @@ <!-- config_more_keys_keyboard_key_height x 1.2 --> <dimen name="config_more_keys_keyboard_slide_allowance">98.3dp</dimen> - <dimen name="config_key_preview_height">94.5dp</dimen> <fraction name="config_key_preview_text_ratio">50%</fraction> - <fraction name="config_key_letter_ratio">40%</fraction> - <fraction name="config_key_large_letter_ratio">42%</fraction> - <fraction name="config_key_label_ratio">28%</fraction> - <fraction name="config_key_large_label_ratio">28%</fraction> - <fraction name="config_key_hint_letter_ratio">23%</fraction> - <fraction name="config_key_hint_label_ratio">28%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">26%</fraction> + <fraction name="config_key_letter_ratio_holo">40%</fraction> + <fraction name="config_key_letter_ratio_lxx">50%</fraction> + <fraction name="config_key_large_letter_ratio_holo">42%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">60%</fraction> + <fraction name="config_key_label_ratio_holo">28%</fraction> + <fraction name="config_key_label_ratio_lxx">32%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">23%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">23%</fraction> + <fraction name="config_key_hint_label_ratio_holo">28%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">20%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">26%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">26%</fraction> <fraction name="config_language_on_spacebar_text_ratio">29.03%</fraction> <dimen name="config_key_hint_letter_padding">3dp</dimen> <dimen name="config_key_shifted_letter_hint_padding">3dp</dimen> diff --git a/java/res/values-ta-rIN/bools.xml b/java/res/values-ta-rIN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-ta-rIN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values-te-rIN/bools.xml b/java/res/values-te-rIN/bools.xml new file mode 100644 index 000000000..130e52eab --- /dev/null +++ b/java/res/values-te-rIN/bools.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources> + <!-- Whether this input method should be used as the default for a locale. Override it + for supported languages. --> + <bool name="im_is_default">true</bool> +</resources> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 5434106c5..f1253b40c 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -32,6 +32,8 @@ <attr name="moreKeysKeyboardStyle" format="reference" /> <!-- MoreKeysKeyboardView style --> <attr name="moreKeysKeyboardViewStyle" format="reference" /> + <!-- MoreKeysKeyboardView style for action key --> + <attr name="moreKeysKeyboardViewForActionStyle" format="reference" /> <!-- Suggestions strip style --> <attr name="suggestionStripViewStyle" format="reference" /> <!-- Suggestion word style --> @@ -52,7 +54,9 @@ <attr name="spacebarIconWidthRatio" format="float" /> <!-- Right padding of hint letter to the edge of the key.--> <attr name="keyHintLetterPadding" format="dimension" /> - <!-- Bottom padding of popup hint letter "..." to the edge of the key.--> + <!-- Popup hint letter string--> + <attr name="keyPopupHintLetter" format="string" /> + <!-- Bottom padding of popup hint letter to the edge of the key.--> <attr name="keyPopupHintLetterPadding" format="dimension" /> <!-- Right padding of shifted letter hint to the edge of the key.--> <attr name="keyShiftedLetterHintPadding" format="dimension" /> @@ -111,8 +115,14 @@ <!-- TODO: consolidate key preview linger timeout with the key preview animation parameters. --> <!-- Delay after key releasing and key press feedback dismissing in millisecond --> <attr name="keyPreviewLingerTimeout" format="integer" /> + <!-- Key preview show up animator --> + <attr name="keyPreviewShowUpAnimator" format="reference" /> + <!-- Key preview dismiss animator --> + <attr name="keyPreviewDismissAnimator" format="reference" /> <!-- Layout resource for more keys keyboard --> <attr name="moreKeysKeyboardLayout" format="reference" /> + <!-- Layout resource for more keys keyboard of action key --> + <attr name="moreKeysKeyboardForActionLayout" format="reference" /> <attr name="backgroundDimAlpha" format="integer" /> <!-- More keys keyboard will shown at touched point. --> <attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" /> @@ -167,6 +177,10 @@ <attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" /> </declare-styleable> + <declare-styleable name="MoreKeysKeyboardView"> + <attr name="divider" format="reference" /> + </declare-styleable> + <declare-styleable name="EmojiPalettesView"> <attr name="categoryIndicatorEnabled" format="boolean" /> <attr name="categoryIndicatorDrawable" format="reference" /> @@ -201,7 +215,13 @@ </declare-styleable> <declare-styleable name="Keyboard"> - <attr name="themeId" format="integer" /> + <attr name="themeId" format="enum"> + <!-- This should be aligned with KeyboardTheme.THEME_ID_*. --> + <enum name="ICS" value="0" /> + <enum name="KLP" value="2" /> + <enum name="LXXLight" value="3" /> + <enum name="LXXDark" value="4" /> + </attr> <!-- Touch position correction --> <attr name="touchPositionCorrectionData" format="reference" /> <!-- Keyboard top, bottom, left, right edges paddings, in propotion of keyboard height. --> @@ -240,7 +260,8 @@ <attr name="iconZwnjKey" format="reference" /> <attr name="iconZwjKey" format="reference" /> <attr name="iconImeKey" format="reference" /> - <attr name="iconEmojiKey" format="reference" /> + <attr name="iconEmojiActionKey" format="reference" /> + <attr name="iconEmojiNormalKey" format="reference" /> </declare-styleable> <declare-styleable name="Keyboard_GridRows"> @@ -268,9 +289,10 @@ <enum name="empty" value="0" /> <enum name="normal" value="1" /> <enum name="functional" value="2" /> - <enum name="action" value="3" /> - <enum name="stickyOff" value="4" /> - <enum name="stickyOn" value="5" /> + <enum name="stickyOff" value="3" /> + <enum name="stickyOn" value="4" /> + <enum name="action" value="5" /> + <enum name="spacebar" value="6" /> </attr> <!-- The key action flags. --> <attr name="keyActionFlags" format="integer"> @@ -289,14 +311,15 @@ <!-- The key label flags. --> <attr name="keyLabelFlags" format="integer"> <!-- This should be aligned with Key.LABEL_FLAGS__* --> - <flag name="alignLeftOfCenter" value="0x08" /> + <flag name="alignHintLabelToBottom" value="0x02" /> + <flag name="alignIconToBottom" value="0x04" /> + <flag name="alignLabelOffCenter" value="0x08" /> <flag name="fontNormal" value="0x10" /> <flag name="fontMonoSpace" value="0x20" /> <flag name="fontDefault" value="0x30" /> <flag name="followKeyLargeLetterRatio" value="0x40" /> <flag name="followKeyLetterRatio" value="0x80" /> <flag name="followKeyLabelRatio" value="0xC0" /> - <flag name="followKeyLargeLabelRatio" value="0x100" /> <flag name="followKeyHintLabelRatio" value="0x140" /> <flag name="hasPopupHint" value="0x200" /> <flag name="hasShiftedLetterHint" value="0x400" /> @@ -315,6 +338,8 @@ <!-- If true, use functionalTextColor instead of ketTextColor to drawing the label on the key --> <flag name="followFunctionalTextColor" value="0x80000" /> + <!-- Keep aspect ratio of key background. --> + <flag name="keepBackgroundAspectRatio" value="0x100000" /> <!-- If true, disable keyHintLabel. --> <flag name="disableKeyHintLabel" value="0x40000000" /> <!-- If true, disable additionalMoreKeys. --> @@ -355,8 +380,6 @@ <attr name="keyLabelSize" format="dimension|fraction" /> <!-- Large size of the text for one letter keys, in the proportion of key height. --> <attr name="keyLargeLetterRatio" format="fraction" /> - <!-- Large size of the text for keys with multiple letters, in the proportion of key height. --> - <attr name="keyLargeLabelRatio" format="fraction" /> <!-- Size of the text for hint letter (= one character hint label), in the proportion of key height. --> <attr name="keyHintLetterRatio" format="fraction" /> @@ -364,6 +387,14 @@ <attr name="keyHintLabelRatio" format="fraction" /> <!-- Size of the text for shifted letter hint, in the proportion of key height. --> <attr name="keyShiftedLetterHintRatio" format="fraction" /> + <!-- The label's horizontal offset to the center of the key. Negative is to left and + positive is to right. The value is in proportion of the width of + TypefaceUtils.KEY_LABEL_REFERENCE_CHAR. --> + <attr name="keyLabelOffCenterRatio" format="fraction" /> + <!-- The hint label's horizontal offset to the center of the key. Negative is to left and + positive is to right. The value is in proportion of the width of + TypefaceUtils.KEY_LABEL_REFERENCE_CHAR. --> + <attr name="keyHintLabelOffCenterRatio" format="fraction" /> <!-- Color to use for the label in a key. --> <attr name="keyTextColor" format="color" /> <attr name="keyTextShadowColor" format="color" /> @@ -412,6 +443,13 @@ <enum name="emojiCategory5" value="15" /> <enum name="emojiCategory6" value="16" /> </attr> + <!-- This should be aligned with Keyboard.themeId and KeyboardTheme.THEME_ID_* --> + <attr name="keyboardTheme" format="enum|string"> + <enum name="ICS" value="0" /> + <enum name="KLP" value="2" /> + <enum name="LXXLight" value="3" /> + <enum name="LXXDark" value="4" /> + </attr> <!-- This should be aligned with KeyboardId.MODE_* --> <attr name="mode" format="enum|string"> <enum name="text" value="0" /> @@ -481,13 +519,24 @@ <declare-styleable name="KeyboardLayoutSet_Feature"> <!-- This should be aligned with ScriptUtils.SCRIPT_* --> <attr name="supportedScript" format="enum"> - <enum name="latin" value="0" /> - <enum name="cyrillic" value="1" /> - <enum name="greek" value="2" /> - <enum name="arabic" value="3" /> - <enum name="hebrew" value="4" /> - <enum name="armenian" value="5" /> - <enum name="georgian" value="6" /> + <enum name="arabic" value="0" /> + <enum name="armenian" value="1" /> + <enum name="bengali" value="2" /> + <enum name="cyrillic" value="3" /> + <enum name="devanagari" value="4" /> + <enum name="georgian" value="5" /> + <enum name="greek" value="6" /> + <enum name="hebrew" value="7" /> + <enum name="kannada" value="8" /> + <enum name="khmer" value="9" /> + <enum name="lao" value="10" /> + <enum name="latin" value="11" /> + <enum name="malayalam" value="12" /> + <enum name="myanmar" value="13" /> + <enum name="sinhala" value="14" /> + <enum name="tamil" value="15" /> + <enum name="telugu" value="16" /> + <enum name="thai" value="17" /> </attr> </declare-styleable> diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml index 783105dc0..5453d51c1 100644 --- a/java/res/values/colors.xml +++ b/java/res/values/colors.xml @@ -53,10 +53,8 @@ <color name="suggested_word_color_lxx_light">#B337474F</color> <color name="gesture_trail_color_lxx_light">#4DB6AC</color> <color name="sliding_key_input_preview_color_lxx_light">#B34DB6AC</color> - <color name="keyboard_background_lxx_light">#ECEFF1</color> <color name="key_background_lxx_light">#ECEFF1</color> <color name="key_background_pressed_lxx_light">#2637474F</color> - <color name="suggestions_strip_background_lxx_light">#E4E7E9</color> <color name="suggested_word_background_selected_lxx_light">#2637474F</color> <color name="gesture_floating_preview_color_lxx_light">#E6ECEFF1</color> <color name="emoji_tab_page_indicator_background_lxx_light">#E4E7E9</color> @@ -73,10 +71,8 @@ <color name="suggested_word_color_lxx_dark">#B3FFFFFF</color> <color name="gesture_trail_color_lxx_dark">#80CBC4</color> <color name="sliding_key_input_preview_color_lxx_dark">#B380CBC4</color> - <color name="keyboard_background_lxx_dark">#263238</color> <color name="key_background_lxx_dark">#263238</color> <color name="key_background_pressed_lxx_dark">#19FFFFFF</color> - <color name="suggestions_strip_background_lxx_dark">#21272B</color> <color name="suggested_word_background_selected_lxx_dark">#19FFFFFF</color> <color name="gesture_floating_preview_color_lxx_dark">#E621272B</color> <color name="emoji_tab_page_indicator_background_lxx_dark">#21272B</color> @@ -86,4 +82,6 @@ <color name="setup_text_action">@android:color/holo_blue_light</color> <color name="setup_step_background">@android:color/background_light</color> <color name="setup_welcome_video_margin_color">#FFCCCCCC</color> + <!-- Accent color for the notification. We need to match this to the OS build --> + <color name="notification_accent_color">#FF607D8B</color> </resources> diff --git a/java/res/values/config-common.xml b/java/res/values/config-common.xml index 063fbfb37..36fd30aef 100644 --- a/java/res/values/config-common.xml +++ b/java/res/values/config-common.xml @@ -24,7 +24,7 @@ at input history to suggest a hopefully helpful suggestions for the next word? --> <bool name="config_default_next_word_prediction">true</bool> - <integer name="config_delay_update_shift_state">100</integer> + <integer name="config_delay_in_milliseconds_to_update_shift_state">100</integer> <integer name="config_double_space_period_timeout">1100</integer> <integer name="config_key_repeat_start_timeout">400</integer> @@ -107,8 +107,8 @@ <!-- Common suggestion strip configuration. --> <integer name="config_suggestions_count_in_strip">3</integer> <fraction name="config_center_suggestion_percentile">36%</fraction> - <integer name="config_delay_update_suggestions">100</integer> - <integer name="config_delay_update_old_suggestions">300</integer> + <integer name="config_delay_in_milliseconds_to_update_suggestions">100</integer> + <integer name="config_delay_in_milliseconds_to_update_old_suggestions">300</integer> <!-- Common more suggestions configuraion. --> <dimen name="config_more_suggestions_key_horizontal_padding">12dp</dimen> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index d748c9179..40760f686 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -43,16 +43,22 @@ <!-- config_more_keys_keyboard_key_height x -0.5 --> <dimen name="config_more_keys_keyboard_vertical_correction_holo">-26.4dp</dimen> <dimen name="config_key_preview_offset_holo">8.0dp</dimen> - - <dimen name="config_key_preview_height">80dp</dimen> + <dimen name="config_key_preview_height_holo">80dp</dimen> + <dimen name="config_key_preview_offset_lxx">50.0dp</dimen> + <dimen name="config_key_preview_height_lxx">122dp</dimen> <fraction name="config_key_preview_text_ratio">82%</fraction> - <fraction name="config_key_letter_ratio">55%</fraction> - <fraction name="config_key_large_letter_ratio">65%</fraction> - <fraction name="config_key_label_ratio">34%</fraction> - <fraction name="config_key_large_label_ratio">40%</fraction> - <fraction name="config_key_hint_letter_ratio">25%</fraction> - <fraction name="config_key_hint_label_ratio">44%</fraction> - <fraction name="config_key_shifted_letter_hint_ratio">35%</fraction> + <fraction name="config_key_letter_ratio_holo">55%</fraction> + <fraction name="config_key_letter_ratio_lxx">55%</fraction> + <fraction name="config_key_large_letter_ratio_holo">65%</fraction> + <fraction name="config_key_large_letter_ratio_lxx">90%</fraction> + <fraction name="config_key_label_ratio_holo">34%</fraction> + <fraction name="config_key_label_ratio_lxx">34%</fraction> + <fraction name="config_key_hint_letter_ratio_holo">25%</fraction> + <fraction name="config_key_hint_letter_ratio_lxx">25%</fraction> + <fraction name="config_key_hint_label_ratio_holo">44%</fraction> + <fraction name="config_key_hint_label_ratio_lxx">30%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_holo">35%</fraction> + <fraction name="config_key_shifted_letter_hint_ratio_lxx">35%</fraction> <fraction name="config_language_on_spacebar_text_ratio">33.735%</fraction> <dimen name="config_key_hint_letter_padding">1dp</dimen> <dimen name="config_key_shifted_letter_hint_padding">2dp</dimen> diff --git a/java/res/values/donottranslate-debug-settings.xml b/java/res/values/donottranslate-debug-settings.xml new file mode 100644 index 000000000..35e6efa77 --- /dev/null +++ b/java/res/values/donottranslate-debug-settings.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Title for Android keyboard debug settings activity / dialog --> + <string name="english_ime_debug_settings">Android Keyboard Debug settings</string> + <string name="prefs_debug_mode">Debug Mode</string> + <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string> + <string name="prefs_force_physical_keyboard_special_key">Force physical keyboard special key</string> + <string name="prefs_show_ui_to_accept_typed_word">Show UI to accept typed word</string> + <!-- Option to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=30]--> + <string name="sliding_key_input_preview">Show slide indicator</string> + <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]--> + <string name="sliding_key_input_preview_summary">Display visual cue while sliding from Shift or Symbol keys</string> + <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> + <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> + <!-- Title of the settings for customize key popup animation parameters [CHAR LIMIT=35] --> + <string name="prefs_customize_key_preview_animation">Customize key preview animation</string> + <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_show_up_duration_settings">Key popup show up duration</string> + <!-- Title of the settings for key popup dismiss animation duration (in milliseconds) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_dismiss_duration_settings">Key popup dismiss duration</string> + <!-- Title of the settings for key popup show up animation start X-scale (in percentile) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_show_up_start_x_scale_settings">Key popup show up start X scale</string> + <!-- Title of the settings for key popup show up animation start Y-scale (in percentile) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_show_up_start_y_scale_settings">Key popup show up start Y scale</string> + <!-- Title of the settings for key popup dismiss animation end X-scale (in percentile) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_dismiss_end_x_scale_settings">Key popup dismiss end X scale</string> + <!-- Title of the settings for key popup dismiss animation end Y-scale (in percentile) [CHAR LIMIT=35] --> + <string name="prefs_key_popup_dismiss_end_y_scale_settings">Key popup dismiss end Y scale</string> + <!-- Title of the settings for reading an external dictionary file --> + <string name="prefs_read_external_dictionary">Read external dictionary file</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] --> + <string name="read_external_dictionary_multiple_files_title">Select a dictionary file to install</string> + <!-- Title of the confirmation dialog to install a file as an external dictionary [CHAR LIMIT=50] --> + <string name="read_external_dictionary_confirm_install_message">Really install this file for <xliff:g id="LANGUAGE_NAME" example="English">%s</xliff:g>?</string> + <!-- Title for an error dialog that contains the details of the error in the body [CHAR LIMIT=80] --> + <string name="read_external_dictionary_error">There was an error</string> + <!-- Title of the settings group for dumpping dictionary files that have been created on the device [CHAR LIMIT=35] --> + <string name="prefs_dump_dynamic_dicts">Dump dictionary</string> +</resources> diff --git a/java/res/values/donottranslate-text-decorator.xml b/java/res/values/donottranslate-text-decorator.xml new file mode 100644 index 000000000..269364573 --- /dev/null +++ b/java/res/values/donottranslate-text-decorator.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <!-- The extra margin in dp around the hit area of the commit/add-to-dictionary indicator --> + <integer name="text_decorator_hit_area_margin_in_dp"> + 4 + </integer> + + <!-- Background color to be used to highlight the target text when the add-to-dictionary + indicator is visible. --> + <color name="text_decorator_add_to_dictionary_indicator_text_highlight_color"> + #D1E7B7 + </color> + + <!-- Foreground color of the commit indicator. --> + <color name="text_decorator_add_to_dictionary_indicator_background_color"> + #4EB848 + </color> + + <!-- Foreground color of the add-to-dictionary indicator. --> + <color name="text_decorator_add_to_dictionary_indicator_foreground_color"> + #FFFFFF + </color> + + <!-- Viewport size of "text_decorator_add_to_dictionary_indicator_path". --> + <integer name="text_decorator_add_to_dictionary_indicator_path_size"> + 480 + </integer> + + <!-- Coordinates of the closed path to be used to render the add-to-dictionary indicator. + The format is: X[0], Y[0], X[1], Y[1], ..., X[N-1], Y[N-1] --> + <integer-array name="text_decorator_add_to_dictionary_indicator_path"> + <item>380</item> + <item>260</item> + <item>260</item> + <item>260</item> + <item>260</item> + <item>380</item> + <item>220</item> + <item>380</item> + <item>220</item> + <item>260</item> + <item>100</item> + <item>260</item> + <item>100</item> + <item>220</item> + <item>220</item> + <item>220</item> + <item>220</item> + <item>100</item> + <item>260</item> + <item>100</item> + <item>260</item> + <item>220</item> + <item>380</item> + <item>220</item> + </integer-array> +</resources> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index e3f0aeade..c54b995f8 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -22,11 +22,6 @@ See {@link SettingsValues#needsToShowVoiceInputKey(SharedPreferences,Resources)} --> <string name="voice_mode_main">0</string> - <!-- Title for Latin keyboard debug settings activity / dialog --> - <string name="english_ime_debug_settings">Android keyboard Debug settings</string> - <string name="prefs_debug_mode">Debug Mode</string> - <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string> - <!-- Subtype locale display name exceptions. For each exception, there should be related string resources for display name that may have explicit keyboard layout. The string resource name must be "subtype_<locale>" or diff --git a/java/res/values/keyboard-icons-holo.xml b/java/res/values/keyboard-icons-holo.xml index d95ff8201..f5484bf4e 100644 --- a/java/res/values/keyboard-icons-holo.xml +++ b/java/res/values/keyboard-icons-holo.xml @@ -35,6 +35,7 @@ <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_holo_dark</item> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_holo_dark</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-lxx-dark.xml b/java/res/values/keyboard-icons-lxx-dark.xml index 15d267cb2..2e2fd0abb 100644 --- a/java/res/values/keyboard-icons-lxx-dark.xml +++ b/java/res/values/keyboard-icons-lxx-dark.xml @@ -24,7 +24,7 @@ <item name="iconShiftKey">@drawable/sym_keyboard_shift_lxx_dark</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete_lxx_dark</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings_lxx_dark</item> - <item name="iconSpaceKey">@drawable/sym_keyboard_spacebar_lxx_dark</item> + <item name="iconSpaceKey">@null</item> <item name="iconEnterKey">@drawable/sym_keyboard_return_lxx_dark</item> <item name="iconGoKey">@drawable/sym_keyboard_go_lxx_dark</item> <item name="iconSearchKey">@drawable/sym_keyboard_search_lxx_dark</item> @@ -34,13 +34,13 @@ <item name="iconPreviousKey">@drawable/sym_keyboard_previous_lxx_dark</item> <item name="iconTabKey">@drawable/sym_keyboard_tab_lxx_dark</item> <item name="iconShortcutKey">@drawable/sym_keyboard_voice_lxx_dark</item> - <!-- TODO: Update this icon for LXX_Dark theme. --> - <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_lxx_dark</item> <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_lxx_dark</item> <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_lxx_dark</item> <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_dark</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_lxx_dark</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_lxx_dark</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_lxx_dark</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-lxx-light.xml b/java/res/values/keyboard-icons-lxx-light.xml index 60853ca31..099a706fe 100644 --- a/java/res/values/keyboard-icons-lxx-light.xml +++ b/java/res/values/keyboard-icons-lxx-light.xml @@ -24,7 +24,7 @@ <item name="iconShiftKey">@drawable/sym_keyboard_shift_lxx_light</item> <item name="iconDeleteKey">@drawable/sym_keyboard_delete_lxx_light</item> <item name="iconSettingsKey">@drawable/sym_keyboard_settings_lxx_light</item> - <item name="iconSpaceKey">@drawable/sym_keyboard_spacebar_lxx_light</item> + <item name="iconSpaceKey">@null</item> <item name="iconEnterKey">@drawable/sym_keyboard_return_lxx_light</item> <item name="iconGoKey">@drawable/sym_keyboard_go_lxx_light</item> <item name="iconSearchKey">@drawable/sym_keyboard_search_lxx_light</item> @@ -34,13 +34,15 @@ <item name="iconPreviousKey">@drawable/sym_keyboard_previous_lxx_light</item> <item name="iconTabKey">@drawable/sym_keyboard_tab_lxx_light</item> <item name="iconShortcutKey">@drawable/sym_keyboard_voice_lxx_light</item> - <!-- TODO: Update this icon for LXX_Light theme. --> - <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item> + <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_lxx_light</item> <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_lxx_light</item> <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_lxx_light</item> <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_light</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_lxx_light</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_lxx_light</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_light</item> + <!-- Use white emoji icon (for lxx_dark) because an action key has green/dark color background. --> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <!-- Use dark green emoji icon (for lxx_light) because a normal key has white color background. --> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_lxx_light</item> </style> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index b6601c8c2..2f8b3800d 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -36,18 +36,20 @@ <!-- Option to control whether or not to show a popup with a larger font on each key press. --> <string name="popup_on_keypress">Popup on keypress</string> - <!-- Settings screen title for input preferences [CHAR LIMIT=33]--> - <string name="settings_screen_input">Input preferences</string> - <!-- Settings screen title for appearance preferences [CHAR LIMIT=33] --> - <string name="settings_screen_appearances">Appearance</string> - <!-- Settings screen title for multi lingual options [CHAR_LIMIT=33] --> - <string name="settings_screen_multi_lingual">Multi lingual options</string> + <!-- Settings screen title for preferences [CHAR LIMIT=33]--> + <string name="settings_screen_preferences">Preferences</string> + <!-- Settings screen title for appearance & layouts preferences [CHAR LIMIT=33] --> + <string name="settings_screen_appearance">Appearance & layouts</string> + <!-- Settings screen title for multilingual options [CHAR_LIMIT=33] --> + <string name="settings_screen_multilingual">Multilingual options</string> <!-- Settings screen title for gesture typing preferences [CHAR_LIMIT=33] --> - <string name="settings_screen_gesture">Gesture typing preferences</string> + <string name="settings_screen_gesture">Gesture Typing</string> <!-- Settings screen title for text correction options [CHAR_LIMIT=33] --> <string name="settings_screen_correction">Text correction</string> <!-- Settings screen title for advanced settings [CHAR LIMIT=33] --> <string name="settings_screen_advanced">Advanced</string> + <!-- Settings screen title for keyboard theme settings [CHAR LIMIT=33] --> + <string name="settings_screen_theme">Theme</string> <!-- Option name for including other IMEs in the language switch list [CHAR LIMIT=30] --> <string name="include_other_imes_in_language_switch_list">Switch to other input methods</string> @@ -58,11 +60,6 @@ <!-- Option summary for showing language switch key [CHAR LIMIT=65] --> <string name="show_language_switch_key_summary">Show when multiple input languages are enabled</string> - <!-- Option to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=30]--> - <string name="sliding_key_input_preview">Show slide indicator</string> - <!-- Option summary to enable sliding key input indicator. The user can see a rubber band-like effect during sliding key input. [CHAR LIMIT=66]--> - <string name="sliding_key_input_preview_summary">Display visual cue while sliding from Shift or Symbol keys</string> - <!-- Option for the dismiss delay of the key popup [CHAR LIMIT=25] --> <string name="key_preview_popup_dismiss_delay">Key popup dismiss delay</string> <!-- Description for delay for dismissing a popup on keypress: no delay [CHAR LIMIT=15] --> @@ -157,16 +154,19 @@ <string name="configure_input_method">Configure input methods</string> <!-- Title for input language selection screen --> - <string name="language_selection_title">Input languages</string> + <string name="language_selection_title">Languages</string> - <!-- Title of a preference to send feedback. [CHAR LIMIT=30]--> - <string name="send_feedback">Send feedback</string> + <!-- Title for the 'Help & feedback' settings fragment which shows a help page and has a button + for submitting feedback. [CHAR LIMIT=35] --> + <string name="help_and_feedback">Help & feedback</string> <!-- Preference for input language selection --> - <string name="select_language">Input languages</string> + <string name="select_language">Languages</string> <!-- Add to dictionary hint --> <string name="hint_add_to_dictionary">Touch again to save</string> + <!-- Add to dictionary hint --> + <string name="hint_add_to_dictionary_without_word">Touch here to save</string> <!-- Inform the user that a particular language has an available dictionary --> <string name="has_dictionary">Dictionary available</string> @@ -322,32 +322,10 @@ mobile devices. [CHAR LIMIT=25] --> <!-- Toast text to describe the same input style already exists [CHAR LIMIT=64]--> <string name="custom_input_style_already_exists">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME" example="English (Dvorak)">%s</xliff:g>"</string> - <!-- Title of the settings for key long press delay [CHAR LIMIT=35] --> - <string name="prefs_key_longpress_timeout_settings">Key long press delay</string> <!-- Title of the settings for keypress vibration duration [CHAR LIMIT=35] --> <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration</string> <!-- Title of the settings for keypress sound volume [CHAR LIMIT=35] --> <string name="prefs_keypress_sound_volume_settings">Keypress sound volume</string> - <!-- Title of the settings for key popup show up animation duration (in milliseconds) [CHAR LIMIT=35] --> - <string name="prefs_key_popup_show_up_duration_settings" translatable="false">Key popup show up duration</string> - <!-- Title of the settings for key popup dismiss animation duration (in milliseconds) [CHAR LIMIT=35] --> - <string name="prefs_key_popup_dismiss_duration_settings" translatable="false">Key popup dismiss duration</string> - <!-- Title of the settings for key popup show up animation start scale (in percentile) [CHAR LIMIT=35] --> - <string name="prefs_key_popup_show_up_start_scale_settings" translatable="false">Key popup show up start scale</string> - <!-- Title of the settings for key popup dismiss animation end scale (in percentile) [CHAR LIMIT=35] --> - <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> - <!-- 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] --> - <string name="read_external_dictionary_multiple_files_title">Select a dictionary file to install</string> - <!-- Title of the confirmation dialog to install a file as an external dictionary [CHAR LIMIT=50] --> - <string name="read_external_dictionary_confirm_install_message">Really install this file for <xliff:g id="LANGUAGE_NAME" example="English">%s</xliff:g>?</string> - <!-- Title for an error dialog that contains the details of the error in the body [CHAR LIMIT=80] --> - <string name="error">There was an error</string> - <!-- Title of the settings group for dumpping dictionary files that have been created on the device [CHAR LIMIT=35] --> - <string name="prefs_dump_dynamic_dicts" translatable="false">Dump dictionary</string> <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] --> <string name="button_default">Default</string> diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml index b139110ca..110f6b792 100644 --- a/java/res/values/themes-common.xml +++ b/java/res/values/themes-common.xml @@ -22,23 +22,24 @@ <style name="KeyboardIcons" /> <!-- Default theme values --> <style name="Keyboard"> - <item name="touchPositionCorrectionData">@array/touch_position_correction_data_default</item> <item name="rowHeight">25%p</item> - <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item> + <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> + <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> + <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> <item name="keyboardLeftPadding">@fraction/config_keyboard_left_padding</item> <item name="keyboardRightPadding">@fraction/config_keyboard_right_padding</item> + <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item> <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item> </style> <style name="KeyboardView"> + <!-- This keyBackground is needed to run unit tests based on {@link InputTestBase}. --> + <!-- TODO: Apply default {@link KeyboardTheme} to {@link InputTestBase} and remove this. --> <item name="keyBackground">@drawable/btn_keyboard_key_klp</item> - <item name="keyLetterSize">@fraction/config_key_letter_ratio</item> - <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio</item> - <item name="keyLabelSize">@fraction/config_key_label_ratio</item> - <item name="keyLargeLabelRatio">@fraction/config_key_large_label_ratio</item> - <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio</item> - <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio</item> - <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio</item> <item name="keyTypeface">normal</item> + <!-- A negative value to disable key text shadow layer. --> + <item name="keyTextShadowRadius">-1.0</item> <item name="keyHintLetterPadding">@dimen/config_key_hint_letter_padding</item> <item name="keyPopupHintLetterPadding">@dimen/config_key_popup_hint_letter_padding</item> <item name="keyShiftedLetterHintPadding">@dimen/config_key_shifted_letter_hint_padding</item> @@ -74,7 +75,6 @@ <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item> <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item> <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item> - <item name="keyPreviewHeight">@dimen/config_key_preview_height</item> <!-- TODO: consolidate key preview linger timeout with the key preview animation parameters. --> <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item> <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item> @@ -108,10 +108,7 @@ for instance delete button, need themed {@link KeyboardView} attributes. --> <style name="EmojiPalettesView" /> <style name="MoreKeysKeyboard" /> - <style - name="MoreKeysKeyboardView" - parent="MainKeyboardView" /> - <style name="MoreKeysKeyboardContainer" /> + <style name="MoreKeysKeyboardView" /> <style name="SuggestionStripView" /> <style name="SuggestionWord"> <item name="android:minWidth">@dimen/config_suggestion_min_width</item> @@ -130,8 +127,4 @@ <item name="android:singleLine">true</item> <item name="android:ellipsize">none</item> </style> - <style name="MoreKeysKeyboardAnimation"> - <item name="android:windowEnterAnimation">@anim/more_keys_keyboard_fadein</item> - <item name="android:windowExitAnimation">@anim/more_keys_keyboard_fadeout</item> - </style> </resources> diff --git a/java/res/values/themes-holo.xml b/java/res/values/themes-holo.xml new file mode 100644 index 000000000..9f1bd2f78 --- /dev/null +++ b/java/res/values/themes-holo.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Holo KeyboardView theme (ICS and KLP) --> + <style + name="KeyboardView.Holo" + parent="KeyboardView" + > + <item name="keyTypeface">bold</item> + <item name="keyLetterSize">@fraction/config_key_letter_ratio_holo</item> + <item name="keyLabelSize">@fraction/config_key_label_ratio_holo</item> + <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio_holo</item> + <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio_holo</item> + <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio_holo</item> + <item name="keyLabelOffCenterRatio">-175%</item> + <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio_holo</item> + <item name="keyHintLabelOffCenterRatio">200%</item> + <!-- U+2026: "…" HORIZONTAL ELLIPSIS --> + <item name="keyPopupHintLetter">…</item> + </style> +</resources> diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 6118ce177..bfbac0a94 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -25,7 +25,9 @@ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item> <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.ICS</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item> + <!-- Note: ICS theme uses the same style for both general more keys and action more keys. --> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item> + <item name="moreKeysKeyboardViewForActionStyle">@style/MoreKeysKeyboardView.ICS</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.ICS</item> <item name="suggestionWordStyle">@style/SuggestionWord.ICS</item> </style> @@ -33,23 +35,17 @@ name="Keyboard.ICS" parent="Keyboard" > - <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> - <item name="themeId">2</item> - <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> - <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + <!-- This should be aligned with KeyboardTheme.THEME_ID_* --> + <item name="themeId">ICS</item> </style> <style name="KeyboardView.ICS" - parent="KeyboardView" + parent="KeyboardView.Holo" > <item name="android:background">@drawable/keyboard_background_holo</item> <item name="keyBackground">@drawable/btn_keyboard_key_ics</item> <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_ics</item> <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_ics</item> - <item name="keyTypeface">bold</item> <item name="keyTextColor">@color/key_text_color_holo</item> <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item> <item name="functionalTextColor">@color/key_text_color_holo</item> @@ -58,15 +54,16 @@ <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_holo</item> <item name="keyPreviewTextColor">@color/key_text_color_holo</item> - <!-- A negative value to disable key text shadow layer. --> - <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.ICS" parent="KeyboardView.ICS" > <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item> + <item name="keyPreviewHeight">@dimen/config_key_preview_height_holo</item> <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> + <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item> + <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item> <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_ics</item> @@ -109,6 +106,7 @@ > <item name="android:background">@drawable/keyboard_popup_panel_background_ics</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item> + <item name="divider">@drawable/more_keys_divider</item> <item name="keyTypeface">normal</item> <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml index 193386062..36b1fc117 100644 --- a/java/res/values/themes-klp.xml +++ b/java/res/values/themes-klp.xml @@ -25,7 +25,9 @@ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.KLP</item> <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.KLP</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.KLP</item> + <!-- Note: KLP theme uses the same style for both general more keys and action more keys. --> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.KLP</item> + <item name="moreKeysKeyboardViewForActionStyle">@style/MoreKeysKeyboardView.KLP</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.KLP</item> <item name="suggestionWordStyle">@style/SuggestionWord.KLP</item> </style> @@ -33,23 +35,17 @@ name="Keyboard.KLP" parent="Keyboard" > - <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> - <item name="themeId">0</item> - <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> - <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + <!-- This should be aligned with KeyboardTheme.THEME_ID_* --> + <item name="themeId">KLP</item> </style> <style name="KeyboardView.KLP" - parent="KeyboardView" + parent="KeyboardView.Holo" > <item name="android:background">@drawable/keyboard_background_holo</item> <item name="keyBackground">@drawable/btn_keyboard_key_klp</item> <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_klp</item> <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_klp</item> - <item name="keyTypeface">bold</item> <item name="keyTextColor">@color/key_text_color_holo</item> <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item> <item name="functionalTextColor">@color/key_text_color_holo</item> @@ -58,15 +54,16 @@ <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_holo</item> <item name="keyPreviewTextColor">@color/key_text_color_holo</item> - <!-- A negative value to disable key text shadow layer. --> - <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.KLP" parent="KeyboardView.KLP" > <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_klp</item> + <item name="keyPreviewHeight">@dimen/config_key_preview_height_holo</item> <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> + <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_holo</item> + <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_holo</item> <item name="gestureFloatingPreviewTextColor">@color/highlight_color_klp</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item> <item name="gestureTrailColor">@color/highlight_color_klp</item> @@ -109,6 +106,7 @@ > <item name="android:background">@drawable/keyboard_popup_panel_background_klp</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item> + <item name="divider">@drawable/more_keys_divider</item> <item name="keyTypeface">normal</item> <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> diff --git a/java/res/values/themes-lxx-dark.xml b/java/res/values/themes-lxx-dark.xml index f5630102d..67f94f329 100644 --- a/java/res/values/themes-lxx-dark.xml +++ b/java/res/values/themes-lxx-dark.xml @@ -26,6 +26,7 @@ <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.LXX_Dark</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.LXX_Dark</item> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.LXX_Dark</item> + <item name="moreKeysKeyboardViewForActionStyle">@style/MoreKeysKeyboardView.LXX_Dark.Action</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.LXX_Dark</item> <item name="suggestionWordStyle">@style/SuggestionWord.LXX_Dark</item> </style> @@ -33,24 +34,17 @@ name="Keyboard.LXX_Dark" parent="Keyboard" > - <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> - <item name="themeId">4</item> - <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> - <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + <!-- This should be aligned with KeyboardTheme.THEME_ID_* --> + <item name="themeId">LXXDark</item> </style> <style name="KeyboardView.LXX_Dark" - parent="KeyboardView" + parent="KeyboardView.LXX" > - <item name="android:background">@color/keyboard_background_lxx_dark</item> + <item name="android:background">@drawable/keyboard_background_lxx_dark</item> <item name="keyBackground">@drawable/btn_keyboard_key_lxx_dark</item> <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_lxx_dark</item> <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_lxx_dark</item> - <item name="spacebarIconWidthRatio">0.9</item> - <item name="keyTypeface">normal</item> <item name="keyTextColor">@color/key_text_color_lxx_dark</item> <item name="keyTextInactivatedColor">@color/key_functional_text_color_lxx_dark</item> <item name="functionalTextColor">@color/key_text_color_lxx_dark</item> @@ -59,15 +53,17 @@ <item name="keyShiftedLetterHintInactivatedColor">@color/key_text_inactive_color_lxx_dark</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_text_color_lxx_dark</item> <item name="keyPreviewTextColor">@color/key_text_color_lxx_dark</item> - <!-- A negative value to disable key text shadow layer. --> - <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.LXX_Dark" parent="KeyboardView.LXX_Dark" > + <item name="moreKeysKeyboardForActionLayout">@layout/more_keys_keyboard_for_action_lxx</item> <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_dark</item> - <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> + <item name="keyPreviewHeight">@dimen/config_key_preview_height_lxx</item> + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_lxx</item> + <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item> + <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item> <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_dark</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_lxx_dark</item> <item name="gestureTrailColor">@color/gesture_trail_color_lxx_dark</item> @@ -108,10 +104,20 @@ > <item name="android:background">@drawable/keyboard_popup_panel_background_lxx_dark</item> <item name="keyBackground">@drawable/btn_keyboard_key_popup_lxx_dark</item> + <item name="divider">@drawable/more_keys_divider</item> <item name="keyTypeface">normal</item> <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> <style + name="MoreKeysKeyboardView.LXX_Dark.Action" + parent="MoreKeysKeyboardView.LXX_Dark" + > + <item name="android:background">@android:color/transparent</item> + <item name="keyBackground">@drawable/btn_keyboard_key_popup_action_lxx_dark</item> + <item name="divider">@null</item> + <item name="keyLabelFlags">keepBackgroundAspectRatio</item> + </style> + <style name="SuggestionStripView.LXX_Dark" parent="KeyboardView.LXX_Dark" > @@ -119,7 +125,7 @@ <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> - <item name="android:background">@color/suggestions_strip_background_lxx_dark</item> + <item name="android:background">@drawable/keyboard_suggest_strip_lxx_dark</item> <item name="android:src">@drawable/suggestions_strip_divider_lxx_dark</item> <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item> <item name="colorValidTypedWord">@color/typed_word_color_lxx_dark</item> diff --git a/java/res/values/themes-lxx-light.xml b/java/res/values/themes-lxx-light.xml index 48bd313f0..be817f46a 100644 --- a/java/res/values/themes-lxx-light.xml +++ b/java/res/values/themes-lxx-light.xml @@ -26,6 +26,7 @@ <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.LXX_Light</item> <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.LXX_Light</item> <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.LXX_Light</item> + <item name="moreKeysKeyboardViewForActionStyle">@style/MoreKeysKeyboardView.LXX_Light.Action</item> <item name="suggestionStripViewStyle">@style/SuggestionStripView.LXX_Light</item> <item name="suggestionWordStyle">@style/SuggestionWord.LXX_Light</item> </style> @@ -33,24 +34,17 @@ name="Keyboard.LXX_Light" parent="Keyboard" > - <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] --> - <item name="themeId">3</item> - <item name="keyboardTopPadding">@fraction/config_keyboard_top_padding_holo</item> - <item name="keyboardBottomPadding">@fraction/config_keyboard_bottom_padding_holo</item> - <item name="horizontalGap">@fraction/config_key_horizontal_gap_holo</item> - <item name="verticalGap">@fraction/config_key_vertical_gap_holo</item> - <item name="touchPositionCorrectionData">@array/touch_position_correction_data_holo</item> + <!-- This should be aligned with KeyboardTheme.THEME_ID_* --> + <item name="themeId">LXXLight</item> </style> <style name="KeyboardView.LXX_Light" - parent="KeyboardView" + parent="KeyboardView.LXX" > - <item name="android:background">@color/keyboard_background_lxx_light</item> + <item name="android:background">@drawable/keyboard_background_lxx_light</item> <item name="keyBackground">@drawable/btn_keyboard_key_lxx_light</item> <item name="functionalKeyBackground">@drawable/btn_keyboard_key_functional_lxx_light</item> <item name="spacebarBackground">@drawable/btn_keyboard_spacebar_lxx_light</item> - <item name="spacebarIconWidthRatio">0.9</item> - <item name="keyTypeface">normal</item> <item name="keyTextColor">@color/key_text_color_lxx_light</item> <item name="keyTextInactivatedColor">@color/key_text_inactive_color_lxx_light</item> <item name="functionalTextColor">@color/key_functional_text_color_lxx_light</item> @@ -59,15 +53,17 @@ <item name="keyShiftedLetterHintInactivatedColor">@color/key_text_inactive_color_lxx_light</item> <item name="keyShiftedLetterHintActivatedColor">@color/key_text_color_lxx_light</item> <item name="keyPreviewTextColor">@color/key_text_color_lxx_light</item> - <!-- A negative value to disable key text shadow layer. --> - <item name="keyTextShadowRadius">-1.0</item> </style> <style name="MainKeyboardView.LXX_Light" parent="KeyboardView.LXX_Light" > + <item name="moreKeysKeyboardForActionLayout">@layout/more_keys_keyboard_for_action_lxx</item> <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_lxx_light</item> - <item name="keyPreviewOffset">@dimen/config_key_preview_offset_holo</item> + <item name="keyPreviewHeight">@dimen/config_key_preview_height_lxx</item> + <item name="keyPreviewOffset">@dimen/config_key_preview_offset_lxx</item> + <item name="keyPreviewShowUpAnimator">@anim/key_preview_show_up_lxx</item> + <item name="keyPreviewDismissAnimator">@anim/key_preview_dismiss_lxx</item> <item name="gestureFloatingPreviewTextColor">@color/auto_correct_color_lxx_light</item> <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_lxx_light</item> <item name="gestureTrailColor">@color/gesture_trail_color_lxx_light</item> @@ -107,12 +103,21 @@ parent="KeyboardView.LXX_Light" > <item name="android:background">@drawable/keyboard_popup_panel_background_lxx_light</item> - <!-- Reuse KLP key background --> - <item name="keyBackground">@drawable/btn_keyboard_key_popup_klp</item> + <item name="keyBackground">@drawable/btn_keyboard_key_popup_lxx_light</item> + <item name="divider">@drawable/more_keys_divider</item> <item name="keyTypeface">normal</item> <item name="verticalCorrection">@dimen/config_more_keys_keyboard_vertical_correction_holo</item> </style> <style + name="MoreKeysKeyboardView.LXX_Light.Action" + parent="MoreKeysKeyboardView.LXX_Light" + > + <item name="android:background">@android:color/transparent</item> + <item name="keyBackground">@drawable/btn_keyboard_key_popup_action_lxx_light</item> + <item name="divider">@null</item> + <item name="keyLabelFlags">keepBackgroundAspectRatio</item> + </style> + <style name="SuggestionStripView.LXX_Light" parent="KeyboardView.LXX_Light" > @@ -120,7 +125,7 @@ <item name="centerSuggestionPercentile">@fraction/config_center_suggestion_percentile</item> <item name="maxMoreSuggestionsRow">@integer/config_max_more_suggestions_row</item> <item name="minMoreSuggestionsWidth">@fraction/config_min_more_suggestions_width</item> - <item name="android:background">@color/suggestions_strip_background_lxx_light</item> + <item name="android:background">@drawable/keyboard_suggest_strip_lxx_light</item> <item name="android:src">@drawable/suggestions_strip_divider_lxx_light</item> <item name="suggestionStripOptions">autoCorrectBold|validTypedWordBold</item> <item name="colorValidTypedWord">@color/typed_word_color_lxx_light</item> diff --git a/java/res/values/themes-lxx.xml b/java/res/values/themes-lxx.xml new file mode 100644 index 000000000..c72188871 --- /dev/null +++ b/java/res/values/themes-lxx.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- LXX KeyboardView theme (LXX_Light and LXX_Dark) --> + <style + name="KeyboardView.LXX" + parent="KeyboardView" + > + <item name="keyTypeface">normal</item> + <item name="keyLetterSize">@fraction/config_key_letter_ratio_lxx</item> + <item name="keyLabelSize">@fraction/config_key_label_ratio_lxx</item> + <item name="keyHintLetterRatio">@fraction/config_key_hint_letter_ratio_lxx</item> + <item name="keyShiftedLetterHintRatio">@fraction/config_key_shifted_letter_hint_ratio_lxx</item> + <item name="keyLargeLetterRatio">@fraction/config_key_large_letter_ratio_lxx</item> + <item name="keyLabelOffCenterRatio">-80%</item> + <item name="keyHintLabelRatio">@fraction/config_key_hint_label_ratio_lxx</item> + <item name="keyHintLabelOffCenterRatio">300%</item> + <item name="keyLabelFlags">alignHintLabelToBottom</item> + <item name="spacebarIconWidthRatio">0.9</item> + <!-- No popup hint letter --> + <item name="keyPopupHintLetter"></item> + </style> +</resources> diff --git a/java/res/xml-sw600dp/key_comma.xml b/java/res/xml-sw600dp/key_comma.xml index 67199e237..e616a8d70 100644 --- a/java/res/xml-sw600dp/key_comma.xml +++ b/java/res/xml-sw600dp/key_comma.xml @@ -2,7 +2,7 @@ <!-- /* ** -** Copyright 2014, The Android Open Source Project +** 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. @@ -21,27 +21,36 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- The table comma key which may have settings as popup key. --> - <!-- Kept as a separate file for cleaner overriding by an overlay. --> - <key-style - latin:styleName="baseTabletCommaKeyStyle" - latin:keySpec="!text/keyspec_tablet_comma" - latin:keyHintLabel="!text/keyhintlabel_tablet_comma" - latin:keyLabelFlags="hasPopupHint" - latin:parentStyle="hasShiftedLetterHintStyle" /> <switch> <case - latin:clobberSettingsKey="true" + latin:mode="url" > <Key - latin:moreKeys="!text/morekeys_tablet_comma" - latin:keyStyle="baseTabletCommaKeyStyle" /> + latin:keySpec="/" + latin:keyStyle="settingsMoreKeysStyle" /> + </case> + <case + latin:mode="email" + > + <Key + latin:keySpec="\@" + latin:keyStyle="settingsMoreKeysStyle" /> + </case> + <case + latin:keyboardLayoutSet="dvorak" + > + <Key + latin:keySpec="!" + latin:moreKeys="!text/morekeys_exclamation,%" + latin:keyStyle="settingsMoreKeysStyle" /> </case> - <!-- clobberSettingsKey="false" --> <default> <Key - latin:moreKeys="!text/morekeys_tablet_comma,!text/keyspec_settings" - latin:keyStyle="baseTabletCommaKeyStyle" /> + latin:keySpec="!text/keyspec_tablet_comma" + latin:moreKeys="!text/morekeys_tablet_comma,%" + latin:keyHintLabel="!text/keyhintlabel_tablet_comma" + latin:keyLabelFlags="hasPopupHint" + latin:keyStyle="settingsMoreKeysStyle" /> </default> </switch> </merge> diff --git a/java/res/xml-sw600dp/key_period.xml b/java/res/xml-sw600dp/key_period.xml index d2909d82d..df1daf792 100644 --- a/java/res/xml-sw600dp/key_period.xml +++ b/java/res/xml-sw600dp/key_period.xml @@ -35,6 +35,13 @@ latin:moreKeys="!autoColumnOrder!8,\\,,.,',#,),(,/,;,@,:,-,",+,\\%,&" latin:backgroundType="functional" /> </case> + <case + latin:keyboardLayoutSet="dvorak" + > + <Key + latin:keySpec="\?" + latin:moreKeys="!text/morekeys_tablet_period,!text/morekeys_question" /> + </case> <default> <Key latin:keySpec="!text/keyspec_tablet_period" diff --git a/java/res/xml-sw600dp/key_space_5kw.xml b/java/res/xml-sw600dp/key_space_7kw.xml index 8302184c7..3311f812a 100644 --- a/java/res/xml-sw600dp/key_space_5kw.xml +++ b/java/res/xml-sw600dp/key_space_7kw.xml @@ -34,7 +34,7 @@ latin:keyStyle="languageSwitchKeyStyle" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="27.0%p" /> + latin:keyWidth="45.0%p" /> <Key latin:keyStyle="zwnjKeyStyle" /> </case> @@ -44,7 +44,7 @@ > <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="36.0%p" /> + latin:keyWidth="54.0%p" /> <Key latin:keyStyle="zwnjKeyStyle" /> </case> @@ -55,13 +55,13 @@ latin:keyStyle="languageSwitchKeyStyle" /> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="36.0%p" /> + latin:keyWidth="54.0%p" /> </case> <!-- languageSwitchKeyEnabled="false" --> <default> <Key latin:keyStyle="spaceKeyStyle" - latin:keyWidth="45.0%p" /> + latin:keyWidth="63.0%p" /> </default> </switch> </merge> diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index 877c796c7..006cda370 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -36,6 +36,9 @@ </default> </switch> <!-- Base key style for the key which may have settings key as more keys. --> + <key-style + latin:styleName="baseSettingsMoreKeysStyle" + latin:parentStyle="hasShiftedLetterHintStyle" /> <include latin:keyboardLayout="@xml/key_styles_settings" /> <!-- Functional key styles --> @@ -83,6 +86,7 @@ <key-style latin:styleName="spaceKeyStyle" latin:keySpec="!icon/space_key|!code/key_space" + latin:backgroundType="spacebar" latin:keyActionFlags="noKeyPreview|enableLongPress" /> <!-- U+200C: ZERO WIDTH NON-JOINER U+200D: ZERO WIDTH JOINER --> @@ -105,7 +109,7 @@ latin:altCode="!code/key_space" /> <key-style latin:styleName="emojiKeyStyle" - latin:keySpec="!icon/emoji_key|!code/key_emoji" + latin:keySpec="!icon/emoji_normal_key|!code/key_emoji" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> <key-style diff --git a/java/res/xml-sw600dp/key_styles_enter.xml b/java/res/xml-sw600dp/key_styles_enter.xml index 740bf3543..63ef2f8f9 100644 --- a/java/res/xml-sw600dp/key_styles_enter.xml +++ b/java/res/xml-sw600dp/key_styles_enter.xml @@ -21,22 +21,19 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- TODO: Stop using many conditional cases for keyspec_emoji_key. There are way too many to maintain. --> <!-- Navigate more keys style --> + <include latin:keyboardLayout="@xml/key_styles_navigate_more_keys" /> <switch> - <!-- latin:passwordInput="true" --> <case latin:imeAction="actionNext" latin:navigatePrevious="true" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> <case latin:imeAction="actionNext" - latin:navigatePrevious="false" > <key-style latin:styleName="navigateMoreKeysStyle" /> @@ -47,12 +44,10 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:navigateNext="false" > <key-style latin:styleName="navigateMoreKeysStyle" /> @@ -63,47 +58,50 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_action_previous,!text/keyspec_action_next" /> + latin:parentStyle="navigatePreviousNextMoreKeysStyle" /> </case> <case latin:navigateNext="true" - latin:navigatePrevious="false" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case - latin:navigateNext="false" latin:navigatePrevious="true" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> - <case - latin:navigateNext="false" - latin:navigatePrevious="false" - > + <default> <key-style latin:styleName="navigateMoreKeysStyle" /> + </default> + </switch> + <!-- Enter key style --> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="defaultEnterKeyStyle" + latin:keySpec="!icon/enter_key|!code/key_enter" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" + latin:parentStyle="navigateMoreKeysStyle" /> </case> + <!-- keyboardTheme="LXXLight|LXXDark" --> <default> <key-style - latin:styleName="navigateMoreKeysStyle" /> + latin:styleName="defaultEnterKeyStyle" + latin:keySpec="!icon/enter_key|!code/key_enter" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor|keepBackgroundAspectRatio" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" + latin:parentStyle="navigateMoreKeysStyle" /> </default> </switch> - <!-- Enter key style --> - <key-style - latin:styleName="defaultEnterKeyStyle" - latin:keySpec="!icon/enter_key|!code/key_enter" - latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" - latin:keyActionFlags="noKeyPreview" - latin:backgroundType="action" - latin:parentStyle="navigateMoreKeysStyle" /> + <include latin:keyboardLayout="@xml/key_styles_actions" /> <switch> <!-- Shift + Enter in textMultiLine field. --> <case @@ -117,120 +115,52 @@ </case> <case latin:imeAction="actionGo" - latin:isIconDefined="go_key" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!icon/go_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionGo" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_go_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="goActionKeyStyle" /> </case> <case latin:imeAction="actionNext" - latin:isIconDefined="next_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/next_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionNext" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_next_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="nextActionKeyStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:isIconDefined="previous_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/previous_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionPrevious" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_previous_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="previousActionKeyStyle" /> </case> <case latin:imeAction="actionDone" - latin:isIconDefined="done_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/done_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionDone" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_done_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="doneActionKeyStyle" /> </case> <case latin:imeAction="actionSend" - latin:isIconDefined="send_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/send_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionSend" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_send_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionSearch" - latin:isIconDefined="search_key" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!icon/search_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="sendActionKeyStyle" /> </case> <case latin:imeAction="actionSearch" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_search_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="searchActionKeyStyle" /> </case> <case latin:imeAction="actionCustomLabel" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="dummy_label|!code/key_enter" - latin:keyLabelFlags="fromCustomActionLabel" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="customLabelActionKeyStyle" /> </case> <!-- imeAction is either actionNone or actionUnspecified. --> <default> diff --git a/java/res/xml-sw600dp/row_dvorak4.xml b/java/res/xml-sw600dp/row_dvorak4.xml deleted file mode 100644 index ab2b5603d..000000000 --- a/java/res/xml-sw600dp/row_dvorak4.xml +++ /dev/null @@ -1,49 +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" -> - <Row - latin:keyWidth="9.0%p" - latin:backgroundType="functional" - > - <Key - latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyboardLayout="@xml/key_settings" /> - <Key - latin:keySpec="_" - latin:keyHintLabel="-" - latin:moreKeys="-" - latin:keyStyle="hasShiftedLetterHintStyle" /> - <include - latin:keyXPos="28.0%p" - latin:keyboardLayout="@xml/key_space_5kw" - latin:backgroundType="normal" /> - <include - latin:keyboardLayout="@xml/key_f1" /> - <include - latin:keyboardLayout="@xml/key_question_exclamation" /> - <include - latin:keyboardLayout="@xml/key_f2" /> - </Row> -</merge> diff --git a/java/res/xml-sw600dp/row_pcqwerty5.xml b/java/res/xml-sw600dp/row_pcqwerty5.xml index ac07f11c2..4dcae1455 100644 --- a/java/res/xml-sw600dp/row_pcqwerty5.xml +++ b/java/res/xml-sw600dp/row_pcqwerty5.xml @@ -50,6 +50,6 @@ latin:keyXPos="-9.0%p" latin:keyWidth="9.0%p" latin:backgroundType="functional" - latin:keyboardLayout="@xml/key_f2" /> + latin:keyboardLayout="@xml/key_emoji" /> </Row> </merge> diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml index 0eb86f2d4..ed7150de4 100644 --- a/java/res/xml-sw600dp/row_qwerty4.xml +++ b/java/res/xml-sw600dp/row_qwerty4.xml @@ -30,18 +30,14 @@ latin:keyWidth="10.0%p" /> <include latin:keyboardLayout="@xml/key_comma" /> - <Key - latin:keySpec="_" /> <!-- Space key. --> <include - latin:keyXPos="28.0%p" - latin:keyboardLayout="@xml/key_space_5kw" + latin:keyXPos="19.0%p" + latin:keyboardLayout="@xml/key_space_7kw" latin:backgroundType="normal" /> <include - latin:keyboardLayout="@xml/key_f1" /> - <include latin:keyboardLayout="@xml/key_period" /> <include - latin:keyboardLayout="@xml/key_f2" /> + latin:keyboardLayout="@xml/key_emoji" /> </Row> </merge> diff --git a/java/res/xml-sw600dp/rows_dvorak.xml b/java/res/xml-sw600dp/rows_dvorak.xml index 88592676d..c8f5e3aa2 100644 --- a/java/res/xml-sw600dp/rows_dvorak.xml +++ b/java/res/xml-sw600dp/rows_dvorak.xml @@ -53,6 +53,8 @@ latin:keyStyle="shiftKeyStyle" latin:keyWidth="fillRight" /> </Row> + <!-- Dvorak layout shares almost the same row with Qwerty layout. + The difference is defined in xml-sw600dp/row_qwerty4.xml. --> <include - latin:keyboardLayout="@xml/row_dvorak4" /> + latin:keyboardLayout="@xml/row_qwerty4" /> </merge> diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml index 757e77933..7a4700d5a 100644 --- a/java/res/xml-sw600dp/rows_number_normal.xml +++ b/java/res/xml-sw600dp/rows_number_normal.xml @@ -141,9 +141,8 @@ </Row> <Row> <Key - latin:keyStyle="spaceKeyStyle" - latin:keyWidth="30%p" - latin:backgroundType="functional" /> + latin:keyStyle="tabletNumSpaceKeyStyle" + latin:keyWidth="30%p" /> <Key latin:keyStyle="numStarKeyStyle" latin:keyXPos="31%p" /> diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml index 9022bc532..612397a90 100644 --- a/java/res/xml-sw600dp/rows_phone.xml +++ b/java/res/xml-sw600dp/rows_phone.xml @@ -107,9 +107,8 @@ </Row> <Row> <Key - latin:keyStyle="spaceKeyStyle" - latin:keyWidth="30%p" - latin:backgroundType="functional" /> + latin:keyStyle="tabletNumSpaceKeyStyle" + latin:keyWidth="30%p" /> <Key latin:keyStyle="numStarKeyStyle" latin:keyXPos="31%p" /> diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml index a915c3351..05e7c685e 100644 --- a/java/res/xml-sw600dp/rows_symbols.xml +++ b/java/res/xml-sw600dp/rows_symbols.xml @@ -70,6 +70,6 @@ <include latin:keyboardLayout="@xml/row_symbols4" /> <include - latin:keyboardLayout="@xml/key_f2" /> + latin:keyboardLayout="@xml/key_emoji" /> </Row> </merge> diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml index 7ead4d5b1..70ac42eb9 100644 --- a/java/res/xml-sw600dp/rows_symbols_shift.xml +++ b/java/res/xml-sw600dp/rows_symbols_shift.xml @@ -72,6 +72,6 @@ <include latin:keyboardLayout="@xml/row_symbols_shift4" /> <include - latin:keyboardLayout="@xml/key_f2" /> + latin:keyboardLayout="@xml/key_emoji" /> </Row> </merge> diff --git a/java/res/xml/key_f1.xml b/java/res/xml/key_comma.xml index 7bd7385a1..cf919a85d 100644 --- a/java/res/xml/key_f1.xml +++ b/java/res/xml/key_comma.xml @@ -23,6 +23,15 @@ > <switch> <case + latin:keyboardLayoutSet="dvorak" + > + <Key + latin:keySpec="q" + latin:moreKeys="!text/morekeys_q,%" + latin:backgroundType="normal" + latin:keyStyle="settingsMoreKeysStyle" /> + </case> + <case latin:mode="url" > <Key diff --git a/java/res/xml/key_f2.xml b/java/res/xml/key_emoji.xml index 473dd210a..473dd210a 100644 --- a/java/res/xml/key_f2.xml +++ b/java/res/xml/key_emoji.xml diff --git a/java/res/xml/key_period.xml b/java/res/xml/key_period.xml index e1d4bbdf7..fc27c0235 100644 --- a/java/res/xml/key_period.xml +++ b/java/res/xml/key_period.xml @@ -48,6 +48,14 @@ latin:moreKeys="!text/morekeys_punctuation" latin:backgroundType="functional" /> </case> + <case + latin:keyboardLayoutSet="dvorak" + > + <Key + latin:keySpec="z" + latin:keyLabelFlags="hasPopupHint" + latin:moreKeys="!text/morekeys_punctuation,!text/morekeys_z" /> + </case> <default> <Key latin:keySpec="!text/keyspec_period" diff --git a/java/res/xml/key_space_symbols.xml b/java/res/xml/key_space_symbols.xml index 0ce522889..047de9f24 100644 --- a/java/res/xml/key_space_symbols.xml +++ b/java/res/xml/key_space_symbols.xml @@ -22,7 +22,6 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > <Key - latin:backgroundType="normal" latin:keyStyle="spaceKeyStyle" latin:keyWidth="30%p" /> </merge> diff --git a/java/res/xml/key_styles_actions.xml b/java/res/xml/key_styles_actions.xml new file mode 100644 index 000000000..83901cad9 --- /dev/null +++ b/java/res/xml/key_styles_actions.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- Go key --> + <switch> + <case latin:isIconDefined="go_key"> + <key-style + latin:styleName="goActionKeyStyle" + latin:keySpec="!icon/go_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="goActionKeyStyle" + latin:keySpec="!text/label_go_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <!-- Next key --> + <switch> + <case latin:isIconDefined="next_key"> + <key-style + latin:styleName="nextActionKeyStyle" + latin:keySpec="!icon/next_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="nextActionKeyStyle" + latin:keySpec="!text/label_next_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <!-- Previous key --> + <switch> + <case latin:isIconDefined="previous_key"> + <key-style + latin:styleName="previousActionKeyStyle" + latin:keySpec="!icon/previous_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="previousActionKeyStyle" + latin:keySpec="!text/label_previous_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <!-- Done key --> + <switch> + <case latin:isIconDefined="done_key"> + <key-style + latin:styleName="doneActionKeyStyle" + latin:keySpec="!icon/done_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="doneActionKeyStyle" + latin:keySpec="!text/label_done_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <!-- Send key --> + <switch> + <case latin:isIconDefined="send_key"> + <key-style + latin:styleName="sendActionKeyStyle" + latin:keySpec="!icon/send_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="sendActionKeyStyle" + latin:keySpec="!text/label_send_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <!-- Seartch key --> + <switch> + <case latin:isIconDefined="search_key"> + <key-style + latin:styleName="searchActionKeyStyle" + latin:keySpec="!icon/search_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <default> + <key-style + latin:styleName="searchActionKeyStyle" + latin:keySpec="!text/label_search_key|!code/key_enter" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="customLabelActionKeyStyle" + latin:keySpec="dummy_label|!code/key_enter" + latin:keyLabelFlags="fromCustomActionLabel" + latin:backgroundType="action" + latin:parentStyle="defaultEnterKeyStyle" /> + </case> + <!-- keyboardTheme="LXXLight|LXXDark" --> + <default> + <key-style + latin:styleName="customLabelActionKeyStyle" + latin:keySpec="dummy_label|!code/key_enter" + latin:keyLabelFlags="fromCustomActionLabel" + latin:backgroundType="functional" + latin:parentStyle="defaultEnterKeyStyle" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index bc739f7d0..b36ddf236 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -36,6 +36,8 @@ </default> </switch> <!-- Base key style for the key which may have settings key as more keys. --> + <key-style + latin:styleName="baseSettingsMoreKeysStyle" /> <include latin:keyboardLayout="@xml/key_styles_settings" /> <!-- Functional key styles --> @@ -78,17 +80,31 @@ latin:keyActionFlags="isRepeatable|noKeyPreview" latin:backgroundType="functional" /> <!-- emojiKeyStyle must be defined before including @xml/key_syles_enter. --> - <key-style - latin:styleName="emojiKeyStyle" - latin:keySpec="!icon/emoji_key|!code/key_emoji" - latin:keyActionFlags="noKeyPreview" - latin:backgroundType="action" /> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="emojiKeyStyle" + latin:keySpec="!icon/emoji_action_key|!code/key_emoji" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" /> + </case> + <!-- keyboardTheme="LXXLight|LXXDark" --> + <default> + <key-style + latin:styleName="emojiKeyStyle" + latin:keySpec="!icon/emoji_action_key|!code/key_emoji" + latin:keyLabelFlags="keepBackgroundAspectRatio" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" /> + </default> + </switch> <include latin:keyboardLayout="@xml/key_styles_enter" /> <!-- TODO: Currently there is no way to specify icon alignment per theme. --> <key-style latin:styleName="spaceKeyStyle" latin:keySpec="!icon/space_key|!code/key_space" + latin:backgroundType="spacebar" latin:keyActionFlags="noKeyPreview|enableLongPress" /> <!-- U+200C: ZERO WIDTH NON-JOINER U+200D: ZERO WIDTH JOINER --> diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml index 900c9bb7a..cfe9a90a1 100644 --- a/java/res/xml/key_styles_currency.xml +++ b/java/res/xml/key_styles_currency.xml @@ -84,7 +84,6 @@ lo: Lao (Kip) mn: Mongolian (Tugrik) ne: Nepali (Nepalese Rupee) - ta_IN: Tamil (Tamil Rupee) th: Thai (Baht) uk: Ukrainian (Hryvnia) vi: Vietnamese (Dong) --> @@ -92,29 +91,24 @@ its unicode, although there is no font glyph for it as of November 2012. --> <!-- TODO: The currency sign of Armenian Dram was created in 2012 and assigned U+058F for its unicode, although there is no font glyph for it as of September 2013. --> - <case latin:languageCode="fa|hi|iw|lo|mn|ne|ta|th|uk|vi"> - <!-- U+00A3: "£" POUND SIGN - U+20AC: "€" EURO SIGN - U+00A2: "¢" CENT SIGN --> - <key-style - latin:styleName="currencyKeyStyle" - latin:keySpec="!text/keyspec_currency" - latin:moreKeys="!text/morekeys_currency" /> - <key-style - latin:styleName="moreCurrency1KeyStyle" - latin:keySpec="£" /> - <key-style - latin:styleName="moreCurrency2KeyStyle" - latin:keySpec="€" /> - <key-style - latin:styleName="moreCurrency3KeyStyle" - latin:keySpec="$" - latin:moreKeys="¢" /> - <key-style - latin:styleName="moreCurrency4KeyStyle" - latin:keySpec="¢" /> + <!-- TODO: The currency sign of Russian Ruble was created in 2014 and assigned U+20BD for + its unicode, although there is no font glyph for it as of August 2014. --> + <case latin:languageCode="fa|hi|iw|lo|mn|ne|th|uk|vi"> + <include latin:keyboardLayout="@xml/key_styles_currency_generic" /> + </case> + <!-- si_LK: Sinhala (Sri Lanka) (Sri Lanka Rupee) + ta_LK: Tamil (Sri Lanka) (Sri Lanka Rupee) --> + <case latin:countryCode="LK"> + <include latin:keyboardLayout="@xml/key_styles_currency_generic" /> </case> - <!-- IN: India (Rupee) --> + <!-- bn_IN: Bengali (India) (Indian Rupee) + en_IN: English (India) (Indian Rupee) + kn_IN: Kannada (India) (Indian Rupee) + ml_IN: Malayalam (India) (Indian Rupee) + mr_IN: Marathi (India) (Indian Rupee) + ta_IN: Tamil (India) (Indian Rupee) + te_IN: Telugu (India) (Indian Rupee) + --> <case latin:countryCode="IN"> <!-- U+20B9: "₹" INDIAN RUPEE SIGN U+00A3: "£" POUND SIGN @@ -123,7 +117,7 @@ <key-style latin:styleName="currencyKeyStyle" latin:keySpec="₹" - latin:moreKeys="!text/morekeys_currency" /> + latin:moreKeys="!text/morekeys_currency_generic" /> <key-style latin:styleName="moreCurrency1KeyStyle" latin:keySpec="£" /> diff --git a/java/res/xml/key_styles_currency_generic.xml b/java/res/xml/key_styles_currency_generic.xml new file mode 100644 index 000000000..888b5b2c9 --- /dev/null +++ b/java/res/xml/key_styles_currency_generic.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <!-- U+00A3: "£" POUND SIGN + U+20AC: "€" EURO SIGN + U+00A2: "¢" CENT SIGN --> + <key-style + latin:styleName="currencyKeyStyle" + latin:keySpec="!text/keyspec_currency" + latin:moreKeys="!text/morekeys_currency_generic" /> + <key-style + latin:styleName="moreCurrency1KeyStyle" + latin:keySpec="£" /> + <key-style + latin:styleName="moreCurrency2KeyStyle" + latin:keySpec="€" /> + <key-style + latin:styleName="moreCurrency3KeyStyle" + latin:keySpec="$" + latin:moreKeys="¢" /> + <key-style + latin:styleName="moreCurrency4KeyStyle" + latin:keySpec="¢" /> +</merge> diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml index 50530e1bf..564f465e9 100644 --- a/java/res/xml/key_styles_enter.xml +++ b/java/res/xml/key_styles_enter.xml @@ -21,8 +21,8 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- TODO: Stop using many conditional cases for keyspec_emoji_key. There are way too many to maintain. --> <!-- Navigate more keys style --> + <include latin:keyboardLayout="@xml/key_styles_navigate_more_keys" /> <switch> <!-- latin:passwordInput="true" --> <case @@ -32,12 +32,10 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> <case latin:imeAction="actionNext" - latin:navigatePrevious="false" latin:passwordInput="true" > <key-style @@ -50,12 +48,10 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:navigateNext="false" latin:passwordInput="true" > <key-style @@ -68,32 +64,25 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_action_previous,!text/keyspec_action_next" /> + latin:parentStyle="navigatePreviousNextMoreKeysStyle" /> </case> <case latin:navigateNext="true" - latin:navigatePrevious="false" latin:passwordInput="true" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case - latin:navigateNext="false" latin:navigatePrevious="true" latin:passwordInput="true" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> <case - latin:navigateNext="false" - latin:navigatePrevious="false" latin:passwordInput="true" > <key-style @@ -107,12 +96,10 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> <case latin:imeAction="actionNext" - latin:navigatePrevious="false" latin:mode="email|url|phone|number|date|time|datetime" > <key-style @@ -125,12 +112,10 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:navigateNext="false" latin:mode="email|url|phone|number|date|time|datetime" > <key-style @@ -143,32 +128,25 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_action_previous,!text/keyspec_action_next" /> + latin:parentStyle="navigatePreviousNextMoreKeysStyle" /> </case> <case latin:navigateNext="true" - latin:navigatePrevious="false" latin:mode="email|url|phone|number|date|time|datetime" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_next" /> + latin:parentStyle="navigateNextMoreKeysStyle" /> </case> <case - latin:navigateNext="false" latin:navigatePrevious="true" latin:mode="email|url|phone|number|date|time|datetime" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_action_previous" /> + latin:parentStyle="navigatePreviousMoreKeysStyle" /> </case> <case - latin:navigateNext="false" - latin:navigatePrevious="false" latin:mode="email|url|phone|number|date|time|datetime" > <key-style @@ -181,17 +159,14 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous" /> + latin:parentStyle="navigateEmojiPreviousMoreKeysStyle" /> </case> <case latin:imeAction="actionNext" - latin:navigatePrevious="false" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:parentStyle="navigateEmojiMoreKeysStyle" /> </case> <case latin:imeAction="actionPrevious" @@ -199,17 +174,14 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_next" /> + latin:parentStyle="navigateEmojiNextMoreKeysStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:navigateNext="false" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:parentStyle="navigateEmojiMoreKeysStyle" /> </case> <case latin:navigateNext="true" @@ -217,52 +189,51 @@ > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous,!text/keyspec_action_next" /> + latin:parentStyle="navigateEmojiPreviousNextMoreKeysStyle" /> </case> <case latin:navigateNext="true" - latin:navigatePrevious="false" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_next" /> + latin:parentStyle="navigateEmojiNextMoreKeysStyle" /> </case> <case - latin:navigateNext="false" latin:navigatePrevious="true" > <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous" /> + latin:parentStyle="navigateEmojiPreviousMoreKeysStyle" /> </case> - <case - latin:navigateNext="false" - latin:navigatePrevious="false" - > + <default> <key-style latin:styleName="navigateMoreKeysStyle" - latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:parentStyle="navigateEmojiMoreKeysStyle" /> + </default> + </switch> + <!-- Enter key style --> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="defaultEnterKeyStyle" + latin:keySpec="!icon/enter_key|!code/key_enter" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" + latin:parentStyle="navigateMoreKeysStyle" /> </case> + <!-- keyboardTheme="LXXLight|LXXDark" --> <default> <key-style - latin:styleName="navigateMoreKeysStyle" /> + latin:styleName="defaultEnterKeyStyle" + latin:keySpec="!icon/enter_key|!code/key_enter" + latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor|keepBackgroundAspectRatio" + latin:keyActionFlags="noKeyPreview" + latin:backgroundType="action" + latin:parentStyle="navigateMoreKeysStyle" /> </default> </switch> - <!-- Enter key style --> - <key-style - latin:styleName="defaultEnterKeyStyle" - latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor" - latin:keyActionFlags="noKeyPreview" - latin:backgroundType="action" - latin:parentStyle="navigateMoreKeysStyle" /> - <key-style - latin:styleName="shiftEnterKeyStyle" - latin:keySpec="!icon/enter_key|!code/key_shift_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + <include latin:keyboardLayout="@xml/key_styles_actions" /> <switch> <!-- Shift + Enter in textMultiLine field. --> <case @@ -271,7 +242,8 @@ > <key-style latin:styleName="enterKeyStyle" - latin:parentStyle="shiftEnterKeyStyle" /> + latin:keySpec="!icon/enter_key|!code/key_shift_enter" + latin:parentStyle="defaultEnterKeyStyle" /> </case> <!-- Smiley in textShortMessage field. This <case> should be after Shift + Enter <case> and before any of action <case>. --> @@ -284,126 +256,57 @@ </case> <case latin:imeAction="actionGo" - latin:isIconDefined="go_key" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!icon/go_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionGo" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_go_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionNext" - latin:isIconDefined="next_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/next_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="goActionKeyStyle" /> </case> <case latin:imeAction="actionNext" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_next_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="nextActionKeyStyle" /> </case> <case latin:imeAction="actionPrevious" - latin:isIconDefined="previous_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/previous_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionPrevious" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_previous_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionDone" - latin:isIconDefined="done_key" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!icon/done_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="previousActionKeyStyle" /> </case> <case latin:imeAction="actionDone" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_done_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="doneActionKeyStyle" /> </case> <case latin:imeAction="actionSend" - latin:isIconDefined="send_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/send_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionSend" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_send_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="sendActionKeyStyle" /> </case> <case latin:imeAction="actionSearch" - latin:isIconDefined="search_key" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/search_key|!code/key_enter" - latin:backgroundType="action" - latin:parentStyle="defaultEnterKeyStyle" /> - </case> - <case - latin:imeAction="actionSearch" - > - <key-style - latin:styleName="enterKeyStyle" - latin:keySpec="!text/label_search_key|!code/key_enter" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="searchActionKeyStyle" /> </case> <case latin:imeAction="actionCustomLabel" > <key-style latin:styleName="enterKeyStyle" - latin:keySpec="dummy_label|!code/key_enter" - latin:keyLabelFlags="fromCustomActionLabel" - latin:parentStyle="defaultEnterKeyStyle" /> + latin:parentStyle="customLabelActionKeyStyle" /> </case> <!-- imeAction is either actionNone or actionUnspecified. --> <default> <key-style latin:styleName="enterKeyStyle" - latin:keySpec="!icon/enter_key|!code/key_enter" latin:parentStyle="defaultEnterKeyStyle" /> </default> </switch> diff --git a/java/res/xml/key_styles_navigate_more_keys.xml b/java/res/xml/key_styles_navigate_more_keys.xml new file mode 100644 index 000000000..f97114db9 --- /dev/null +++ b/java/res/xml/key_styles_navigate_more_keys.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="navigateNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!text/keyspec_action_next" /> + <key-style + latin:styleName="navigatePreviousMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!text/keyspec_action_previous" /> + <key-style + latin:styleName="navigatePreviousNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_action_previous,!text/keyspec_action_next" /> + <key-style + latin:styleName="navigateEmojiMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!text/keyspec_emoji_action_key" /> + <key-style + latin:styleName="navigateEmojiNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_next" /> + <key-style + latin:styleName="navigateEmojiPreviousMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_previous" /> + <key-style + latin:styleName="navigateEmojiPreviousNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_previous,!text/keyspec_action_next" /> + </case> + <default> + <key-style + latin:styleName="navigateNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!icon/next_key|!code/key_action_next" /> + <key-style + latin:styleName="navigatePreviousMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!icon/previous_key|!code/key_action_previous" /> + <key-style + latin:styleName="navigatePreviousNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!icon/previous_key|!code/key_action_previous,!icon/next_key|!code/key_action_next" /> + <key-style + latin:styleName="navigateEmojiMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!text/keyspec_emoji_action_key" /> + <key-style + latin:styleName="navigateEmojiNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!icon/next_key|!code/key_action_next" /> + <key-style + latin:styleName="navigateEmojiPreviousMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!icon/previous_key|!code/key_action_previous" /> + <key-style + latin:styleName="navigateEmojiPreviousNextMoreKeysStyle" + latin:keyLabelFlags="hasPopupHint|preserveCase" + latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/keyspec_emoji_action_key,!icon/previous_key|!code/key_action_previous,!icon/next_key|!code/key_action_next" /> + </default> + </switch> +</merge> diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml index df4448c25..97ae6c6c3 100644 --- a/java/res/xml/key_styles_number.xml +++ b/java/res/xml/key_styles_number.xml @@ -39,7 +39,7 @@ latin:parentStyle="numKeyBaseStyle" /> <key-style latin:styleName="numberKeyStyle" - latin:keyLabelFlags="alignLeftOfCenter|hasHintLabel" + latin:keyLabelFlags="alignLabelOffCenter|hasHintLabel" latin:parentStyle="numKeyStyle" /> <key-style latin:styleName="num0KeyStyle" @@ -120,14 +120,27 @@ <key-style latin:styleName="numSpaceKeyStyle" latin:keySpec="!icon/space_key_for_number_layout|!code/key_space" + latin:keyLabelFlags="alignIconToBottom" latin:keyActionFlags="enableLongPress" latin:parentStyle="numKeyBaseStyle" /> - <!-- Override defaultEnterKeyStyle in key_styles_enter.xml --> - <key-style - latin:styleName="defaultEnterKeyStyle" - latin:keySpec="!icon/enter_key|!code/key_enter" - latin:keyLabelFlags="preserveCase|autoXScale|followKeyLargeLabelRatio" - latin:keyActionFlags="noKeyPreview" - latin:backgroundType="functional" - latin:parentStyle="navigateMoreKeysStyle" /> + <!-- TODO: Consolidate these space key styles with numSpaceKeyStyle above by introducing <case> + predicator that checks device form-factor. --> + <switch> + <case latin:keyboardTheme="ICS|KLP"> + <key-style + latin:styleName="tabletNumSpaceKeyStyle" + latin:keySpec="!icon/space_key|!code/key_space" + latin:backgroundType="functional" + latin:keyActionFlags="enableLongPress" + latin:parentStyle="numKeyBaseStyle" /> + </case> + <case latin:keyboardTheme="LXXLight|LXXDark"> + <key-style + latin:styleName="tabletNumSpaceKeyStyle" + latin:keySpec="!icon/space_key|!code/key_space" + latin:backgroundType="spacebar" + latin:keyActionFlags="enableLongPress" + latin:parentStyle="numKeyBaseStyle" /> + </case> + </switch> </merge> diff --git a/java/res/xml/key_styles_settings.xml b/java/res/xml/key_styles_settings.xml index 956b40235..a504bed78 100644 --- a/java/res/xml/key_styles_settings.xml +++ b/java/res/xml/key_styles_settings.xml @@ -29,15 +29,17 @@ > <key-style latin:styleName="settingsMoreKeysStyle" - latin:backgroundType="functional" /> + latin:backgroundType="functional" + latin:parentStyle="baseSettingsMoreKeysStyle" /> </case> <!-- clobberSettingsKey="false" --> <default> <key-style latin:styleName="settingsMoreKeysStyle" latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/keyspec_settings" - latin:backgroundType="functional" /> + latin:additionalMoreKeys="!text/keyspec_settings" + latin:backgroundType="functional" + latin:parentStyle="baseSettingsMoreKeysStyle" /> </default> </switch> </merge> diff --git a/java/res/xml/keyboard_layout_set_bengali.xml b/java/res/xml/keyboard_layout_set_bengali.xml index 6e40e6de4..de48a0ceb 100644 --- a/java/res/xml/keyboard_layout_set_bengali.xml +++ b/java/res/xml/keyboard_layout_set_bengali.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="bengali" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_bengali" diff --git a/java/res/xml/keyboard_layout_set_hindi.xml b/java/res/xml/keyboard_layout_set_hindi.xml index e850c7ebc..ee41b2ae7 100644 --- a/java/res/xml/keyboard_layout_set_hindi.xml +++ b/java/res/xml/keyboard_layout_set_hindi.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="devanagari" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_hindi" diff --git a/java/res/xml/keyboard_layout_set_hindi_compact.xml b/java/res/xml/keyboard_layout_set_hindi_compact.xml index 77d02fbbc..147b2993f 100644 --- a/java/res/xml/keyboard_layout_set_hindi_compact.xml +++ b/java/res/xml/keyboard_layout_set_hindi_compact.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="devanagari" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_hindi_compact" diff --git a/java/res/xml/keyboard_layout_set_kannada.xml b/java/res/xml/keyboard_layout_set_kannada.xml index 8dcf99657..14323e7ef 100644 --- a/java/res/xml/keyboard_layout_set_kannada.xml +++ b/java/res/xml/keyboard_layout_set_kannada.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="kannada" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_kannada" diff --git a/java/res/xml/keyboard_layout_set_khmer.xml b/java/res/xml/keyboard_layout_set_khmer.xml index 181f98b3d..752c8520a 100644 --- a/java/res/xml/keyboard_layout_set_khmer.xml +++ b/java/res/xml/keyboard_layout_set_khmer.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="khmer" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_khmer" diff --git a/java/res/xml/keyboard_layout_set_lao.xml b/java/res/xml/keyboard_layout_set_lao.xml index 2ffde45db..6285f87f9 100644 --- a/java/res/xml/keyboard_layout_set_lao.xml +++ b/java/res/xml/keyboard_layout_set_lao.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="lao" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_lao" diff --git a/java/res/xml/keyboard_layout_set_malayalam.xml b/java/res/xml/keyboard_layout_set_malayalam.xml index 14c76baf0..f6086f6cb 100644 --- a/java/res/xml/keyboard_layout_set_malayalam.xml +++ b/java/res/xml/keyboard_layout_set_malayalam.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="malayalam" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_malayalam" diff --git a/java/res/xml/keyboard_layout_set_marathi.xml b/java/res/xml/keyboard_layout_set_marathi.xml index e5c68e7ce..6aea17546 100644 --- a/java/res/xml/keyboard_layout_set_marathi.xml +++ b/java/res/xml/keyboard_layout_set_marathi.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="devanagari" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_marathi" diff --git a/java/res/xml/keyboard_layout_set_myanmar.xml b/java/res/xml/keyboard_layout_set_myanmar.xml index 5c823b263..5af8c064e 100644 --- a/java/res/xml/keyboard_layout_set_myanmar.xml +++ b/java/res/xml/keyboard_layout_set_myanmar.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="myanmar" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_myanmar" diff --git a/java/res/xml/keyboard_layout_set_nepali_romanized.xml b/java/res/xml/keyboard_layout_set_nepali_romanized.xml index fbbc6a5a0..5bad83537 100644 --- a/java/res/xml/keyboard_layout_set_nepali_romanized.xml +++ b/java/res/xml/keyboard_layout_set_nepali_romanized.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="devanagari" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_nepali_romanized" diff --git a/java/res/xml/keyboard_layout_set_nepali_traditional.xml b/java/res/xml/keyboard_layout_set_nepali_traditional.xml index 4a3b60153..6f11dee58 100644 --- a/java/res/xml/keyboard_layout_set_nepali_traditional.xml +++ b/java/res/xml/keyboard_layout_set_nepali_traditional.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="devanagari" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_nepali_traditional" diff --git a/java/res/xml/keyboard_layout_set_sinhala.xml b/java/res/xml/keyboard_layout_set_sinhala.xml index 8e6e619d2..1955d218f 100644 --- a/java/res/xml/keyboard_layout_set_sinhala.xml +++ b/java/res/xml/keyboard_layout_set_sinhala.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="sinhala" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_sinhala" diff --git a/java/res/xml/keyboard_layout_set_tamil.xml b/java/res/xml/keyboard_layout_set_tamil.xml index 5c0491505..27f4a3266 100644 --- a/java/res/xml/keyboard_layout_set_tamil.xml +++ b/java/res/xml/keyboard_layout_set_tamil.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="tamil" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_tamil" diff --git a/java/res/xml/keyboard_layout_set_telugu.xml b/java/res/xml/keyboard_layout_set_telugu.xml index aca47b9d9..2bf65bcbd 100644 --- a/java/res/xml/keyboard_layout_set_telugu.xml +++ b/java/res/xml/keyboard_layout_set_telugu.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="telugu" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_telugu" diff --git a/java/res/xml/keyboard_layout_set_thai.xml b/java/res/xml/keyboard_layout_set_thai.xml index b8f99971b..f69dfa6bd 100644 --- a/java/res/xml/keyboard_layout_set_thai.xml +++ b/java/res/xml/keyboard_layout_set_thai.xml @@ -20,6 +20,8 @@ <KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> + <Feature + latin:supportedScript="thai" /> <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_thai" diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index 5021f33ee..4f8efa8d6 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -28,7 +28,7 @@ be_BY: Belarusian (Belarus)/east_slavic bg: Bulgarian/bulgarian bg: Bulgarian/bulgarian_bds - (bn_IN: Bengali (India)/bengali) # This is a preliminary keyboard layout. + bn_IN: Bengali (India)/bengali ca: Catalan/spanish cs: Czech/qwertz da: Danish/nordic @@ -51,7 +51,7 @@ fr_CH: French (Switzerland)/swiss gl_ES: Galician (Spain)/spanish hi: Hindi/hindi - (hi: Hindi/hindi_compact) # This is a preliminary keyboard layout. + hi: Hindi/hindi_compact hr: Croatian/qwertz hu: Hungarian/qwertz hy_AM: Armenian (Armenia) Phonetic/armenian_phonetic @@ -63,15 +63,15 @@ ka_GE: Georgian (Georgia)/georgian kk: Kazakh/east_slavic km_KH: Khmer (Cambodia)/khmer - (kn_IN: Kannada (India)/kannada) # This is a preliminary keyboard layout. + kn_IN: Kannada (India)/kannada ky: Kyrgyz/east_slavic lo_LA: Lao (Laos)/lao lt: Lithuanian/qwerty lv: Latvian/qwerty mk: Macedonian/south_slavic - (ml_IN: Malayalam (India)/malayalam) # This is a preliminary keyboard layout. + ml_IN: Malayalam (India)/malayalam mn_MN: Mongolian (Mongolia)/mongolian - (mr_IN: Marathi (India)/marathi) # This is a preliminary keyboard layout. + mr_IN: Marathi (India)/marathi ms_MY: Malay (Malaysia)/qwerty (my_MM: Myanmar (Myanmar)/myanmar) # This is a preliminary keyboard layout. nb: Norwegian Bokmål/nordic @@ -91,8 +91,10 @@ (sr-Latn: Serbian/qwerty) # not yet implemented. sv: Swedish/nordic sw: Swahili/qwerty - (ta_IN: Tamil (India)/tamil) # This is a preliminary keyboard layout. - (te_IN: Telugu (India)/telugu) # This is a preliminary keyboard layout. + ta_IN: Tamil (India)/tamil + (ta_LK: Tamil (Sri Lanka)/tamil) # Disabled in conjunction with si_LK. + ta_SG: Tamil (Singapore)/tamil + te_IN: Telugu (India)/telugu th: Thai/thai tl: Tagalog/spanish tr: Turkish/qwerty @@ -176,8 +178,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This bengali keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xbff5986c" @@ -346,8 +346,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This hindi_compact keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic_compact" android:subtypeId="0xe49c89a1" @@ -446,8 +444,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This kannada keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8c78064f" @@ -496,8 +492,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This malayalam keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xc182ebd4" @@ -514,8 +508,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This marathi keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x747b9f03" @@ -534,6 +526,7 @@ /> <!-- TODO: This Myanmar keyboard is a preliminary layout. This isn't based on the final specification. --> + <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0xea266ea4" @@ -542,6 +535,7 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=myanmar,EmojiCapable,CombiningRules=MyanmarReordering" android:isAsciiCapable="false" /> + --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x3f12ee14" @@ -622,8 +616,9 @@ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This sinhala keyboard is a preliminary layout. + <!-- TODO: This Sinhala keyboard is a preliminary layout. This isn't based on the final specification. --> + <!-- <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x5c6b3bde" @@ -632,6 +627,7 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=sinhala,EmojiCapable" android:isAsciiCapable="false" /> + --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x8e94d413" @@ -690,8 +686,6 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable" android:isAsciiCapable="true" /> - <!-- TODO: This tamil keyboard is a preliminary layout. - This isn't based on the final specification. --> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x67acea2a" @@ -700,8 +694,25 @@ android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable" android:isAsciiCapable="false" /> - <!-- TODO: This telugu keyboard is a preliminary layout. - This isn't based on the final specification. --> + <!-- TODO: Enable ta_LK subtype when si_LK subtype is ready --> + <!-- + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0x6ca12d84" + android:imeSubtypeLocale="ta_LK" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable" + android:isAsciiCapable="false" + /> + --> + <subtype android:icon="@drawable/ic_ime_switcher_dark" + android:label="@string/subtype_generic" + android:subtypeId="0x785abbd9" + android:imeSubtypeLocale="ta_SG" + android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="KeyboardLayoutSet=tamil,EmojiCapable" + android:isAsciiCapable="false" + /> <subtype android:icon="@drawable/ic_ime_switcher_dark" android:label="@string/subtype_generic" android:subtypeId="0x1e177389" diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 6493d0cbd..c14cd645a 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -19,139 +19,27 @@ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" android:key="english_ime_settings"> <PreferenceScreen - android:fragment="com.android.inputmethod.latin.settings.InputSettingsFragment" - android:title="@string/settings_screen_input" - android:key="screen_input" /> - <ListPreference - android:key="pref_keyboard_theme" - android:title="@string/keyboard_theme" - android:entryValues="@array/keyboard_theme_ids" - android:entries="@array/keyboard_theme_names" - android:persistent="true" /> + android:fragment="com.android.inputmethod.latin.settings.PreferencesSettingsFragment" + android:title="@string/settings_screen_preferences" + android:key="screen_preferences" /> + <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.AppearanceSettingsFragment" + android:title="@string/settings_screen_appearance" + android:key="screen_appearance" /> <PreferenceScreen android:fragment="com.android.inputmethod.latin.settings.MultiLingualSettingsFragment" - android:title="@string/settings_screen_multi_lingual" - android:key="screen_multi_lingual" /> + android:title="@string/settings_screen_multilingual" + android:key="screen_multilingual" /> <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.GestureSettingsFragment" android:title="@string/settings_screen_gesture" - android:key="screen_gesture"> - <CheckBoxPreference - android:key="gesture_input" - android:title="@string/gesture_input" - android:summary="@string/gesture_input_summary" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="pref_gesture_floating_preview_text" - android:dependency="gesture_input" - android:title="@string/gesture_floating_preview_text" - android:summary="@string/gesture_floating_preview_text_summary" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="pref_gesture_preview_trail" - android:dependency="gesture_input" - android:title="@string/gesture_preview_trail" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="pref_gesture_space_aware" - android:dependency="gesture_input" - android:title="@string/gesture_space_aware" - android:summary="@string/gesture_space_aware_summary" - android:defaultValue="true" - android:persistent="true" /> - </PreferenceScreen> + android:key="screen_gesture" /> <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.CorrectionSettingsFragment" android:title="@string/settings_screen_correction" - android:key="screen_correction"> - <PreferenceScreen - android:key="edit_personal_dictionary" - android:title="@string/edit_personal_dictionary"> - <intent android:action="android.settings.USER_DICTIONARY_SETTINGS" /> - </PreferenceScreen> - <PreferenceScreen - android:key="configure_dictionaries_key" - android:title="@string/configure_dictionaries_title"> - <intent - android:action="android.intent.action.MAIN" - android:targetClass="@string/dictionary_pack_settings_activity"> - <extra - android:name="clientId" - android:value="@string/dictionary_pack_client_id" /> - </intent> - </PreferenceScreen> - <CheckBoxPreference - android:key="pref_key_block_potentially_offensive" - android:title="@string/prefs_block_potentially_offensive_title" - android:summary="@string/prefs_block_potentially_offensive_summary" - android:defaultValue="@bool/config_block_potentially_offensive" - android:persistent="true" /> - <ListPreference - android:key="auto_correction_threshold" - android:title="@string/auto_correction" - android:summary="@string/auto_correction_summary" - android:entryValues="@array/auto_correction_threshold_mode_indexes" - android:entries="@array/auto_correction_threshold_modes" - android:defaultValue="@string/auto_correction_threshold_mode_index_modest" - android:persistent="true" /> - <CheckBoxPreference - android:key="show_suggestions" - android:summary="@string/prefs_show_suggestions_summary" - android:title="@string/prefs_show_suggestions" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="pref_key_use_personalized_dicts" - android:title="@string/use_personalized_dicts" - android:summary="@string/use_personalized_dicts_summary" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="pref_key_use_contacts_dict" - android:title="@string/use_contacts_dict" - android:summary="@string/use_contacts_dict_summary" - android:defaultValue="true" - android:persistent="true" /> - <CheckBoxPreference - android:key="next_word_prediction" - android:title="@string/bigram_prediction" - android:summary="@string/bigram_prediction_summary" - android:defaultValue="true" - android:persistent="true" /> - </PreferenceScreen> + android:key="screen_correction" /> <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.AdvancedSettingsFragment" android:title="@string/settings_screen_advanced" - android:key="screen_advanced"> - <!-- TODO: consolidate key preview dismiss delay with the key preview animation parameters. --> - <ListPreference - android:key="pref_key_preview_popup_dismiss_delay" - android:title="@string/key_preview_popup_dismiss_delay" /> - <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_vibration_duration_settings" - android:title="@string/prefs_keypress_vibration_duration_settings" - latin:maxValue="@integer/config_max_vibration_duration" /> - <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_keypress_sound_volume" - android:title="@string/prefs_keypress_sound_volume_settings" - latin:maxValue="100" /> <!-- percent --> - <!-- The settigs for showing setup wizard application icon shouldn't be persistent and - the default value is added programmatically. --> - <CheckBoxPreference - android:key="pref_show_setup_wizard_icon" - android:title="@string/show_setup_wizard_icon" - android:summary="@string/show_setup_wizard_icon_summary" /> - <!-- title will be set programmatically to embed application name --> - <CheckBoxPreference - android:key="pref_enable_metrics_logging" - android:summary="@string/enable_metrics_logging_summary" - android:defaultValue="true" - android:persistent="true" /> - <PreferenceScreen - android:fragment="com.android.inputmethod.latin.settings.DebugSettings" - android:key="screen_debug" - android:title="Debug settings" - android:defaultValue="false" - android:persistent="true" /> - </PreferenceScreen> + android:key="screen_advanced" /> </PreferenceScreen> diff --git a/java/res/xml/prefs_screen_advanced.xml b/java/res/xml/prefs_screen_advanced.xml new file mode 100644 index 000000000..5aefcc8d5 --- /dev/null +++ b/java/res/xml/prefs_screen_advanced.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + android:title="@string/settings_screen_advanced" + android:key="screen_advanced"> + <!-- TODO: consolidate key preview dismiss delay with the key preview animation parameters. --> + <ListPreference + android:key="pref_key_preview_popup_dismiss_delay" + android:title="@string/key_preview_popup_dismiss_delay" /> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:key="pref_vibration_duration_settings" + android:title="@string/prefs_keypress_vibration_duration_settings" + latin:maxValue="@integer/config_max_vibration_duration" /> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:key="pref_keypress_sound_volume" + android:title="@string/prefs_keypress_sound_volume_settings" + latin:maxValue="100" /> <!-- percent --> + <!-- The settings for showing setup wizard application icon shouldn't be persistent and + the default value is added programmatically. --> + <CheckBoxPreference + android:key="pref_show_setup_wizard_icon" + android:title="@string/show_setup_wizard_icon" + android:summary="@string/show_setup_wizard_icon_summary" /> + <!-- title will be set programmatically to embed application name --> + <CheckBoxPreference + android:key="pref_enable_metrics_logging" + android:summary="@string/enable_metrics_logging_summary" + android:defaultValue="true" + android:persistent="true" /> + <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.DebugSettingsFragment" + android:key="screen_debug" + android:title="Debug settings" + android:defaultValue="false" + android:persistent="true" /> +</PreferenceScreen> diff --git a/java/res/xml/prefs_screen_appearance.xml b/java/res/xml/prefs_screen_appearance.xml new file mode 100644 index 000000000..7719c058b --- /dev/null +++ b/java/res/xml/prefs_screen_appearance.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + android:key="screen_appearance" + android:title="@string/settings_screen_appearance"> + <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.ThemeSettingsFragment" + android:key="screen_theme" + android:title="@string/settings_screen_theme" /> + <PreferenceScreen + android:fragment="com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment" + android:key="custom_input_styles" + android:title="@string/custom_input_styles_title" /> +</PreferenceScreen> diff --git a/java/res/xml/prefs_screen_correction.xml b/java/res/xml/prefs_screen_correction.xml new file mode 100644 index 000000000..dd5ba540c --- /dev/null +++ b/java/res/xml/prefs_screen_correction.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + android:title="@string/settings_screen_correction" + android:key="screen_correction"> + <PreferenceScreen + android:key="edit_personal_dictionary" + android:title="@string/edit_personal_dictionary"> + <intent android:action="android.settings.USER_DICTIONARY_SETTINGS" /> + </PreferenceScreen> + <PreferenceScreen + android:key="configure_dictionaries_key" + android:title="@string/configure_dictionaries_title"> + <intent + android:action="android.intent.action.MAIN" + android:targetClass="@string/dictionary_pack_settings_activity"> + <extra + android:name="clientId" + android:value="@string/dictionary_pack_client_id" /> + </intent> + </PreferenceScreen> + <CheckBoxPreference + android:key="pref_key_block_potentially_offensive" + android:title="@string/prefs_block_potentially_offensive_title" + android:summary="@string/prefs_block_potentially_offensive_summary" + android:defaultValue="@bool/config_block_potentially_offensive" + android:persistent="true" /> + <ListPreference + android:key="auto_correction_threshold" + android:title="@string/auto_correction" + android:summary="@string/auto_correction_summary" + android:entryValues="@array/auto_correction_threshold_mode_indexes" + android:entries="@array/auto_correction_threshold_modes" + android:defaultValue="@string/auto_correction_threshold_mode_index_modest" + android:persistent="true" /> + <CheckBoxPreference + android:key="show_suggestions" + android:summary="@string/prefs_show_suggestions_summary" + android:title="@string/prefs_show_suggestions" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_key_use_personalized_dicts" + android:title="@string/use_personalized_dicts" + android:summary="@string/use_personalized_dicts_summary" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_key_use_contacts_dict" + android:title="@string/use_contacts_dict" + android:summary="@string/use_contacts_dict_summary" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="next_word_prediction" + android:title="@string/bigram_prediction" + android:summary="@string/bigram_prediction_summary" + android:defaultValue="true" + android:persistent="true" /> +</PreferenceScreen> diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml index ae29a8a82..c47740268 100644 --- a/java/res/xml/prefs_screen_debug.xml +++ b/java/res/xml/prefs_screen_debug.xml @@ -31,6 +31,16 @@ android:defaultValue="false" android:persistent="true" /> <CheckBoxPreference + android:key="force_physical_keyboard_special_key" + android:title="@string/prefs_force_physical_keyboard_special_key" + android:defaultValue="false" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_show_ui_to_accept_typed_word" + android:title="@string/prefs_show_ui_to_accept_typed_word" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference android:key="pref_sliding_key_input_preview" android:title="@string/sliding_key_input_preview" android:summary="@string/sliding_key_input_preview_summary" @@ -42,19 +52,38 @@ latin:minValue="@integer/config_min_longpress_timeout" latin:maxValue="@integer/config_max_longpress_timeout" latin:stepValue="@integer/config_longpress_timeout_step" /> + <CheckBoxPreference + android:key="pref_has_custom_key_preview_animation_params" + android:title="@string/prefs_customize_key_preview_animation" + android:defaultValue="false" + android:persistent="true" /> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:dependency="pref_has_custom_key_preview_animation_params" + android:key="pref_key_preview_show_up_start_x_scale" + android:title="@string/prefs_key_popup_show_up_start_x_scale_settings" + latin:maxValue="100" /> <!-- percent --> + <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:dependency="pref_has_custom_key_preview_animation_params" + android:key="pref_key_preview_show_up_start_y_scale" + android:title="@string/prefs_key_popup_show_up_start_y_scale_settings" + latin:maxValue="100" /> <!-- percent --> <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_key_preview_show_up_start_scale" - android:title="@string/prefs_key_popup_show_up_start_scale_settings" + android:dependency="pref_has_custom_key_preview_animation_params" + android:key="pref_key_preview_dismiss_end_x_scale" + android:title="@string/prefs_key_popup_dismiss_end_x_scale_settings" latin:maxValue="100" /> <!-- percent --> <com.android.inputmethod.latin.settings.SeekBarDialogPreference - android:key="pref_key_preview_dismiss_end_scale" - android:title="@string/prefs_key_popup_dismiss_end_scale_settings" + android:dependency="pref_has_custom_key_preview_animation_params" + android:key="pref_key_preview_dismiss_end_y_scale" + android:title="@string/prefs_key_popup_dismiss_end_y_scale_settings" latin:maxValue="100" /> <!-- percent --> <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:dependency="pref_has_custom_key_preview_animation_params" android:key="pref_key_preview_show_up_duration" android:title="@string/prefs_key_popup_show_up_duration_settings" latin:maxValue="100" /> <!-- milliseconds --> <com.android.inputmethod.latin.settings.SeekBarDialogPreference + android:dependency="pref_has_custom_key_preview_animation_params" android:key="pref_key_preview_dismiss_duration" android:title="@string/prefs_key_popup_dismiss_duration_settings" latin:maxValue="100" /> <!-- milliseconds --> diff --git a/java/res/xml/prefs_screen_gesture.xml b/java/res/xml/prefs_screen_gesture.xml new file mode 100644 index 000000000..c87316dfa --- /dev/null +++ b/java/res/xml/prefs_screen_gesture.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + android:title="@string/settings_screen_gesture" + android:key="screen_gesture"> + <CheckBoxPreference + android:key="gesture_input" + android:title="@string/gesture_input" + android:summary="@string/gesture_input_summary" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_gesture_floating_preview_text" + android:dependency="gesture_input" + android:title="@string/gesture_floating_preview_text" + android:summary="@string/gesture_floating_preview_text_summary" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_gesture_preview_trail" + android:dependency="gesture_input" + android:title="@string/gesture_preview_trail" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_gesture_space_aware" + android:dependency="gesture_input" + android:title="@string/gesture_space_aware" + android:summary="@string/gesture_space_aware_summary" + android:defaultValue="true" + android:persistent="true" /> +</PreferenceScreen> diff --git a/java/res/xml/prefs_screen_multi_lingual.xml b/java/res/xml/prefs_screen_multilingual.xml index 937d439d6..07a4b701c 100644 --- a/java/res/xml/prefs_screen_multi_lingual.xml +++ b/java/res/xml/prefs_screen_multilingual.xml @@ -16,8 +16,8 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" - android:title="@string/settings_screen_multi_lingual" - android:key="screen_multi_lingual"> + android:title="@string/settings_screen_multilingual" + android:key="screen_multilingual"> <CheckBoxPreference android:key="pref_show_language_switch_key" android:title="@string/show_language_switch_key" @@ -31,8 +31,4 @@ android:summary="@string/include_other_imes_in_language_switch_list_summary" android:defaultValue="false" android:persistent="true" /> - <PreferenceScreen - android:fragment="com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment" - android:key="custom_input_styles" - android:title="@string/custom_input_styles_title" /> </PreferenceScreen> diff --git a/java/res/xml/prefs_screen_input.xml b/java/res/xml/prefs_screen_preferences.xml index 7704e3f80..101edc855 100644 --- a/java/res/xml/prefs_screen_input.xml +++ b/java/res/xml/prefs_screen_preferences.xml @@ -17,7 +17,7 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" - android:title="@string/settings_screen_input"> + android:title="@string/settings_screen_preferences"> <CheckBoxPreference android:key="auto_cap" android:title="@string/auto_cap" diff --git a/java/res/xml/prefs_screen_theme.xml b/java/res/xml/prefs_screen_theme.xml new file mode 100644 index 000000000..677a6ea3b --- /dev/null +++ b/java/res/xml/prefs_screen_theme.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" + android:title="@string/settings_screen_theme" + android:key="screen_theme"> + <!-- Keyboard theme list will be populated programmatically here. --> +</PreferenceScreen> diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml deleted file mode 100644 index e7a3ee736..000000000 --- a/java/res/xml/row_dvorak4.xml +++ /dev/null @@ -1,45 +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" -> - <Row - latin:keyWidth="10%p" - > - <Key - latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="15%p" /> - <Key - latin:keySpec="q" - latin:backgroundType="normal" - latin:keyStyle="settingsMoreKeysStyle" /> - <include - latin:keyXPos="25%p" - latin:keyboardLayout="@xml/key_space_5kw" /> - <Key - latin:keySpec="z" - latin:keyLabelFlags="hasPopupHint" - latin:moreKeys="!text/morekeys_punctuation,!text/morekeys_z" /> - <Key - latin:keyStyle="enterKeyStyle" - latin:keyWidth="fillRight" /> - </Row> -</merge> diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml index 509092d96..5bc104f69 100644 --- a/java/res/xml/row_qwerty4.xml +++ b/java/res/xml/row_qwerty4.xml @@ -28,7 +28,7 @@ latin:keyStyle="toSymbolKeyStyle" latin:keyWidth="15%p" /> <include - latin:keyboardLayout="@xml/key_f1" /> + latin:keyboardLayout="@xml/key_comma" /> <include latin:keyXPos="25%p" latin:keyboardLayout="@xml/key_space_5kw" /> diff --git a/java/res/xml/rowkeys_kannada3.xml b/java/res/xml/rowkeys_kannada3.xml index 0f1aecbac..03fa05551 100644 --- a/java/res/xml/rowkeys_kannada3.xml +++ b/java/res/xml/rowkeys_kannada3.xml @@ -29,13 +29,12 @@ <Key latin:keySpec="ಂ" latin:moreKeys="ಎ" /> - <!-- U+0CAE: "ಮ" KANNADA LETTER MA + <!-- U+0CAE: "ಮ" KANNADA LETTER MA --> + <Key latin:keySpec="ಮ" /> + <!-- U+0CA8: "ನ" KANNADA LETTER NA U+0CA3: "ಣ" KANNADA LETTER NNA --> - <Key - latin:keySpec="ಮ" + <Key latin:keySpec="ನ" latin:moreKeys="ಣ" /> - <!-- U+0CA8: "ನ" KANNADA LETTER NA --> - <Key latin:keySpec="ನ" /> <!-- U+0CB5: "ವ" KANNADA LETTER VA --> <Key latin:keySpec="ವ" /> <!-- U+0CB2: "ಲ" KANNADA LETTER LA diff --git a/java/res/xml/rowkeys_tamil1.xml b/java/res/xml/rowkeys_tamil1.xml index 4debd9e3e..d6a5cfaf9 100644 --- a/java/res/xml/rowkeys_tamil1.xml +++ b/java/res/xml/rowkeys_tamil1.xml @@ -19,29 +19,39 @@ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> - <!-- U+0B94: "ஔ" TAMIL LETTER AU --> + <!-- U+0B94: "ஔ" TAMIL LETTER AU + U+0BCC: "ௌ" TAMIL VOWEL SIGN AU --> <Key latin:keySpec="ஔ" + latin:moreKeys="ௌ,%" latin:keyHintLabel="1" latin:additionalMoreKeys="1" /> - <!-- U+0B90: "ஐ" TAMIL LETTER AI --> + <!-- U+0B90: "ஐ" TAMIL LETTER AI + U+0BC8: "ை" TAMIL VOWEL SIGN AI --> <Key latin:keySpec="ஐ" + latin:moreKeys="ை,%" latin:keyHintLabel="2" latin:additionalMoreKeys="2" /> - <!-- U+0B86: "ஆ" TAMIL LETTER AA --> + <!-- U+0B86: "ஆ" TAMIL LETTER AA + U+0BBE: "ா" TAMIL VOWEL SIGN AA --> <Key latin:keySpec="ஆ" + latin:moreKeys="ா,%" latin:keyHintLabel="3" latin:additionalMoreKeys="3" /> - <!-- U+0B88: "ஈ" TAMIL LETTER II --> + <!-- U+0B88: "ஈ" TAMIL LETTER II + U+0BC0: "ீ" TAMIL VOWEL SIGN II --> <Key latin:keySpec="ஈ" + latin:moreKeys="ீ,%" latin:keyHintLabel="4" latin:additionalMoreKeys="4" /> - <!-- U+0B8A: "ஊ" TAMIL LETTER UU --> + <!-- U+0B8A: "ஊ" TAMIL LETTER UU + U+0BC2: "ூ" TAMIL VOWEL SIGN UU --> <Key latin:keySpec="ஊ" + latin:moreKeys="ூ,%" latin:keyHintLabel="5" latin:additionalMoreKeys="5" /> <!-- U+0BAE: "ம" TAMIL LETTER MA --> diff --git a/java/res/xml/rowkeys_tamil2.xml b/java/res/xml/rowkeys_tamil2.xml index 894825cb4..ec0b2a83c 100644 --- a/java/res/xml/rowkeys_tamil2.xml +++ b/java/res/xml/rowkeys_tamil2.xml @@ -20,21 +20,31 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> <!-- U+0B93: "ஓ" TAMIL LETTER OO + U+0BCB: "ோ" TAMIL VOWEL SIGN OO U+0BD0: "ௐ" TAMIL OM --> <Key latin:keySpec="ஓ" - latin:moreKeys="ௐ" /> - <!-- U+0B8F: "ஏ" TAMIL LETTER EE --> - <Key latin:keySpec="ஏ" /> + latin:moreKeys="ோ,ௐ" /> + <!-- U+0B8F: "ஏ" TAMIL LETTER EE + U+0BC7: "ே" TAMIL VOWEL SIGN EE --> + <Key + latin:keySpec="ஏ" + latin:moreKeys="ே" /> <!-- U+0B85: "அ" TAMIL LETTER A U+0B83: "ஃ" TAMIL SIGN VISARGA --> <Key latin:keySpec="அ" latin:moreKeys="ஃ" /> - <!-- U+0B87: "இ" TAMIL LETTER I --> - <Key latin:keySpec="இ" /> - <!-- U+0B89: "உ" TAMIL LETTER U --> - <Key latin:keySpec="உ" /> + <!-- U+0B87: "இ" TAMIL LETTER I + U+0BBF: "ி" TAMIL VOWEL SIGN I --> + <Key + latin:keySpec="இ" + latin:moreKeys="ி" /> + <!-- U+0B89: "உ" TAMIL LETTER U + U+0BC1: "ு" TAMIL VOWEL SIGN U --> + <Key + latin:keySpec="உ" + latin:moreKeys="ு" /> <!-- U+0BB1: "ற" TAMIL LETTER RRA --> <Key latin:keySpec="ற" /> <!-- U+0BAA: "ப" TAMIL LETTER PA --> diff --git a/java/res/xml/rowkeys_tamil3.xml b/java/res/xml/rowkeys_tamil3.xml index 5386e61cf..e517e142b 100644 --- a/java/res/xml/rowkeys_tamil3.xml +++ b/java/res/xml/rowkeys_tamil3.xml @@ -19,10 +19,16 @@ --> <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"> - <!-- U+0B92: "ஒ" TAMIL LETTER O --> - <Key latin:keySpec="ஒ" /> - <!-- U+0B8E: "எ" TAMIL LETTER E --> - <Key latin:keySpec="எ" /> + <!-- U+0B92: "ஒ" TAMIL LETTER O + U+0BCA: "ொ" TAMIL VOWEL SIGN O --> + <Key + latin:keySpec="ஒ" + latin:moreKeys="ொ" /> + <!-- U+0B8E: "எ" TAMIL LETTER E + U+0BC6: "ெ" TAMIL VOWEL SIGN E --> + <Key + latin:keySpec="எ" + latin:moreKeys="ெ" /> <!-- U+0BCD: "்" TAMIL SIGN VIRAMA --> <Key latin:keySpec="்" /> <!-- U+0BB0: "ர" TAMIL LETTER RA --> diff --git a/java/res/xml/rows_dvorak.xml b/java/res/xml/rows_dvorak.xml index 13d70210d..f656613ec 100644 --- a/java/res/xml/rows_dvorak.xml +++ b/java/res/xml/rows_dvorak.xml @@ -49,6 +49,8 @@ latin:keyWidth="fillRight" latin:visualInsetsLeft="1%p" /> </Row> + <!-- Dvorak layout shares almost the same row with Qwerty layout. + The difference is defined in xml/row_qwerty4.xml. --> <include - latin:keyboardLayout="@xml/row_dvorak4" /> + latin:keyboardLayout="@xml/row_qwerty4" /> </merge> diff --git a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java new file mode 100644 index 000000000..7d1717bd1 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.os.Build; + +public final class BuildCompatUtils { + private BuildCompatUtils() { + // This utility class is not publicly instantiable. + } + + private static final boolean IS_RELEASE_BUILD = Build.VERSION.CODENAME.equals("REL"); + + /** + * The "effective" API version. + * {@link android.os.Build.VERSION#SDK_INT} if the platform is a release build. + * {@link android.os.Build.VERSION#SDK_INT} plus 1 if the platform is a development build. + */ + public static final int EFFECTIVE_SDK_INT = IS_RELEASE_BUILD + ? Build.VERSION.SDK_INT + : Build.VERSION.SDK_INT + 1; + + /** + * API version for L-release. + */ + // TODO: Substitute this constant reference with Build.VERSION_CODES.L* once the *next* version + // becomes available. + public static final int VERSION_CODES_LXX = 21; +} diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java index 660029baf..6aa2736c1 100644 --- a/java/src/com/android/inputmethod/compat/CompatUtils.java +++ b/java/src/com/android/inputmethod/compat/CompatUtils.java @@ -21,6 +21,7 @@ import android.util.Log; import java.lang.reflect.Constructor; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public final class CompatUtils { @@ -33,31 +34,31 @@ public final class CompatUtils { public static Class<?> getClass(final String className) { try { return Class.forName(className); - } catch (ClassNotFoundException e) { + } catch (final ClassNotFoundException e) { return null; } } public static Method getMethod(final Class<?> targetClass, final String name, final Class<?>... parameterTypes) { - if (targetClass == null || TextUtils.isEmpty(name)) return null; + if (targetClass == null || TextUtils.isEmpty(name)) { + return null; + } try { return targetClass.getMethod(name, parameterTypes); - } catch (SecurityException e) { - // ignore - } catch (NoSuchMethodException e) { + } catch (final SecurityException | NoSuchMethodException e) { // ignore } return null; } public static Field getField(final Class<?> targetClass, final String name) { - if (targetClass == null || TextUtils.isEmpty(name)) return null; + if (targetClass == null || TextUtils.isEmpty(name)) { + return null; + } try { return targetClass.getField(name); - } catch (SecurityException e) { - // ignore - } catch (NoSuchFieldException e) { + } catch (final SecurityException | NoSuchFieldException e) { // ignore } return null; @@ -65,22 +66,25 @@ public final class CompatUtils { public static Constructor<?> getConstructor(final Class<?> targetClass, final Class<?> ... types) { - if (targetClass == null || types == null) return null; + if (targetClass == null || types == null) { + return null; + } try { return targetClass.getConstructor(types); - } catch (SecurityException e) { - // ignore - } catch (NoSuchMethodException e) { + } catch (final SecurityException | NoSuchMethodException e) { // ignore } return null; } public static Object newInstance(final Constructor<?> constructor, final Object ... args) { - if (constructor == null) return null; + if (constructor == null) { + return null; + } try { return constructor.newInstance(args); - } catch (Exception e) { + } catch (final InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { Log.e(TAG, "Exception in newInstance", e); } return null; @@ -88,10 +92,13 @@ public final class CompatUtils { public static Object invoke(final Object receiver, final Object defaultValue, final Method method, final Object... args) { - if (method == null) return defaultValue; + if (method == null) { + return defaultValue; + } try { return method.invoke(receiver, args); - } catch (Exception e) { + } catch (final IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { Log.e(TAG, "Exception in invoke", e); } return defaultValue; @@ -99,21 +106,113 @@ public final class CompatUtils { public static Object getFieldValue(final Object receiver, final Object defaultValue, final Field field) { - if (field == null) return defaultValue; + if (field == null) { + return defaultValue; + } try { return field.get(receiver); - } catch (Exception e) { + } catch (final IllegalAccessException | IllegalArgumentException e) { Log.e(TAG, "Exception in getFieldValue", e); } return defaultValue; } public static void setFieldValue(final Object receiver, final Field field, final Object value) { - if (field == null) return; + if (field == null) { + return; + } try { field.set(receiver, value); - } catch (Exception e) { + } catch (final IllegalAccessException | IllegalArgumentException e) { Log.e(TAG, "Exception in setFieldValue", e); } } + + public static ClassWrapper getClassWrapper(final String className) { + return new ClassWrapper(getClass(className)); + } + + public static final class ClassWrapper { + private final Class<?> mClass; + public ClassWrapper(final Class<?> targetClass) { + mClass = targetClass; + } + + public boolean exists() { + return mClass != null; + } + + public <T> ToObjectMethodWrapper<T> getMethod(final String name, + final T defaultValue, final Class<?>... parameterTypes) { + return new ToObjectMethodWrapper<T>(CompatUtils.getMethod(mClass, name, parameterTypes), + defaultValue); + } + + public ToIntMethodWrapper getPrimitiveMethod(final String name, final int defaultValue, + final Class<?>... parameterTypes) { + return new ToIntMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes), + defaultValue); + } + + public ToFloatMethodWrapper getPrimitiveMethod(final String name, final float defaultValue, + final Class<?>... parameterTypes) { + return new ToFloatMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes), + defaultValue); + } + + public ToBooleanMethodWrapper getPrimitiveMethod(final String name, + final boolean defaultValue, final Class<?>... parameterTypes) { + return new ToBooleanMethodWrapper(CompatUtils.getMethod(mClass, name, parameterTypes), + defaultValue); + } + } + + public static final class ToObjectMethodWrapper<T> { + private final Method mMethod; + private final T mDefaultValue; + public ToObjectMethodWrapper(final Method method, final T defaultValue) { + mMethod = method; + mDefaultValue = defaultValue; + } + @SuppressWarnings("unchecked") + public T invoke(final Object receiver, final Object... args) { + return (T) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args); + } + } + + public static final class ToIntMethodWrapper { + private final Method mMethod; + private final int mDefaultValue; + public ToIntMethodWrapper(final Method method, final int defaultValue) { + mMethod = method; + mDefaultValue = defaultValue; + } + public int invoke(final Object receiver, final Object... args) { + return (int) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args); + } + } + + public static final class ToFloatMethodWrapper { + private final Method mMethod; + private final float mDefaultValue; + public ToFloatMethodWrapper(final Method method, final float defaultValue) { + mMethod = method; + mDefaultValue = defaultValue; + } + public float invoke(final Object receiver, final Object... args) { + return (float) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args); + } + } + + public static final class ToBooleanMethodWrapper { + private final Method mMethod; + private final boolean mDefaultValue; + public ToBooleanMethodWrapper(final Method method, final boolean defaultValue) { + mMethod = method; + mDefaultValue = defaultValue; + } + public boolean invoke(final Object receiver, final Object... args) { + return (boolean) CompatUtils.invoke(receiver, mDefaultValue, mMethod, args); + } + } } diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java new file mode 100644 index 000000000..5af31795c --- /dev/null +++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java @@ -0,0 +1,161 @@ +/* + * 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.compat; + +import android.graphics.Matrix; +import android.graphics.RectF; + +import com.android.inputmethod.annotations.UsedForTesting; + +@UsedForTesting +public final class CursorAnchorInfoCompatWrapper { + + /** + * The insertion marker or character bounds have at least one visible region. + */ + public static final int FLAG_HAS_VISIBLE_REGION = 0x01; + + /** + * The insertion marker or character bounds have at least one invisible (clipped) region. + */ + public static final int FLAG_HAS_INVISIBLE_REGION = 0x02; + + /** + * The insertion marker or character bounds is placed at right-to-left (RTL) character. + */ + public static final int FLAG_IS_RTL = 0x04; + + // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX). + private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass; + private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod; + private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod; + private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod; + private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod; + private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod; + private static final CompatUtils.ToIntMethodWrapper sGetComposingTextStartMethod; + private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBaselineMethod; + private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBottomMethod; + private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerHorizontalMethod; + private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerTopMethod; + private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod; + private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod; + + private static int INVALID_TEXT_INDEX = -1; + static { + sCursorAnchorInfoClass = CompatUtils.getClassWrapper( + "android.view.inputmethod.CursorAnchorInfo"); + sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getSelectionStart", INVALID_TEXT_INDEX); + sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getSelectionEnd", INVALID_TEXT_INDEX); + sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod( + "getCharacterBounds", (RectF)null, int.class); + sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getCharacterBoundsFlags", 0, int.class); + sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod( + "getComposingText", (CharSequence)null); + sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getComposingTextStart", INVALID_TEXT_INDEX); + sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getInsertionMarkerBaseline", 0.0f); + sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getInsertionMarkerBottom", 0.0f); + sGetInsertionMarkerHorizontalMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getInsertionMarkerHorizontal", 0.0f); + sGetInsertionMarkerTopMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getInsertionMarkerTop", 0.0f); + sGetMatrixMethod = sCursorAnchorInfoClass.getMethod("getMatrix", (Matrix)null); + sGetInsertionMarkerFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( + "getInsertionMarkerFlags", 0); + } + + @UsedForTesting + public boolean isAvailable() { + return sCursorAnchorInfoClass.exists() && mInstance != null; + } + + private Object mInstance; + + private CursorAnchorInfoCompatWrapper(final Object instance) { + mInstance = instance; + } + + @UsedForTesting + public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) { + if (!sCursorAnchorInfoClass.exists()) { + return new CursorAnchorInfoCompatWrapper(null); + } + return new CursorAnchorInfoCompatWrapper(instance); + } + + private static final class FakeHolder { + static CursorAnchorInfoCompatWrapper sInstance = new CursorAnchorInfoCompatWrapper(null); + } + + @UsedForTesting + public static CursorAnchorInfoCompatWrapper getFake() { + return FakeHolder.sInstance; + } + + public int getSelectionStart() { + return sGetSelectionStartMethod.invoke(mInstance); + } + + public int getSelectionEnd() { + return sGetSelectionEndMethod.invoke(mInstance); + } + + public CharSequence getComposingText() { + return sGetComposingTextMethod.invoke(mInstance); + } + + public int getComposingTextStart() { + return sGetComposingTextStartMethod.invoke(mInstance); + } + + public Matrix getMatrix() { + return sGetMatrixMethod.invoke(mInstance); + } + + public RectF getCharacterBounds(final int index) { + return sGetCharacterBoundsMethod.invoke(mInstance, index); + } + + public int getCharacterBoundsFlags(final int index) { + return sGetCharacterBoundsFlagsMethod.invoke(mInstance, index); + } + + public float getInsertionMarkerBaseline() { + return sGetInsertionMarkerBaselineMethod.invoke(mInstance); + } + + public float getInsertionMarkerBottom() { + return sGetInsertionMarkerBottomMethod.invoke(mInstance); + } + + public float getInsertionMarkerHorizontal() { + return sGetInsertionMarkerHorizontalMethod.invoke(mInstance); + } + + public float getInsertionMarkerTop() { + return sGetInsertionMarkerTopMethod.invoke(mInstance); + } + + public int getInsertionMarkerFlags() { + return sGetInsertionMarkerFlagsMethod.invoke(mInstance); + } +} diff --git a/java/src/com/android/inputmethod/compat/DownloadManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/DownloadManagerCompatUtils.java index d0b9c5da6..6209b60b3 100644 --- a/java/src/com/android/inputmethod/compat/DownloadManagerCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/DownloadManagerCompatUtils.java @@ -24,7 +24,7 @@ public final class DownloadManagerCompatUtils { // DownloadManager.Request#setAllowedOverMetered() has been introduced // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). private static final Method METHOD_setAllowedOverMetered = CompatUtils.getMethod( - DownloadManager.Request.class, "setAllowedOverMetered", Boolean.TYPE); + DownloadManager.Request.class, "setAllowedOverMetered", boolean.class); public static DownloadManager.Request setAllowedOverMetered( final DownloadManager.Request request, final boolean allowOverMetered) { diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java index be7bf402d..a5c71b22f 100644 --- a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java @@ -16,67 +16,34 @@ package com.android.inputmethod.compat; -import android.util.Log; import android.view.inputmethod.InputConnection; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; +import android.view.inputmethod.InputMethodManager; public final class InputConnectionCompatUtils { - private static final String TAG = InputConnectionCompatUtils.class.getSimpleName(); - - // Note that CursorAnchorInfoRequest is supposed to be available in API level 21 and later. - private static Class<?> getCursorAnchorInfoRequestClass() { - try { - return Class.forName("android.view.inputmethod.CursorAnchorInfoRequest"); - } catch (ClassNotFoundException e) { - return null; - } - } - - private static final Class<?> TYPE_CursorAnchorInfoRequest; - private static final Constructor<?> CONSTRUCTOR_CursorAnchorInfoRequest; - private static final Method METHOD_requestCursorAnchorInfo; + private static final CompatUtils.ClassWrapper sInputConnectionType; + private static final CompatUtils.ToBooleanMethodWrapper sRequestCursorUpdatesMethod; static { - TYPE_CursorAnchorInfoRequest = getCursorAnchorInfoRequestClass(); - CONSTRUCTOR_CursorAnchorInfoRequest = CompatUtils.getConstructor( - TYPE_CursorAnchorInfoRequest, int.class, int.class); - METHOD_requestCursorAnchorInfo = CompatUtils.getMethod(InputConnection.class, - "requestCursorAnchorInfo", TYPE_CursorAnchorInfoRequest); + sInputConnectionType = new CompatUtils.ClassWrapper(InputConnection.class); + sRequestCursorUpdatesMethod = sInputConnectionType.getPrimitiveMethod( + "requestCursorUpdates", false, int.class); } - public static boolean isRequestCursorAnchorInfoAvailable() { - return METHOD_requestCursorAnchorInfo != null && - CONSTRUCTOR_CursorAnchorInfoRequest != null; + public static boolean isRequestCursorUpdatesAvailable() { + return sRequestCursorUpdatesMethod != null; } /** - * Local copies of some constants in CursorAnchorInfoRequest until the SDK becomes publicly - * available. + * Local copies of some constants in InputConnection until the SDK becomes publicly available. */ - private final static int RESULT_NOT_HANDLED = 0; - private final static int RESULT_SCHEDULED = 1; - private final static int TYPE_CURSOR_ANCHOR_INFO = 1; - private final static int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 1; - private final static int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 2; - private final static int TYPE_CURSOR_RECT = 2; - private final static int FLAG_CURSOR_RECT_MONITOR = 1; - private final static int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 2; - private final static int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 4; + private static int CURSOR_UPDATE_IMMEDIATE = 1 << 0; + private static int CURSOR_UPDATE_MONITOR = 1 << 1; - private static int requestCursorAnchorInfoImpl(final InputConnection inputConnection, - final int type, final int flags) { - if (!isRequestCursorAnchorInfoAvailable()) { - return RESULT_NOT_HANDLED; - } - final Object requestObject = CompatUtils.newInstance( - CONSTRUCTOR_CursorAnchorInfoRequest, type, flags); - if (requestObject == null) { - return RESULT_NOT_HANDLED; + private static boolean requestCursorUpdatesImpl(final InputConnection inputConnection, + final int cursorUpdateMode) { + if (!isRequestCursorUpdatesAvailable()) { + return false; } - return (Integer) CompatUtils.invoke(inputConnection, - RESULT_NOT_HANDLED /* defaultValue */, - METHOD_requestCursorAnchorInfo, requestObject); + return sRequestCursorUpdatesMethod.invoke(inputConnection, cursorUpdateMode); } /** @@ -88,47 +55,10 @@ public final class InputConnectionCompatUtils { * as soon as possible to notify the current cursor/anchor position to the input method. * @return {@code false} if the request is not handled. Otherwise returns {@code true}. */ - public static boolean requestCursorAnchorInfo(final InputConnection inputConnection, + public static boolean requestCursorUpdates(final InputConnection inputConnection, final boolean enableMonitor, final boolean requestImmediateCallback) { - final int requestFlags = (enableMonitor ? FLAG_CURSOR_ANCHOR_INFO_MONITOR : 0) - | (requestImmediateCallback ? FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE : 0); - final int requestResult = requestCursorAnchorInfoImpl(inputConnection, - TYPE_CURSOR_ANCHOR_INFO, requestFlags); - switch (requestResult) { - case RESULT_NOT_HANDLED: - return false; - case RESULT_SCHEDULED: - return true; - default: - Log.w(TAG, "requestCursorAnchorInfo returned unknown result=" + requestResult - + " for type=TYPE_CURSOR_ANCHOR_INFO flags=" + requestFlags); - return true; - } - } - - /** - * Requests the editor to call back {@link InputMethodManager#updateCursor}. - * @param inputConnection the input connection to which the request is to be sent. - * @param enableMonitor {@code true} to request the editor to call back the method whenever the - * cursor position is changed. - * @return {@code false} if the request is not handled. Otherwise returns {@code true}. - */ - public static boolean requestCursorRect(final InputConnection inputConnection, - final boolean enableMonitor) { - final int requestFlags = enableMonitor ? - FLAG_CURSOR_RECT_MONITOR | FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES | - FLAG_CURSOR_RECT_WITH_VIEW_MATRIX : 0; - final int requestResult = requestCursorAnchorInfoImpl(inputConnection, TYPE_CURSOR_RECT, - requestFlags); - switch (requestResult) { - case RESULT_NOT_HANDLED: - return false; - case RESULT_SCHEDULED: - return true; - default: - Log.w(TAG, "requestCursorAnchorInfo returned unknown result=" + requestResult - + " for type=TYPE_CURSOR_RECT flags=" + requestFlags); - return true; - } + final int cursorUpdateMode = (enableMonitor ? CURSOR_UPDATE_MONITOR : 0) + | (requestImmediateCallback ? CURSOR_UPDATE_IMMEDIATE : 0); + return requestCursorUpdatesImpl(inputConnection, cursorUpdateMode); } } diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java index 18b3a6060..aa20c0336 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java @@ -26,7 +26,7 @@ public final class InputMethodManagerCompatWrapper { // Note that InputMethodManager.switchToNextInputMethod() has been introduced // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod( - InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE); + InputMethodManager.class, "switchToNextInputMethod", IBinder.class, boolean.class); // Note that InputMethodManager.shouldOfferSwitchingToNextInputMethod() has been introduced // in API level 19 (Build.VERSION_CODES.KITKAT). diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java index ee9125a07..365867257 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java @@ -32,8 +32,8 @@ public final class InputMethodSubtypeCompatUtils { // has been introduced in API level 17 (Build.VERSION_CODE.JELLY_BEAN_MR1). private static final Constructor<?> CONSTRUCTOR_INPUT_METHOD_SUBTYPE = CompatUtils.getConstructor(InputMethodSubtype.class, - Integer.TYPE, Integer.TYPE, String.class, String.class, String.class, - Boolean.TYPE, Boolean.TYPE, Integer.TYPE); + int.class, int.class, String.class, String.class, String.class, boolean.class, + boolean.class, int.class); static { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (CONSTRUCTOR_INPUT_METHOD_SUBTYPE == null) { diff --git a/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java new file mode 100644 index 000000000..eb180071e --- /dev/null +++ b/java/src/com/android/inputmethod/compat/NotificationCompatUtils.java @@ -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. + */ + +package com.android.inputmethod.compat; + +import android.app.Notification; +import android.os.Build; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class NotificationCompatUtils { + // Note that TextInfo.getCharSequence() is supposed to be available in API level 21 and later. + private static final Method METHOD_setColor = + CompatUtils.getMethod(Notification.Builder.class, "setColor", int.class); + private static final Method METHOD_setVisibility = + CompatUtils.getMethod(Notification.Builder.class, "setVisibility", int.class); + private static final Method METHOD_setCategory = + CompatUtils.getMethod(Notification.Builder.class, "setCategory", String.class); + private static final Method METHOD_setPriority = + CompatUtils.getMethod(Notification.Builder.class, "setPriority", int.class); + private static final Method METHOD_build = + CompatUtils.getMethod(Notification.Builder.class, "build"); + private static final Field FIELD_VISIBILITY_SECRET = + CompatUtils.getField(Notification.class, "VISIBILITY_SECRET"); + private static final int VISIBILITY_SECRET = null == FIELD_VISIBILITY_SECRET ? 0 + : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_VISIBILITY_SECRET); + private static final Field FIELD_CATEGORY_RECOMMENDATION = + CompatUtils.getField(Notification.class, "CATEGORY_RECOMMENDATION"); + private static final String CATEGORY_RECOMMENDATION = null == FIELD_CATEGORY_RECOMMENDATION ? "" + : (String) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_CATEGORY_RECOMMENDATION); + private static final Field FIELD_PRIORITY_LOW = + CompatUtils.getField(Notification.class, "PRIORITY_LOW"); + private static final int PRIORITY_LOW = null == FIELD_PRIORITY_LOW ? 0 + : (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */, + FIELD_PRIORITY_LOW); + + private NotificationCompatUtils() { + // This class is non-instantiable. + } + + // Sets the accent color + public static void setColor(final Notification.Builder builder, final int color) { + CompatUtils.invoke(builder, null, METHOD_setColor, color); + } + + public static void setVisibilityToSecret(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setVisibility, VISIBILITY_SECRET); + } + + public static void setCategoryToRecommendation(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setCategory, CATEGORY_RECOMMENDATION); + } + + public static void setPriorityToLow(final Notification.Builder builder) { + CompatUtils.invoke(builder, null, METHOD_setPriority, PRIORITY_LOW); + } + + public static Notification build(final Notification.Builder builder) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + // #build was added in API level 16, JELLY_BEAN + return (Notification) CompatUtils.invoke(builder, null, METHOD_build); + } else { + // #getNotification was deprecated in API level 16, JELLY_BEAN + return builder.getNotification(); + } + } +} diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 4c7663527..c33c01552 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -34,9 +34,9 @@ import java.util.ArrayList; public final class SuggestionSpanUtils { // Note that SuggestionSpan.FLAG_AUTO_CORRECTION has been introduced // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1). - public static final Field FIELD_FLAG_AUTO_CORRECTION = CompatUtils.getField( + private static final Field FIELD_FLAG_AUTO_CORRECTION = CompatUtils.getField( SuggestionSpan.class, "FLAG_AUTO_CORRECTION"); - public static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils.getFieldValue( + private static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils.getFieldValue( null /* receiver */, null /* defaultValue */, FIELD_FLAG_AUTO_CORRECTION); static { @@ -68,7 +68,7 @@ public final class SuggestionSpanUtils { public static CharSequence getTextWithSuggestionSpan(final Context context, final String pickedWord, final SuggestedWords suggestedWords) { if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty() - || suggestedWords.mIsPrediction || suggestedWords.isPunctuationSuggestions()) { + || suggestedWords.isPrediction() || suggestedWords.isPunctuationSuggestions()) { return pickedWord; } diff --git a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java index 6e32e74ab..1fb597ba6 100644 --- a/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/UserDictionaryCompatUtils.java @@ -26,7 +26,7 @@ public final class UserDictionaryCompatUtils { // UserDictionary.Words#addWord(Context, String, int, String, Locale) was introduced // in API level 16 (Build.VERSION_CODES.JELLY_BEAN). private static final Method METHOD_addWord = CompatUtils.getMethod(Words.class, "addWord", - Context.class, String.class, Integer.TYPE, String.class, Locale.class); + Context.class, String.class, int.class, String.class, Locale.class); @SuppressWarnings("deprecation") public static void addWord(final Context context, final String word, diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java index 767cc423d..0f00be133 100644 --- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java @@ -30,7 +30,13 @@ public final class ViewCompatUtils { View.class, "getPaddingEnd"); private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod( View.class, "setPaddingRelative", - Integer.TYPE, Integer.TYPE, Integer.TYPE, Integer.TYPE); + int.class, int.class, int.class, int.class); + // Note that View.setElevation(float) has been introduced in API level 21. + private static final Method METHOD_setElevation = CompatUtils.getMethod( + View.class, "setElevation", float.class); + // Note that View.setTextAlignment(int) has been introduced in API level 17. + private static final Method METHOD_setTextAlignment = CompatUtils.getMethod( + View.class, "setTextAlignment", int.class); private ViewCompatUtils() { // This utility class is not publicly instantiable. @@ -51,4 +57,21 @@ public final class ViewCompatUtils { } CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom); } + + public static void setElevation(final View view, final float elevation) { + CompatUtils.invoke(view, null, METHOD_setElevation, elevation); + } + + // These TEXT_ALIGNMENT_* constants have been introduced in API 17. + public static final int TEXT_ALIGNMENT_INHERIT = 0; + public static final int TEXT_ALIGNMENT_GRAVITY = 1; + public static final int TEXT_ALIGNMENT_TEXT_START = 2; + public static final int TEXT_ALIGNMENT_TEXT_END = 3; + public static final int TEXT_ALIGNMENT_CENTER = 4; + public static final int TEXT_ALIGNMENT_VIEW_START = 5; + public static final int TEXT_ALIGNMENT_VIEW_END = 6; + + public static void setTextAlignment(final View view, final int textAlignment) { + CompatUtils.invoke(view, null, METHOD_setTextAlignment, textAlignment); + } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java index 75cc7d4cb..3dbbc9b9b 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java +++ b/java/src/com/android/inputmethod/dictionarypack/DownloadManagerWrapper.java @@ -54,15 +54,13 @@ public class DownloadManagerWrapper { if (null != mDownloadManager) { mDownloadManager.remove(ids); } + } catch (IllegalArgumentException e) { + // This is expected to happen on boot when the device is encrypted. } catch (SQLiteException e) { // We couldn't remove the file from DownloadManager. Apparently, the database can't // be opened. It may be a problem with file system corruption. In any case, there is // not much we can do apart from avoiding crashing. Log.e(TAG, "Can't remove files with ID " + ids + " from download manager", e); - } catch (IllegalArgumentException e) { - // Not sure how this can happen, but it could be another case where the provider - // is disabled. Or it could be a bug in older versions of the framework. - Log.e(TAG, "Can't find the content URL for DownloadManager?", e); } } @@ -71,10 +69,10 @@ public class DownloadManagerWrapper { if (null != mDownloadManager) { return mDownloadManager.openDownloadedFile(fileId); } + } catch (IllegalArgumentException e) { + // This is expected to happen on boot when the device is encrypted. } catch (SQLiteException e) { Log.e(TAG, "Can't open downloaded file with ID " + fileId, e); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Can't find the content URL for DownloadManager?", e); } // We come here if mDownloadManager is null or if an exception was thrown. throw new FileNotFoundException(); @@ -85,10 +83,10 @@ public class DownloadManagerWrapper { if (null != mDownloadManager) { return mDownloadManager.query(query); } + } catch (IllegalArgumentException e) { + // This is expected to happen on boot when the device is encrypted. } catch (SQLiteException e) { Log.e(TAG, "Can't query the download manager", e); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Can't find the content URL for DownloadManager?", e); } // We come here if mDownloadManager is null or if an exception was thrown. return null; @@ -99,10 +97,10 @@ public class DownloadManagerWrapper { if (null != mDownloadManager) { return mDownloadManager.enqueue(request); } + } catch (IllegalArgumentException e) { + // This is expected to happen on boot when the device is encrypted. } catch (SQLiteException e) { Log.e(TAG, "Can't enqueue a request with the download manager", e); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Can't find the content URL for DownloadManager?", e); } return 0; } diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index 95a094232..6fbca44c5 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -31,12 +31,14 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.ConnectivityManager; import android.net.Uri; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.compat.ConnectivityManagerCompatUtils; import com.android.inputmethod.compat.DownloadManagerCompatUtils; +import com.android.inputmethod.compat.NotificationCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; @@ -858,7 +860,7 @@ public final class UpdateHandler { final String language = (null == locale ? "" : locale.getDisplayLanguage()); final String titleFormat = context.getString(R.string.dict_available_notification_title); final String notificationTitle = String.format(titleFormat, language); - final Notification notification = new Notification.Builder(context) + final Notification.Builder builder = new Notification.Builder(context) .setAutoCancel(true) .setContentIntent(notificationIntent) .setContentTitle(notificationTitle) @@ -866,8 +868,13 @@ public final class UpdateHandler { .setTicker(notificationTitle) .setOngoing(false) .setOnlyAlertOnce(true) - .setSmallIcon(R.drawable.ic_notify_dictionary) - .getNotification(); + .setSmallIcon(R.drawable.ic_notify_dictionary); + NotificationCompatUtils.setColor(builder, + context.getResources().getColor(R.color.notification_accent_color)); + NotificationCompatUtils.setPriorityToLow(builder); + NotificationCompatUtils.setVisibilityToSecret(builder); + NotificationCompatUtils.setCategoryToRecommendation(builder); + final Notification notification = NotificationCompatUtils.build(builder); notificationManager.notify(DICT_AVAILABLE_NOTIFICATION_ID, notification); } diff --git a/java/src/com/android/inputmethod/event/Combiner.java b/java/src/com/android/inputmethod/event/Combiner.java index 8b808c6b3..fee93f0c6 100644 --- a/java/src/com/android/inputmethod/event/Combiner.java +++ b/java/src/com/android/inputmethod/event/Combiner.java @@ -18,6 +18,8 @@ package com.android.inputmethod.event; import java.util.ArrayList; +import javax.annotation.Nonnull; + /** * A generic interface for combiners. Combiners are objects that transform chains of input events * into committable strings and manage feedback to show to the user on the combining state. @@ -33,6 +35,7 @@ public interface Combiner { * @param event the event to combine with the existing state. * @return the resulting event. */ + @Nonnull Event processEvent(ArrayList<Event> previousEvents, Event event); /** diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java index 61bc11b39..2d2731f21 100644 --- a/java/src/com/android/inputmethod/event/CombinerChain.java +++ b/java/src/com/android/inputmethod/event/CombinerChain.java @@ -24,6 +24,8 @@ import com.android.inputmethod.latin.Constants; import java.util.ArrayList; import java.util.HashMap; +import javax.annotation.Nonnull; + /** * This class implements the logic chain between receiving events and generating code points. * @@ -80,23 +82,43 @@ public class CombinerChain { } } + private void updateStateFeedback() { + mStateFeedback.clear(); + for (int i = mCombiners.size() - 1; i >= 0; --i) { + mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback()); + } + } + /** - * Pass a new event through the whole chain. + * Process an event through the combining chain, and return a processed event to apply. * @param previousEvents the list of previous events in this composition * @param newEvent the new event to process + * @return the processed event. It may be the same event, or a consumed event, or a completely + * new event. However it may never be null. */ - public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { + @Nonnull + public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { final ArrayList<Event> modifiablePreviousEvents = new ArrayList<>(previousEvents); Event event = newEvent; for (final Combiner combiner : mCombiners) { // A combiner can never return more than one event; it can return several // code points, but they should be encapsulated within one event. event = combiner.processEvent(modifiablePreviousEvents, event); - if (null == event) { - // Combiners return null if they eat the event. + if (event.isConsumed()) { + // If the event is consumed, then we don't pass it to subsequent combiners: + // they should not see it at all. break; } } + updateStateFeedback(); + return event; + } + + /** + * Apply a processed event. + * @param event the event to be applied + */ + public void applyProcessedEvent(final Event event) { if (null != event) { // TODO: figure out the generic way of doing this if (Constants.CODE_DELETE == event.mKeyCode) { @@ -112,10 +134,7 @@ public class CombinerChain { } } } - mStateFeedback.clear(); - for (int i = mCombiners.size() - 1; i >= 0; --i) { - mStateFeedback.append(mCombiners.get(i).getCombiningStateFeedback()); - } + updateStateFeedback(); } /** diff --git a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java index bef4d8594..4f3f4d25f 100644 --- a/java/src/com/android/inputmethod/event/DeadKeyCombiner.java +++ b/java/src/com/android/inputmethod/event/DeadKeyCombiner.java @@ -23,6 +23,8 @@ import com.android.inputmethod.latin.Constants; import java.util.ArrayList; +import javax.annotation.Nonnull; + /** * A combiner that handles dead keys. */ @@ -31,12 +33,18 @@ public class DeadKeyCombiner implements Combiner { final StringBuilder mDeadSequence = new StringBuilder(); @Override + @Nonnull public Event processEvent(final ArrayList<Event> previousEvents, final Event event) { - if (null == event) return null; // Just in case some combiner is broken if (TextUtils.isEmpty(mDeadSequence)) { + // No dead char is currently being tracked: this is the most common case. if (event.isDead()) { + // The event was a dead key. Start tracking it. mDeadSequence.appendCodePoint(event.mCodePoint); + return Event.createConsumedEvent(event); } + // Regular keystroke when not keeping track of a dead key. Simply said, there are + // no dead keys at all in the current input, so this combiner has nothing to do and + // simply returns the event as is. The majority of events will go through this path. return event; } else { // TODO: Allow combining for several dead chars rather than only the first one. diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index d257441e0..ef5b04747 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -67,6 +67,8 @@ public class Event { final private static int FLAG_DEAD = 0x1; // This event is coming from a key repeat, software or hardware. final private static int FLAG_REPEAT = 0x2; + // This event has already been consumed. + final private static int FLAG_CONSUMED = 0x4; final private int mEventType; // The type of event - one of the constants above // The code point associated with the event, if relevant. This is a unicode code point, and @@ -219,6 +221,18 @@ public class Event { null /* next */); } + /** + * Creates an event identical to the passed event, but that has already been consumed. + * @param source the event to copy the properties of. + * @return an identical event marked as consumed. + */ + public static Event createConsumedEvent(final Event source) { + // A consumed event should not input any text at all, so we pass the empty string as text. + return new Event(source.mEventType, source.mText, source.mCodePoint, source.mKeyCode, + source.mX, source.mY, source.mSuggestedWordInfo, source.mFlags | FLAG_CONSUMED, + source.mNextEvent); + } + public static Event createNotHandledEvent() { return new Event(EVENT_TYPE_NOT_HANDLED, null /* text */, NOT_A_CODE_POINT, NOT_A_KEY_CODE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE, @@ -241,6 +255,10 @@ public class Event { return 0 != (FLAG_REPEAT & mFlags); } + public boolean isConsumed() { return 0 != (FLAG_CONSUMED & mFlags); } + + public boolean isGesture() { return EVENT_TYPE_GESTURE == mEventType; } + // Returns whether this is a fake key press from the suggestion strip. This happens with // punctuation signs selected from the suggestion strip. public boolean isSuggestionStripPress() { @@ -252,6 +270,9 @@ public class Event { } public CharSequence getTextToCommit() { + if (isConsumed()) { + return ""; // A consumed event should input no text. + } switch (mEventType) { case EVENT_TYPE_MODE_KEY: case EVENT_TYPE_NOT_HANDLED: diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java index cdff265c6..5bc9111de 100644 --- a/java/src/com/android/inputmethod/event/InputTransaction.java +++ b/java/src/com/android/inputmethod/event/InputTransaction.java @@ -42,6 +42,7 @@ public class InputTransaction { private int mRequiredShiftUpdate = SHIFT_NO_UPDATE; private boolean mRequiresUpdateSuggestions = false; private boolean mDidAffectContents = false; + private boolean mDidAutoCorrect = false; public InputTransaction(final SettingsValues settingsValues, final Event event, final long timestamp, final int spaceState, final int shiftState) { @@ -97,4 +98,19 @@ public class InputTransaction { public boolean didAffectContents() { return mDidAffectContents; } + + /** + * Indicate that this transaction performed an auto-correction. + */ + public void setDidAutoCorrect() { + mDidAutoCorrect = true; + } + + /** + * Find out whether this transaction performed an auto-correction. + * @return Whether this transaction performed an auto-correction. + */ + public boolean didAutoCorrect() { + return mDidAutoCorrect; + } } diff --git a/java/src/com/android/inputmethod/event/MyanmarReordering.java b/java/src/com/android/inputmethod/event/MyanmarReordering.java index 32919932d..dcd06c899 100644 --- a/java/src/com/android/inputmethod/event/MyanmarReordering.java +++ b/java/src/com/android/inputmethod/event/MyanmarReordering.java @@ -21,6 +21,8 @@ import com.android.inputmethod.latin.Constants; import java.util.ArrayList; import java.util.Arrays; +import javax.annotation.Nonnull; + /** * A combiner that reorders input for Myanmar. */ @@ -111,7 +113,7 @@ public class MyanmarReordering implements Combiner { * Clears the currently combining stream of events and returns the resulting software text * event corresponding to the stream. Optionally adds a new event to the cleared stream. * @param newEvent the new event to add to the stream. null if none. - * @return the resulting software text event. Null if none. + * @return the resulting software text event. Never null. */ private Event clearAndGetResultingEvent(final Event newEvent) { final CharSequence combinedText; @@ -124,18 +126,19 @@ public class MyanmarReordering implements Combiner { if (null != newEvent) { mCurrentEvents.add(newEvent); } - return null == combinedText ? null + return null == combinedText ? Event.createConsumedEvent(newEvent) : Event.createSoftwareTextEvent(combinedText, Event.NOT_A_KEY_CODE); } @Override + @Nonnull public Event processEvent(ArrayList<Event> previousEvents, Event newEvent) { final int codePoint = newEvent.mCodePoint; if (VOWEL_E == codePoint) { final Event lastEvent = getLastEvent(); if (null == lastEvent) { mCurrentEvents.add(newEvent); - return null; + return Event.createConsumedEvent(newEvent); } else if (isConsonantOrMedial(lastEvent.mCodePoint)) { final Event resultingEvent = clearAndGetResultingEvent(null); mCurrentEvents.add(Event.createSoftwareKeypressEvent(ZERO_WIDTH_NON_JOINER, @@ -151,7 +154,7 @@ public class MyanmarReordering implements Combiner { final Event lastEvent = getLastEvent(); if (null == lastEvent) { mCurrentEvents.add(newEvent); - return null; + return Event.createConsumedEvent(newEvent); } else if (VOWEL_E == lastEvent.mCodePoint) { final int eventSize = mCurrentEvents.size(); if (eventSize >= 2 @@ -162,7 +165,7 @@ public class MyanmarReordering implements Combiner { mCurrentEvents.remove(eventSize - 2); mCurrentEvents.add(newEvent); mCurrentEvents.add(lastEvent); - return null; + return Event.createConsumedEvent(newEvent); } // If there is already a consonant, then we are starting a new syllable. for (int i = eventSize - 2; i >= 0; --i) { @@ -174,7 +177,7 @@ public class MyanmarReordering implements Combiner { mCurrentEvents.remove(eventSize - 1); mCurrentEvents.add(newEvent); mCurrentEvents.add(lastEvent); - return null; + return Event.createConsumedEvent(newEvent); } else { // lastCodePoint is a consonant/medial. But if it's something else it's fine return clearAndGetResultingEvent(newEvent); } @@ -182,7 +185,7 @@ public class MyanmarReordering implements Combiner { final Event lastEvent = getLastEvent(); if (null == lastEvent) { mCurrentEvents.add(newEvent); - return null; + return Event.createConsumedEvent(newEvent); } else if (VOWEL_E == lastEvent.mCodePoint) { final int eventSize = mCurrentEvents.size(); // If there is already a consonant, then we are in the middle of a syllable, and we @@ -198,7 +201,7 @@ public class MyanmarReordering implements Combiner { mCurrentEvents.remove(eventSize - 1); mCurrentEvents.add(newEvent); mCurrentEvents.add(lastEvent); - return null; + return Event.createConsumedEvent(newEvent); } // Otherwise, we just commit everything. return clearAndGetResultingEvent(null); @@ -228,10 +231,10 @@ public class MyanmarReordering implements Combiner { mCurrentEvents.remove(eventSize - 1); } } - return null; + return Event.createConsumedEvent(newEvent); } else if (eventSize > 0) { mCurrentEvents.remove(eventSize - 1); - return null; + return Event.createConsumedEvent(newEvent); } } } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index af54fb674..81ea90a4d 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -58,7 +58,9 @@ public class Key implements Comparable<Key> { private final String mHintLabel; /** Flags of the label */ private final int mLabelFlags; - private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08; + private static final int LABEL_FLAGS_ALIGN_HINT_LABEL_TO_BOTTOM = 0x02; + private static final int LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM = 0x04; + private static final int LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER = 0x08; // Font typeface specification. private static final int LABEL_FLAGS_FONT_MASK = 0x30; private static final int LABEL_FLAGS_FONT_NORMAL = 0x10; @@ -69,7 +71,6 @@ public class Key implements Comparable<Key> { private static final int LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO = 0x40; private static final int LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO = 0x80; private static final int LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO = 0xC0; - private static final int LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO = 0x100; private static final int LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO = 0x140; // End of key text ratio mask enum values private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200; @@ -86,6 +87,7 @@ public class Key implements Comparable<Key> { private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000; private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000; private static final int LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR = 0x80000; + private static final int LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO = 0x100000; private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000; private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000; @@ -107,11 +109,23 @@ public class Key implements Comparable<Key> { private final MoreKeySpec[] mMoreKeys; /** More keys column number and flags */ private final int mMoreKeysColumnAndFlags; - private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff; - private static final int MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER = 0x80000000; + private static final int MORE_KEYS_COLUMN_NUMBER_MASK = 0x000000ff; + // If this flag is specified, more keys keyboard should have the specified number of columns. + // Otherwise more keys keyboard should have less than or equal to the specified maximum number + // of columns. + private static final int MORE_KEYS_FLAGS_FIXED_COLUMN = 0x00000100; + // If this flag is specified, the order of more keys is determined by the order in the more + // keys' specification. Otherwise the order of more keys is automatically determined. + private static final int MORE_KEYS_FLAGS_FIXED_ORDER = 0x00000200; + private static final int MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER = 0; + private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER = + MORE_KEYS_FLAGS_FIXED_COLUMN; + private static final int MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER = + (MORE_KEYS_FLAGS_FIXED_COLUMN | MORE_KEYS_FLAGS_FIXED_ORDER); private static final int MORE_KEYS_FLAGS_HAS_LABELS = 0x40000000; private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000; private static final int MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY = 0x10000000; + // TODO: Rename these specifiers to !autoOrder! and !fixedOrder! respectively. private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!"; private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!"; private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!"; @@ -123,9 +137,10 @@ public class Key implements Comparable<Key> { public static final int BACKGROUND_TYPE_EMPTY = 0; public static final int BACKGROUND_TYPE_NORMAL = 1; public static final int BACKGROUND_TYPE_FUNCTIONAL = 2; - public static final int BACKGROUND_TYPE_ACTION = 3; - public static final int BACKGROUND_TYPE_STICKY_OFF = 4; - public static final int BACKGROUND_TYPE_STICKY_ON = 5; + public static final int BACKGROUND_TYPE_STICKY_OFF = 3; + public static final int BACKGROUND_TYPE_STICKY_ON = 4; + public static final int BACKGROUND_TYPE_ACTION = 5; + public static final int BACKGROUND_TYPE_SPACEBAR = 6; private final int mActionFlags; private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01; @@ -253,25 +268,31 @@ public class Key implements Comparable<Key> { int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); - int moreKeysColumn = style.getInt(keyAttr, - R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); + // Get maximum column order number and set a relevant mode value. + int moreKeysColumnAndFlags = MORE_KEYS_MODE_MAX_COLUMN_WITH_AUTO_ORDER + | style.getInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn, + params.mMaxMoreKeysKeyboardColumn); int value; if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) { - moreKeysColumn = value & MORE_KEYS_COLUMN_MASK; + // Override with fixed column order number and set a relevant mode value. + moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_AUTO_ORDER + | (value & MORE_KEYS_COLUMN_NUMBER_MASK); } if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) { - moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK); + // Override with fixed column order number and set a relevant mode value. + moreKeysColumnAndFlags = MORE_KEYS_MODE_FIXED_COLUMN_WITH_FIXED_ORDER + | (value & MORE_KEYS_COLUMN_NUMBER_MASK); } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) { - moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_HAS_LABELS; } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) { - moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; } if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) { - moreKeysColumn |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY; + moreKeysColumnAndFlags |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY; } - mMoreKeysColumnAndFlags = moreKeysColumn; + mMoreKeysColumnAndFlags = moreKeysColumnAndFlags; final String[] additionalMoreKeys; if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) { @@ -483,9 +504,10 @@ public class Key implements Comparable<Key> { case BACKGROUND_TYPE_EMPTY: return "empty"; case BACKGROUND_TYPE_NORMAL: return "normal"; case BACKGROUND_TYPE_FUNCTIONAL: return "functional"; - case BACKGROUND_TYPE_ACTION: return "action"; case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff"; case BACKGROUND_TYPE_STICKY_ON: return "stickyOn"; + case BACKGROUND_TYPE_ACTION: return "action"; + case BACKGROUND_TYPE_SPACEBAR: return "spacebar"; default: return null; } } @@ -526,6 +548,10 @@ public class Key implements Comparable<Key> { return this instanceof Spacer; } + public final boolean isActionKey() { + return mBackgroundType == BACKGROUND_TYPE_ACTION; + } + public final boolean isShift() { return mCode == CODE_SHIFT; } @@ -577,8 +603,6 @@ public class Key implements Comparable<Key> { return params.mLargeLetterSize; case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO: return params.mLabelSize; - case LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO: - return params.mLargeLabelSize; case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO: return params.mHintLabelSize; default: // No follow key ratio flag specified. @@ -641,8 +665,16 @@ public class Key implements Comparable<Key> { return Typeface.DEFAULT_BOLD; } - public final boolean isAlignLeftOfCenter() { - return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; + public final boolean isAlignHintLabelToBottom(final int defaultFlags) { + return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_ALIGN_HINT_LABEL_TO_BOTTOM) != 0; + } + + public final boolean isAlignIconToBottom() { + return (mLabelFlags & LABEL_FLAGS_ALIGN_ICON_TO_BOTTOM) != 0; + } + + public final boolean isAlignLabelOffCenter() { + return (mLabelFlags & LABEL_FLAGS_ALIGN_LABEL_OFF_CENTER) != 0; } public final boolean hasPopupHint() { @@ -666,17 +698,29 @@ public class Key implements Comparable<Key> { return (mLabelFlags & LABEL_FLAGS_AUTO_SCALE) == LABEL_FLAGS_AUTO_SCALE; } + public final boolean needsToKeepBackgroundAspectRatio(final int defaultFlags) { + return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO) != 0; + } + + public final boolean hasCustomActionLabel() { + return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0; + } + private final boolean isShiftedLetterActivated() { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel); } - public final int getMoreKeysColumn() { - return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; + public final int getMoreKeysColumnNumber() { + return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_NUMBER_MASK; } - public final boolean isFixedColumnOrderMoreKeys() { - return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; + public final boolean isMoreKeysFixedColumn() { + return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN) != 0; + } + + public final boolean isMoreKeysFixedOrder() { + return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_ORDER) != 0; } public final boolean hasLabelsInMoreKeys() { @@ -684,9 +728,10 @@ public class Key implements Comparable<Key> { } public final int getMoreKeyLabelFlags() { - return hasLabelsInMoreKeys() + final int labelSizeFlag = hasLabelsInMoreKeys() ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO; + return labelSizeFlag | LABEL_FLAGS_AUTO_X_SCALE; } public final boolean needsDividersInMoreKeys() { @@ -814,47 +859,37 @@ public class Key implements Comparable<Key> { return dx * dx + dy * dy; } - private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = { - android.R.attr.state_checkable, - android.R.attr.state_checked - }; - - private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = { - android.R.attr.state_pressed, - android.R.attr.state_checkable, - android.R.attr.state_checked - }; - - private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = { - android.R.attr.state_checkable - }; - - private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = { - android.R.attr.state_pressed, - android.R.attr.state_checkable - }; - - private final static int[] KEY_STATE_NORMAL = { - }; + static class KeyBackgroundState { + private final int[] mReleasedState; + private final int[] mPressedState; - private final static int[] KEY_STATE_PRESSED = { - android.R.attr.state_pressed - }; - - private final static int[] KEY_STATE_EMPTY = { - android.R.attr.state_empty - }; + private KeyBackgroundState(final int ... attrs) { + mReleasedState = attrs; + mPressedState = Arrays.copyOf(attrs, attrs.length + 1); + mPressedState[attrs.length] = android.R.attr.state_pressed; + } - // action normal state (with properties) - private static final int[] KEY_STATE_ACTIVE_NORMAL = { - android.R.attr.state_active - }; + public int[] getState(final boolean pressed) { + return pressed ? mPressedState : mReleasedState; + } - // action pressed state (with properties) - private static final int[] KEY_STATE_ACTIVE_PRESSED = { - android.R.attr.state_active, - android.R.attr.state_pressed - }; + public static final KeyBackgroundState[] STATES = { + // 0: BACKGROUND_TYPE_EMPTY + new KeyBackgroundState(android.R.attr.state_empty), + // 1: BACKGROUND_TYPE_NORMAL + new KeyBackgroundState(), + // 2: BACKGROUND_TYPE_FUNCTIONAL + new KeyBackgroundState(), + // 3: BACKGROUND_TYPE_STICKY_OFF + new KeyBackgroundState(android.R.attr.state_checkable), + // 4: BACKGROUND_TYPE_STICKY_ON + new KeyBackgroundState(android.R.attr.state_checkable, android.R.attr.state_checked), + // 5: BACKGROUND_TYPE_ACTION + new KeyBackgroundState(android.R.attr.state_active), + // 6: BACKGROUND_TYPE_SPACEBAR + new KeyBackgroundState(), + }; + } /** * Returns the background drawable for the key, based on the current state and type of the key. @@ -866,33 +901,13 @@ public class Key implements Comparable<Key> { final Drawable background; if (mBackgroundType == BACKGROUND_TYPE_FUNCTIONAL) { background = functionalKeyBackground; - } else if (getCode() == Constants.CODE_SPACE) { + } else if (mBackgroundType == BACKGROUND_TYPE_SPACEBAR) { background = spacebarBackground; } else { background = keyBackground; } - final int[] stateSet; - switch (mBackgroundType) { - case BACKGROUND_TYPE_ACTION: - stateSet = mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; - break; - case BACKGROUND_TYPE_STICKY_OFF: - stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; - break; - case BACKGROUND_TYPE_STICKY_ON: - stateSet = mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; - break; - case BACKGROUND_TYPE_EMPTY: - stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_EMPTY; - break; - case BACKGROUND_TYPE_FUNCTIONAL: - stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; - break; - default: /* BACKGROUND_TYPE_NORMAL */ - stateSet = mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; - break; - } - background.setState(stateSet); + final int[] state = KeyBackgroundState.STATES[mBackgroundType].getState(mPressed); + background.setState(state); return background; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 870ac86e1..feb79efe9 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -113,13 +113,21 @@ public final class KeyboardLayoutSet { boolean mIsSpellChecker; int mKeyboardWidth; int mKeyboardHeight; - int mScriptId; + int mScriptId = ScriptUtils.SCRIPT_LATIN; // Sparse array of KeyboardLayoutSet element parameters indexed by element's id. final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap = new SparseArray<>(); } - public static void clearKeyboardCache() { + public static void onSystemLocaleChanged() { + clearKeyboardCache(); + } + + public static void onKeyboardThemeChanged() { + clearKeyboardCache(); + } + + private static void clearKeyboardCache() { sKeyboardCache.clear(); sKeysCache.clear(); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index f35126750..60665f8de 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -37,6 +37,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.ScriptUtils; @@ -59,7 +60,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private KeyboardLayoutSet mKeyboardLayoutSet; // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}. private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet(); - private SettingsValues mCurrentSettingsValues; private KeyboardTheme mKeyboardTheme; private Context mThemeContext; @@ -101,7 +101,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) { mKeyboardTheme = keyboardTheme; mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); - KeyboardLayoutSet.clearKeyboardCache(); + KeyboardLayoutSet.onKeyboardThemeChanged(); return true; } return false; @@ -119,7 +119,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); mKeyboardLayoutSet = builder.build(); - mCurrentSettingsValues = settingsValues; try { mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState); mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext); @@ -143,19 +142,24 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private void setKeyboard(final Keyboard keyboard) { // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}. - setMainKeyboardFrame(); + final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); + setMainKeyboardFrame(currentSettingsValues); + // TODO: pass this object to setKeyboard instead of getting the current values. final MainKeyboardView keyboardView = mKeyboardView; final Keyboard oldKeyboard = keyboardView.getKeyboard(); keyboardView.setKeyboard(keyboard); mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding); keyboardView.setKeyPreviewPopupEnabled( - mCurrentSettingsValues.mKeyPreviewPopupOn, - mCurrentSettingsValues.mKeyPreviewPopupDismissDelay); + currentSettingsValues.mKeyPreviewPopupOn, + currentSettingsValues.mKeyPreviewPopupDismissDelay); keyboardView.setKeyPreviewAnimationParams( - mCurrentSettingsValues.mKeyPreviewShowUpStartScale, - mCurrentSettingsValues.mKeyPreviewShowUpDuration, - mCurrentSettingsValues.mKeyPreviewDismissEndScale, - mCurrentSettingsValues.mKeyPreviewDismissDuration); + currentSettingsValues.mHasCustomKeyPreviewAnimationParams, + currentSettingsValues.mKeyPreviewShowUpStartXScale, + currentSettingsValues.mKeyPreviewShowUpStartYScale, + currentSettingsValues.mKeyPreviewShowUpDuration, + currentSettingsValues.mKeyPreviewDismissEndXScale, + currentSettingsValues.mKeyPreviewDismissEndYScale, + currentSettingsValues.mKeyPreviewDismissDuration); keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady()); final boolean subtypeChanged = (oldKeyboard == null) || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); @@ -232,8 +236,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); } - private void setMainKeyboardFrame() { - mMainKeyboardFrame.setVisibility(View.VISIBLE); + private void setMainKeyboardFrame(final SettingsValues settingsValues) { + mMainKeyboardFrame.setVisibility( + settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE); mEmojiPalettesView.setVisibility(View.GONE); mEmojiPalettesView.stopEmojiPalettes(); } @@ -249,6 +254,16 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mEmojiPalettesView.setVisibility(View.VISIBLE); } + public void onToggleEmojiKeyboard() { + if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) { + mLatinIME.startShowingInputView(); + setEmojiKeyboard(); + } else { + mLatinIME.stopShowingInputView(); + setAlphabetKeyboard(); + } + } + // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { @@ -337,7 +352,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { R.layout.input_view, null); mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById( - R.id.emoji_keyboard_view); + R.id.emoji_palettes_view); mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view); mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 4c2e0dd1d..7161d3f26 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -17,11 +17,11 @@ package com.android.inputmethod.keyboard; import android.content.SharedPreferences; -import android.os.Build; import android.os.Build.VERSION_CODES; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.R; import java.util.Arrays; @@ -32,6 +32,8 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916"; static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509"; + // These should be aligned with Keyboard.themeId and Keyboard.Case.keyboardTheme + // attributes' values in attrs.xml. public static final int THEME_ID_ICS = 0; public static final int THEME_ID_KLP = 2; public static final int THEME_ID_LXX_LIGHT = 3; @@ -39,17 +41,16 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public static final int DEFAULT_THEME_ID = THEME_ID_KLP; private static final KeyboardTheme[] KEYBOARD_THEMES = { - new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS, + new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS, // This has never been selected because we support ICS or later. VERSION_CODES.BASE), - new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP, + new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP, // Default theme for ICS, JB, and KLP. VERSION_CODES.ICE_CREAM_SANDWICH), - new KeyboardTheme(THEME_ID_LXX_LIGHT, R.style.KeyboardTheme_LXX_Light, + new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light, // Default theme for LXX. - // TODO: Update this constant once the *next* version becomes available. - VERSION_CODES.CUR_DEVELOPMENT), - new KeyboardTheme(THEME_ID_LXX_DARK, R.style.KeyboardTheme_LXX_Dark, + BuildCompatUtils.VERSION_CODES_LXX), + new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark, VERSION_CODES.BASE), }; @@ -60,12 +61,15 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public final int mThemeId; public final int mStyleId; + public final String mThemeName; private final int mMinApiVersion; // Note: The themeId should be aligned with "themeId" attribute of Keyboard style // in values/themes-<style>.xml. - private KeyboardTheme(final int themeId, final int styleId, final int minApiVersion) { + private KeyboardTheme(final int themeId, final String themeName, final int styleId, + final int minApiVersion) { mThemeId = themeId; + mThemeName = themeName; mStyleId = styleId; mMinApiVersion = minApiVersion; } @@ -99,15 +103,6 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return null; } - private static int getSdkVersion() { - final int sdkVersion = Build.VERSION.SDK_INT; - // TODO: Consider to remove this check once the *next* version becomes available. - if (sdkVersion > VERSION_CODES.KITKAT) { - return VERSION_CODES.CUR_DEVELOPMENT; - } - return sdkVersion; - } - @UsedForTesting static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { @@ -138,9 +133,14 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return searchKeyboardThemeById(DEFAULT_THEME_ID); } + public static String getKeyboardThemeName(final int themeId) { + final KeyboardTheme theme = searchKeyboardThemeById(themeId); + return theme.mThemeName; + } + public static void saveKeyboardThemeId(final String themeIdString, final SharedPreferences prefs) { - saveKeyboardThemeId(themeIdString, prefs, getSdkVersion()); + saveKeyboardThemeId(themeIdString, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting @@ -159,7 +159,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) { - return getKeyboardTheme(prefs, getSdkVersion()); + return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index f967f620a..98cd1da54 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -29,6 +29,7 @@ import android.graphics.Region; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; @@ -47,7 +48,9 @@ import java.util.HashSet; * @attr ref R.styleable#KeyboardView_functionalKeyBackground * @attr ref R.styleable#KeyboardView_spacebarBackground * @attr ref R.styleable#KeyboardView_spacebarIconWidthRatio + * @attr ref R.styleable#Keyboard_Key_keyLabelFlags * @attr ref R.styleable#KeyboardView_keyHintLetterPadding + * @attr ref R.styleable#KeyboardView_keyPopupHintLetter * @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding * @attr ref R.styleable#KeyboardView_keyTextShadowRadius @@ -60,6 +63,8 @@ import java.util.HashSet; * @attr ref R.styleable#Keyboard_Key_keyHintLetterRatio * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintRatio * @attr ref R.styleable#Keyboard_Key_keyHintLabelRatio + * @attr ref R.styleable#Keyboard_Key_keyLabelOffCenterRatio + * @attr ref R.styleable#Keyboard_Key_keyHintLabelOffCenterRatio * @attr ref R.styleable#Keyboard_Key_keyPreviewTextRatio * @attr ref R.styleable#Keyboard_Key_keyTextColor * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled @@ -73,7 +78,11 @@ import java.util.HashSet; public class KeyboardView extends View { // XML attributes private final KeyVisualAttributes mKeyVisualAttributes; + // Default keyLabelFlags from {@link KeyboardTheme}. + // Currently only "alignHintLabelToBottom" is supported. + private final int mDefaultKeyLabelFlags; private final float mKeyHintLetterPadding; + private final String mKeyPopupHintLetter; private final float mKeyPopupHintLetterPadding; private final float mKeyShiftedLetterHintPadding; private final float mKeyTextShadowRadius; @@ -85,9 +94,6 @@ public class KeyboardView extends View { private final Rect mKeyBackgroundPadding = new Rect(); private static final float KET_TEXT_SHADOW_RADIUS_DISABLED = -1.0f; - // HORIZONTAL ELLIPSIS "...", character for popup hint. - private static final String POPUP_HINT_CHAR = "\u2026"; - // The maximum key label width in the proportion to the key width. private static final float MAX_LABEL_RATIO = 0.90f; @@ -132,6 +138,8 @@ public class KeyboardView extends View { R.styleable.KeyboardView_spacebarIconWidthRatio, 1.0f); mKeyHintLetterPadding = keyboardViewAttr.getDimension( R.styleable.KeyboardView_keyHintLetterPadding, 0.0f); + mKeyPopupHintLetter = keyboardViewAttr.getString( + R.styleable.KeyboardView_keyPopupHintLetter); mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension( R.styleable.KeyboardView_keyPopupHintLetterPadding, 0.0f); mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension( @@ -144,6 +152,7 @@ public class KeyboardView extends View { final TypedArray keyAttr = context.obtainStyledAttributes(attrs, R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView); + mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0); mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); keyAttr.recycle(); @@ -331,11 +340,27 @@ public class KeyboardView extends View { // Draw key background. protected void onDrawKeyBackground(final Key key, final Canvas canvas, final Drawable background) { - final Rect padding = mKeyBackgroundPadding; - final int bgWidth = key.getDrawWidth() + padding.left + padding.right; - final int bgHeight = key.getHeight() + padding.top + padding.bottom; - final int bgX = -padding.left; - final int bgY = -padding.top; + final int keyWidth = key.getDrawWidth(); + final int keyHeight = key.getHeight(); + final int bgWidth, bgHeight, bgX, bgY; + if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags) + // HACK: To disable expanding normal/functional key background. + && !key.hasCustomActionLabel()) { + final int intrinsicWidth = background.getIntrinsicWidth(); + final int intrinsicHeight = background.getIntrinsicHeight(); + final float minScale = Math.min( + keyWidth / (float)intrinsicWidth, keyHeight / (float)intrinsicHeight); + bgWidth = (int)(intrinsicWidth * minScale); + bgHeight = (int)(intrinsicHeight * minScale); + bgX = (keyWidth - bgWidth) / 2; + bgY = (keyHeight - bgHeight) / 2; + } else { + final Rect padding = mKeyBackgroundPadding; + bgWidth = keyWidth + padding.left + padding.right; + bgHeight = keyHeight + padding.top + padding.bottom; + bgX = -padding.left; + bgY = -padding.top; + } final Rect bounds = background.getBounds(); if (bgWidth != bounds.right || bgHeight != bounds.bottom) { background.setBounds(0, 0, bgWidth, bgHeight); @@ -355,7 +380,8 @@ public class KeyboardView extends View { // Draw key label. final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha); - float positionX = centerX; + float labelX = centerX; + float labelBaseline = centerY; final String label = key.getLabel(); if (label != null) { paint.setTypeface(key.selectTypeface(params)); @@ -364,15 +390,15 @@ public class KeyboardView extends View { final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); // Vertical label text alignment. - final float baseline = centerY + labelCharHeight / 2.0f; + labelBaseline = centerY + labelCharHeight / 2.0f; // Horizontal label text alignment - if (key.isAlignLeftOfCenter()) { - // TODO: Parameterise this? - positionX = centerX - labelCharWidth * 7.0f / 4.0f; + if (key.isAlignLabelOffCenter()) { + // The label is placed off center of the key. Used mainly on "phone number" layout. + labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth; paint.setTextAlign(Align.LEFT); } else { - positionX = centerX; + labelX = centerX; paint.setTextAlign(Align.CENTER); } if (key.needsAutoXScale()) { @@ -400,7 +426,7 @@ public class KeyboardView extends View { paint.clearShadowLayer(); } blendAlpha(paint, params.mAnimAlpha); - canvas.drawText(label, 0, label.length(), positionX, baseline, paint); + canvas.drawText(label, 0, label.length(), labelX, labelBaseline, paint); // Turn off drop shadow and reset x-scale. paint.clearShadowLayer(); paint.setTextScaleX(1.0f); @@ -416,22 +442,22 @@ public class KeyboardView extends View { blendAlpha(paint, params.mAnimAlpha); final float labelCharHeight = TypefaceUtils.getReferenceCharHeight(paint); final float labelCharWidth = TypefaceUtils.getReferenceCharWidth(paint); - final KeyVisualAttributes visualAttr = key.getVisualAttributes(); - final float adjustmentY = (visualAttr == null) ? 0.0f - : visualAttr.mHintLabelVerticalAdjustment * labelCharHeight; - final float hintX, hintY; + final float hintX, hintBaseline; if (key.hasHintLabel()) { // The hint label is placed just right of the key label. Used mainly on // "phone number" layout. - // TODO: Generalize the following calculations. - hintX = positionX + labelCharWidth * 2.0f; - hintY = centerY + labelCharHeight / 2.0f; + hintX = labelX + params.mHintLabelOffCenterRatio * labelCharWidth; + if (key.isAlignHintLabelToBottom(mDefaultKeyLabelFlags)) { + hintBaseline = labelBaseline; + } else { + hintBaseline = centerY + labelCharHeight / 2.0f; + } paint.setTextAlign(Align.LEFT); } else if (key.hasShiftedLetterHint()) { // The hint label is placed at top-right corner of the key. Used mainly on tablet. hintX = keyWidth - mKeyShiftedLetterHintPadding - labelCharWidth / 2.0f; paint.getFontMetrics(mFontMetrics); - hintY = -mFontMetrics.top; + hintBaseline = -mFontMetrics.top; paint.setTextAlign(Align.CENTER); } else { // key.hasHintLetter() // The hint letter is placed at top-right corner of the key. Used mainly on phone. @@ -439,10 +465,12 @@ public class KeyboardView extends View { final float hintLabelWidth = TypefaceUtils.getStringWidth(hintLabel, paint); hintX = keyWidth - mKeyHintLetterPadding - Math.max(hintDigitWidth, hintLabelWidth) / 2.0f; - hintY = -paint.ascent(); + hintBaseline = -paint.ascent(); paint.setTextAlign(Align.CENTER); } - canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY + adjustmentY, paint); + final float adjustmentY = params.mHintLabelVerticalAdjustment * labelCharHeight; + canvas.drawText( + hintLabel, 0, hintLabel.length(), hintX, hintBaseline + adjustmentY, paint); } // Draw key icon. @@ -454,9 +482,13 @@ public class KeyboardView extends View { iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth); } final int iconHeight = icon.getIntrinsicHeight(); - // Align center. - final int iconY = (keyHeight - iconHeight) / 2; - final int iconX = (keyWidth - iconWidth) / 2; + final int iconY; + if (key.isAlignIconToBottom()) { + iconY = keyHeight - iconHeight; + } else { + iconY = (keyHeight - iconHeight) / 2; // Align vertically center. + } + final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center. drawIcon(canvas, icon, iconX, iconY, iconWidth, iconHeight); } @@ -468,6 +500,9 @@ public class KeyboardView extends View { // Draw popup hint "..." at the bottom right corner of the key. protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params) { + if (TextUtils.isEmpty(mKeyPopupHintLetter)) { + return; + } final int keyWidth = key.getDrawWidth(); final int keyHeight = key.getHeight(); @@ -478,7 +513,7 @@ public class KeyboardView extends View { final float hintX = keyWidth - mKeyHintLetterPadding - TypefaceUtils.getReferenceCharWidth(paint) / 2.0f; final float hintY = keyHeight - mKeyPopupHintLetterPadding; - canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint); + canvas.drawText(mKeyPopupHintLetter, hintX, hintY, paint); } protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x, diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 702efb3d7..2b16785c2 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -46,6 +46,7 @@ import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview; import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer; import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; +import com.android.inputmethod.keyboard.internal.KeyPreviewView; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; @@ -85,7 +86,10 @@ import java.util.WeakHashMap; * @attr ref R.styleable#MainKeyboardView_keyPreviewOffset * @attr ref R.styleable#MainKeyboardView_keyPreviewHeight * @attr ref R.styleable#MainKeyboardView_keyPreviewLingerTimeout + * @attr ref R.styleable#MainKeyboardView_keyPreviewShowUpAnimator + * @attr ref R.styleable#MainKeyboardView_keyPreviewDismissAnimator * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardLayout + * @attr ref R.styleable#MainKeyboardView_moreKeysKeyboardForActionLayout * @attr ref R.styleable#MainKeyboardView_backgroundDimAlpha * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint * @attr ref R.styleable#MainKeyboardView_gestureFloatingPreviewTextLingerTimeout @@ -145,6 +149,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final Paint mBackgroundDimAlphaPaint = new Paint(); private boolean mNeedsToDimEntireKeyboard; private final View mMoreKeysKeyboardContainer; + private final View mMoreKeysKeyboardForActionContainer; private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>(); private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; // More keys panel (used by both more keys keyboard and more suggestions view) @@ -229,6 +234,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack final int moreKeysKeyboardLayoutId = mainKeyboardViewAttr.getResourceId( R.styleable.MainKeyboardView_moreKeysKeyboardLayout, 0); + final int moreKeysKeyboardForActionLayoutId = mainKeyboardViewAttr.getResourceId( + R.styleable.MainKeyboardView_moreKeysKeyboardForActionLayout, + moreKeysKeyboardLayoutId); mConfigShowMoreKeysKeyboardAtTouchedPoint = mainKeyboardViewAttr.getBoolean( R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); @@ -246,8 +254,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mSlidingKeyInputDrawingPreview.setDrawingView(mDrawingPreviewPlacerView); mainKeyboardViewAttr.recycle(); - mMoreKeysKeyboardContainer = LayoutInflater.from(getContext()) - .inflate(moreKeysKeyboardLayoutId, null); + final LayoutInflater inflater = LayoutInflater.from(getContext()); + mMoreKeysKeyboardContainer = inflater.inflate(moreKeysKeyboardLayoutId, null); + mMoreKeysKeyboardForActionContainer = inflater.inflate( + moreKeysKeyboardForActionLayoutId, null); mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator( languageOnSpacebarFadeoutAnimatorResId, this); mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator( @@ -390,20 +400,34 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } /** - * Enables or disables the key feedback popup. This is a popup that shows a magnified + * Enables or disables the key preview popup. This is a popup that shows a magnified * version of the depressed key. By default the preview is enabled. * @param previewEnabled whether or not to enable the key feedback preview * @param delay the delay after which the preview is dismissed - * @see #isKeyPreviewPopupEnabled() */ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) { mKeyPreviewDrawParams.setPopupEnabled(previewEnabled, delay); } - public void setKeyPreviewAnimationParams(final float showUpStartScale, final int showUpDuration, - final float dismissEndScale, final int dismissDuration) { - mKeyPreviewDrawParams.setAnimationParams( - showUpStartScale, showUpDuration, dismissEndScale, dismissDuration); + /** + * Enables or disables the key preview popup animations and set animations' parameters. + * + * @param hasCustomAnimationParams false to use the default key preview popup animations + * specified by keyPreviewShowUpAnimator and keyPreviewDismissAnimator attributes. + * true to override the default animations with the specified parameters. + * @param showUpStartXScale from this x-scale the show up animation will start. + * @param showUpStartYScale from this y-scale the show up animation will start. + * @param showUpDuration the duration of the show up animation in milliseconds. + * @param dismissEndXScale to this x-scale the dismiss animation will end. + * @param dismissEndYScale to this y-scale the dismiss animation will end. + * @param dismissDuration the duration of the dismiss animation in milliseconds. + */ + public void setKeyPreviewAnimationParams(final boolean hasCustomAnimationParams, + final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration, + final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) { + mKeyPreviewDrawParams.setAnimationParams(hasCustomAnimationParams, + showUpStartXScale, showUpStartYScale, showUpDuration, + dismissEndXScale, dismissEndYScale, dismissDuration); } private void locatePreviewPlacerView() { @@ -565,7 +589,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack mMoreKeysKeyboardCache.put(key, moreKeysKeyboard); } - final View container = mMoreKeysKeyboardContainer; + final View container = key.isActionKey() ? mMoreKeysKeyboardForActionContainer + : mMoreKeysKeyboardContainer; final MoreKeysKeyboardView moreKeysKeyboardView = (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); moreKeysKeyboardView.setKeyboard(moreKeysKeyboard); @@ -731,7 +756,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public void onHideWindow() { onDismissMoreKeysPanel(); final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isAccessibilityEnabled()) { accessibilityDelegate.onHideWindow(); } } @@ -742,7 +768,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public boolean onHoverEvent(final MotionEvent event) { final MainKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { return accessibilityDelegate.onHoverEvent(event); } return super.onHoverEvent(event); @@ -764,6 +791,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged, final int languageOnSpacebarFormatType, final boolean hasMultipleEnabledIMEsOrSubtypes) { + if (subtypeChanged) { + KeyPreviewView.clearTextCache(); + } mLanguageOnSpacebarFormatType = languageOnSpacebarFormatType; mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes; final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator; diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index e0184d7f1..abcfff8a6 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -18,11 +18,9 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.graphics.Paint; -import android.graphics.drawable.Drawable; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; -import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.R; @@ -43,7 +41,7 @@ public final class MoreKeysKeyboard extends Keyboard { @UsedForTesting static class MoreKeysKeyboardParams extends KeyboardParams { - public boolean mIsFixedOrder; + public boolean mIsMoreKeysFixedOrder; /* package */int mTopRowAdjustment; public int mNumRows; public int mNumColumns; @@ -61,29 +59,35 @@ public final class MoreKeysKeyboard extends Keyboard { * Set keyboard parameters of more keys keyboard. * * @param numKeys number of keys in this more keys keyboard. - * @param maxColumns number of maximum columns of this more keys keyboard. + * @param numColumn number of columns of this more keys keyboard. * @param keyWidth more keys keyboard key width in pixel, including horizontal gap. * @param rowHeight more keys keyboard row height in pixel, including vertical gap. * @param coordXInParent coordinate x of the key preview in parent keyboard. * @param parentKeyboardWidth parent keyboard width in pixel. - * @param isFixedColumnOrder if true, more keys should be laid out in fixed order. + * @param isMoreKeysFixedColumn true if more keys keyboard should have + * <code>numColumn</code> columns. Otherwise more keys keyboard should have + * <code>numColumn</code> columns at most. + * @param isMoreKeysFixedOrder true if the order of more keys is determined by the order in + * the more keys' specification. Otherwise the order of more keys is automatically + * determined. * @param dividerWidth width of divider, zero for no dividers. */ - public void setParameters(final int numKeys, final int maxColumns, final int keyWidth, + public void setParameters(final int numKeys, final int numColumn, final int keyWidth, final int rowHeight, final int coordXInParent, final int parentKeyboardWidth, - final boolean isFixedColumnOrder, final int dividerWidth) { - mIsFixedOrder = isFixedColumnOrder; - if (parentKeyboardWidth / keyWidth < Math.min(numKeys, maxColumns)) { + final boolean isMoreKeysFixedColumn, final boolean isMoreKeysFixedOrder, + final int dividerWidth) { + mIsMoreKeysFixedOrder = isMoreKeysFixedOrder; + if (parentKeyboardWidth / keyWidth < Math.min(numKeys, numColumn)) { throw new IllegalArgumentException("Keyboard is too small to hold more keys: " - + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + maxColumns); + + parentKeyboardWidth + " " + keyWidth + " " + numKeys + " " + numColumn); } mDefaultKeyWidth = keyWidth; mDefaultRowHeight = rowHeight; - final int numRows = (numKeys + maxColumns - 1) / maxColumns; + final int numRows = (numKeys + numColumn - 1) / numColumn; mNumRows = numRows; - final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns) - : getOptimizedColumns(numKeys, maxColumns); + final int numColumns = isMoreKeysFixedColumn ? Math.min(numKeys, numColumn) + : getOptimizedColumns(numKeys, numColumn); mNumColumns = numColumns; final int topKeys = numKeys % numColumns; mTopKeys = topKeys == 0 ? numColumns : topKeys; @@ -120,7 +124,7 @@ public final class MoreKeysKeyboard extends Keyboard { mRightKeys = rightKeys; // Adjustment of the top row. - mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment() + mTopRowAdjustment = isMoreKeysFixedOrder ? getFixedOrderTopRowAdjustment() : getAutoOrderTopRowAdjustment(); mDividerWidth = dividerWidth; mColumnWidth = mDefaultKeyWidth + mDividerWidth; @@ -148,7 +152,7 @@ public final class MoreKeysKeyboard extends Keyboard { // Return key position according to column count (0 is default). /* package */int getColumnPos(final int n) { - return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n); + return mIsMoreKeysFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n); } private int getFixedOrderColumnPos(final int n) { @@ -251,7 +255,6 @@ public final class MoreKeysKeyboard extends Keyboard { public static class Builder extends KeyboardBuilder<MoreKeysKeyboardParams> { private final Key mParentKey; - private final Drawable mDivider; private static final float LABEL_PADDING_RATIO = 0.2f; private static final float DIVIDER_RATIO = 0.2f; @@ -263,7 +266,8 @@ public final class MoreKeysKeyboard extends Keyboard { * @param keyboard the {@link Keyboard} that contains the parentKey. * @param isSingleMoreKeyWithPreview true if the <code>key</code> has just a single * "more key" and its key popup preview is enabled. - * @param keyPreviewDrawParams the parameter to place key preview. + * @param keyPreviewVisibleWidth the width of visible part of key popup preview. + * @param keyPreviewVisibleHeight the height of visible part of key popup preview * @param paintToMeasure the {@link Paint} object to measure a "more key" width */ public Builder(final Context context, final Key key, final Keyboard keyboard, @@ -299,16 +303,14 @@ public final class MoreKeysKeyboard extends Keyboard { } final int dividerWidth; if (key.needsDividersInMoreKeys()) { - mDivider = mResources.getDrawable(R.drawable.more_keys_divider); dividerWidth = (int)(keyWidth * DIVIDER_RATIO); } else { - mDivider = null; dividerWidth = 0; } final MoreKeySpec[] moreKeys = key.getMoreKeys(); - mParams.setParameters(moreKeys.length, key.getMoreKeysColumn(), keyWidth, rowHeight, - key.getX() + key.getWidth() / 2, keyboard.mId.mWidth, - key.isFixedColumnOrderMoreKeys(), dividerWidth); + mParams.setParameters(moreKeys.length, key.getMoreKeysColumnNumber(), keyWidth, + rowHeight, key.getX() + key.getWidth() / 2, keyboard.mId.mWidth, + key.isMoreKeysFixedColumn(), key.isMoreKeysFixedOrder(), dividerWidth); } private static int getMaxKeyWidth(final Key parentKey, final int minKeyWidth, @@ -345,7 +347,8 @@ public final class MoreKeysKeyboard extends Keyboard { if (params.mDividerWidth > 0 && pos != 0) { final int dividerX = (pos > 0) ? x - params.mDividerWidth : x + params.mDefaultKeyWidth; - final Key divider = new MoreKeyDivider(params, mDivider, dividerX, y); + final Key divider = new MoreKeyDivider( + params, dividerX, y, params.mDividerWidth, params.mDefaultRowHeight); params.onAddKey(divider); } } @@ -353,22 +356,11 @@ public final class MoreKeysKeyboard extends Keyboard { } } - private static class MoreKeyDivider extends Key.Spacer { - private final Drawable mIcon; - - public MoreKeyDivider(final MoreKeysKeyboardParams params, final Drawable icon, - final int x, final int y) { - super(params, x, y, params.mDividerWidth, params.mDefaultRowHeight); - mIcon = icon; - } - - @Override - public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) { - // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the - // constructor. - // TODO: Drawable itself should have an alpha value. - mIcon.setAlpha(128); - return mIcon; + // Used as a divider maker. A divider is drawn by {@link MoreKeysKeyboardView}. + public static class MoreKeyDivider extends Key.Spacer { + public MoreKeyDivider(final KeyboardParams params, final int x, final int y, + final int width, final int height) { + super(params, x, y, width, height); } } } diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 5140c4ffc..841283b7f 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -17,6 +17,10 @@ package com.android.inputmethod.keyboard; import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -24,6 +28,7 @@ import android.view.ViewGroup; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate; +import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -35,6 +40,7 @@ import com.android.inputmethod.latin.utils.CoordinateUtils; public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel { private final int[] mCoordinates = CoordinateUtils.newInstance(); + private final Drawable mDivider; protected final KeyDetector mKeyDetector; private Controller mController = EMPTY_CONTROLLER; protected KeyboardActionListener mListener; @@ -53,6 +59,14 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel public MoreKeysKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) { super(context, attrs, defStyle); + final TypedArray moreKeysKeyboardViewAttr = context.obtainStyledAttributes(attrs, + R.styleable.MoreKeysKeyboardView, defStyle, R.style.MoreKeysKeyboardView); + mDivider = moreKeysKeyboardViewAttr.getDrawable(R.styleable.MoreKeysKeyboardView_divider); + if (mDivider != null) { + // TODO: Drawable itself should have an alpha value. + mDivider.setAlpha(128); + } + moreKeysKeyboardViewAttr.recycle(); mKeyDetector = new MoreKeysDetector(getResources().getDimension( R.dimen.config_more_keys_keyboard_slide_allowance)); } @@ -70,11 +84,28 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel } @Override + protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, + final KeyDrawParams params) { + if (!key.isSpacer() || !(key instanceof MoreKeysKeyboard.MoreKeyDivider) + || mDivider == null) { + super.onDrawKeyTopVisuals(key, canvas, paint, params); + return; + } + final int keyWidth = key.getDrawWidth(); + final int keyHeight = key.getHeight(); + final int iconWidth = Math.min(mDivider.getIntrinsicWidth(), keyWidth); + final int iconHeight = mDivider.getIntrinsicHeight(); + final int iconX = (keyWidth - iconWidth) / 2; // Align horizontally center + final int iconY = (keyHeight - iconHeight) / 2; // Align vertically center + drawIcon(canvas, mDivider, iconX, iconY, iconWidth, iconHeight); + } + + @Override public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); mKeyDetector.setKeyboard( keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); - if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { + if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { if (mAccessibilityDelegate == null) { mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate( this, mKeyDetector); @@ -111,7 +142,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel mOriginY = y + container.getPaddingTop(); controller.onShowMoreKeysPanel(this); final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isAccessibilityEnabled()) { accessibilityDelegate.onShowMoreKeysKeyboard(); } } @@ -208,7 +240,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel return; } final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isAccessibilityEnabled()) { accessibilityDelegate.onDismissMoreKeysKeyboard(); } mController.onDismissMoreKeysPanel(); @@ -254,7 +287,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel @Override public boolean onHoverEvent(final MotionEvent event) { final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { return accessibilityDelegate.onHoverEvent(event); } return super.onHoverEvent(event); diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java new file mode 100644 index 000000000..c22717f95 --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -0,0 +1,368 @@ +/* + * 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.graphics.Matrix; +import android.graphics.RectF; +import android.inputmethodservice.InputMethodService; +import android.os.Message; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.CursorAnchorInfo; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; +import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; + +import javax.annotation.Nonnull; + +/** + * A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class + * is designed to be independent of UI subsystems such as {@link View}. All the UI related + * operations are delegated to {@link TextDecoratorUi} via {@link TextDecoratorUiOperator}. + */ +public class TextDecorator { + private static final String TAG = TextDecorator.class.getSimpleName(); + private static final boolean DEBUG = false; + + private static final int INVALID_CURSOR_INDEX = -1; + + private static final int MODE_MONITOR = 0; + private static final int MODE_WAITING_CURSOR_INDEX = 1; + private static final int MODE_SHOWING_INDICATOR = 2; + + private int mMode = MODE_MONITOR; + + private String mLastComposingText = null; + private boolean mHasRtlCharsInLastComposingText = false; + private RectF mComposingTextBoundsForLastComposingText = new RectF(); + + private boolean mIsFullScreenMode = false; + private String mWaitingWord = null; + private int mWaitingCursorStart = INVALID_CURSOR_INDEX; + private int mWaitingCursorEnd = INVALID_CURSOR_INDEX; + private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null; + + @Nonnull + private final Listener mListener; + + @Nonnull + private TextDecoratorUiOperator mUiOperator = EMPTY_UI_OPERATOR; + + public interface Listener { + /** + * Called when the user clicks the indicator to add the word into the dictionary. + * @param word the word which the user clicked on. + */ + void onClickComposingTextToAddToDictionary(final String word); + } + + public TextDecorator(final Listener listener) { + mListener = (listener != null) ? listener : EMPTY_LISTENER; + } + + /** + * Sets the UI operator for {@link TextDecorator}. Any user visible operations will be + * delegated to the associated UI operator. + * @param uiOperator the UI operator to be associated. + */ + public void setUiOperator(final TextDecoratorUiOperator uiOperator) { + mUiOperator.disposeUi(); + mUiOperator = uiOperator; + mUiOperator.setOnClickListener(getOnClickHandler()); + } + + private final Runnable mDefaultOnClickHandler = new Runnable() { + @Override + public void run() { + onClickIndicator(); + } + }; + + @UsedForTesting + final Runnable getOnClickHandler() { + return mDefaultOnClickHandler; + } + + /** + * Shows the "Add to dictionary" indicator and associates it with associating the given word. + * + * @param word the word which should be associated with the indicator. This object will be + * passed back in {@link Listener#onClickComposingTextToAddToDictionary(String)}. + * @param selectionStart the cursor index (inclusive) when the indicator should be displayed. + * @param selectionEnd the cursor index (exclusive) when the indicator should be displayed. + */ + public void showAddToDictionaryIndicator(final String word, final int selectionStart, + final int selectionEnd) { + mWaitingWord = word; + mWaitingCursorStart = selectionStart; + mWaitingCursorEnd = selectionEnd; + mMode = MODE_WAITING_CURSOR_INDEX; + layoutLater(); + return; + } + + /** + * Must be called when the input method is about changing to for from the full screen mode. + * @param fullScreenMode {@code true} if the input method is entering the full screen mode. + * {@code false} is the input method is finishing the full screen mode. + */ + public void notifyFullScreenMode(final boolean fullScreenMode) { + final boolean fullScreenModeChanged = (mIsFullScreenMode != fullScreenMode); + mIsFullScreenMode = fullScreenMode; + if (fullScreenModeChanged) { + layoutLater(); + } + } + + /** + * Resets previous requests and makes indicator invisible. + */ + public void reset() { + mWaitingWord = null; + mMode = MODE_MONITOR; + mWaitingCursorStart = INVALID_CURSOR_INDEX; + mWaitingCursorEnd = INVALID_CURSOR_INDEX; + cancelLayoutInternalExpectedly("Resetting internal state."); + } + + /** + * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} + * is called. + * + * <p>CAVEAT: Currently the input method author is responsible for ignoring + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} called in full screen + * mode.</p> + * @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}. + */ + public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) { + mCursorAnchorInfoWrapper = info; + // Do not use layoutLater() to minimize the latency. + layoutImmediately(); + } + + private void cancelLayoutInternalUnexpectedly(final String message) { + mUiOperator.hideUi(); + Log.d(TAG, message); + } + + private void cancelLayoutInternalExpectedly(final String message) { + mUiOperator.hideUi(); + if (DEBUG) { + Log.d(TAG, message); + } + } + + private void layoutLater() { + mLayoutInvalidator.invalidateLayout(); + } + + + private void layoutImmediately() { + // Clear pending layout requests. + mLayoutInvalidator.cancelInvalidateLayout(); + layoutMain(); + } + + private void layoutMain() { + final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper; + + if (info == null || !info.isAvailable()) { + cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available."); + return; + } + + final Matrix matrix = info.getMatrix(); + if (matrix == null) { + cancelLayoutInternalUnexpectedly("Matrix is null"); + } + + final CharSequence composingText = info.getComposingText(); + if (!TextUtils.isEmpty(composingText)) { + final int composingTextStart = info.getComposingTextStart(); + final int lastCharRectIndex = composingTextStart + composingText.length() - 1; + final RectF lastCharRect = info.getCharacterBounds(lastCharRectIndex); + final int lastCharRectFlags = info.getCharacterBoundsFlags(lastCharRectIndex); + final boolean hasInvisibleRegionInLastCharRect = + (lastCharRectFlags & CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) + != 0; + if (lastCharRect == null || matrix == null || hasInvisibleRegionInLastCharRect) { + mUiOperator.hideUi(); + return; + } + + // Note that the following layout information is fragile, and must be invalidated + // even when surrounding text next to the composing text is changed because it can + // affect how the composing text is rendered. + // TODO: Investigate if we can change the input logic to make the target text + // composing state so that we can retrieve the character bounds reliably. + final String composingTextString = composingText.toString(); + final float top = lastCharRect.top; + final float bottom = lastCharRect.bottom; + float left = lastCharRect.left; + float right = lastCharRect.right; + boolean useRtlLayout = false; + for (int i = composingText.length() - 1; i >= 0; --i) { + final int characterIndex = composingTextStart + i; + final RectF characterBounds = info.getCharacterBounds(characterIndex); + final int characterBoundsFlags = info.getCharacterBoundsFlags(characterIndex); + if (characterBounds == null) { + break; + } + if (characterBounds.top != top) { + break; + } + if (characterBounds.bottom != bottom) { + break; + } + if ((characterBoundsFlags & CursorAnchorInfoCompatWrapper.FLAG_IS_RTL) != 0) { + // This is for both RTL text and bi-directional text. RTL languages usually mix + // RTL characters with LTR characters and in this case we should display the + // indicator on the left, while in LTR languages that normally never happens. + // TODO: Try to come up with a better algorithm. + useRtlLayout = true; + } + left = Math.min(characterBounds.left, left); + right = Math.max(characterBounds.right, right); + } + mLastComposingText = composingTextString; + mHasRtlCharsInLastComposingText = useRtlLayout; + mComposingTextBoundsForLastComposingText.set(left, top, right, bottom); + } + + final int selectionStart = info.getSelectionStart(); + final int selectionEnd = info.getSelectionEnd(); + switch (mMode) { + case MODE_MONITOR: + mUiOperator.hideUi(); + return; + case MODE_WAITING_CURSOR_INDEX: + if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) { + mUiOperator.hideUi(); + return; + } + mMode = MODE_SHOWING_INDICATOR; + break; + case MODE_SHOWING_INDICATOR: + if (selectionStart != mWaitingCursorStart || selectionEnd != mWaitingCursorEnd) { + mUiOperator.hideUi(); + mMode = MODE_MONITOR; + mWaitingCursorStart = INVALID_CURSOR_INDEX; + mWaitingCursorEnd = INVALID_CURSOR_INDEX; + return; + } + break; + default: + cancelLayoutInternalUnexpectedly("Unexpected internal mode=" + mMode); + return; + } + + if (!TextUtils.equals(mLastComposingText, mWaitingWord)) { + cancelLayoutInternalUnexpectedly("mLastComposingText doesn't match mWaitingWord"); + return; + } + + if ((info.getInsertionMarkerFlags() & + CursorAnchorInfoCompatWrapper.FLAG_HAS_INVISIBLE_REGION) != 0) { + mUiOperator.hideUi(); + return; + } + + mUiOperator.layoutUi(matrix, mComposingTextBoundsForLastComposingText, + mHasRtlCharsInLastComposingText); + } + + private void onClickIndicator() { + if (mMode != MODE_SHOWING_INDICATOR) { + return; + } + mListener.onClickComposingTextToAddToDictionary(mWaitingWord); + } + + private final LayoutInvalidator mLayoutInvalidator = new LayoutInvalidator(this); + + /** + * Used for managing pending layout tasks for {@link TextDecorator#layoutLater()}. + */ + private static final class LayoutInvalidator { + private final HandlerImpl mHandler; + public LayoutInvalidator(final TextDecorator ownerInstance) { + mHandler = new HandlerImpl(ownerInstance); + } + + private static final int MSG_LAYOUT = 0; + + private static final class HandlerImpl + extends LeakGuardHandlerWrapper<TextDecorator> { + public HandlerImpl(final TextDecorator ownerInstance) { + super(ownerInstance); + } + + @Override + public void handleMessage(final Message msg) { + final TextDecorator owner = getOwnerInstance(); + if (owner == null) { + return; + } + switch (msg.what) { + case MSG_LAYOUT: + owner.layoutMain(); + break; + } + } + } + + /** + * Puts a layout task into the scheduler. Does nothing if one or more layout tasks are + * already scheduled. + */ + public void invalidateLayout() { + if (!mHandler.hasMessages(MSG_LAYOUT)) { + mHandler.obtainMessage(MSG_LAYOUT).sendToTarget(); + } + } + + /** + * Clears the pending layout tasks. + */ + public void cancelInvalidateLayout() { + mHandler.removeMessages(MSG_LAYOUT); + } + } + + private final static Listener EMPTY_LISTENER = new Listener() { + @Override + public void onClickComposingTextToAddToDictionary(final String word) { + } + }; + + private final static TextDecoratorUiOperator EMPTY_UI_OPERATOR = new TextDecoratorUiOperator() { + @Override + public void disposeUi() { + } + @Override + public void hideUi() { + } + @Override + public void setOnClickListener(Runnable listener) { + } + @Override + public void layoutUi(Matrix matrix, RectF composingTextBounds, boolean useRtlLayout) { + } + }; +} diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java new file mode 100644 index 000000000..d87dc1bfa --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java @@ -0,0 +1,262 @@ +/* + * 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.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.ColorDrawable; +import android.inputmethodservice.InputMethodService; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.ViewParent; +import android.widget.PopupWindow; +import android.widget.RelativeLayout; + +import com.android.inputmethod.latin.R; + +/** + * Used as the UI component of {@link TextDecorator}. + */ +public final class TextDecoratorUi implements TextDecoratorUiOperator { + private static final boolean VISUAL_DEBUG = false; + private static final int VISUAL_DEBUG_HIT_AREA_COLOR = 0x80ff8000; + + private final RelativeLayout mLocalRootView; + private final AddToDictionaryIndicatorView mAddToDictionaryIndicatorView; + private final PopupWindow mTouchEventWindow; + private final View mTouchEventWindowClickListenerView; + private final float mHitAreaMarginInPixels; + private final RectF mDisplayRect; + + /** + * This constructor is designed to be called from {@link InputMethodService#setInputView(View)}. + * Other usages are not supported. + * + * @param context the context of the input method. + * @param inputView the view that is passed to {@link InputMethodService#setInputView(View)}. + */ + public TextDecoratorUi(final Context context, final View inputView) { + final Resources resources = context.getResources(); + final int hitAreaMarginInDP = resources.getInteger( + R.integer.text_decorator_hit_area_margin_in_dp); + mHitAreaMarginInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + hitAreaMarginInDP, resources.getDisplayMetrics()); + final DisplayMetrics displayMetrics = resources.getDisplayMetrics(); + mDisplayRect = new RectF(0.0f, 0.0f, displayMetrics.widthPixels, + displayMetrics.heightPixels); + + mLocalRootView = new RelativeLayout(context); + mLocalRootView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT)); + // TODO: Use #setBackground(null) for API Level >= 16. + mLocalRootView.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + + final ViewGroup contentView = getContentView(inputView); + mAddToDictionaryIndicatorView = new AddToDictionaryIndicatorView(context); + mLocalRootView.addView(mAddToDictionaryIndicatorView); + if (contentView != null) { + contentView.addView(mLocalRootView); + } + + // This popup window is used to avoid the limitation that the input method is not able to + // observe the touch events happening outside of InputMethodService.Insets#touchableRegion. + // We don't use this popup window for rendering the UI for performance reasons though. + mTouchEventWindow = new PopupWindow(context); + if (VISUAL_DEBUG) { + mTouchEventWindow.setBackgroundDrawable(new ColorDrawable(VISUAL_DEBUG_HIT_AREA_COLOR)); + } else { + mTouchEventWindow.setBackgroundDrawable(null); + } + mTouchEventWindowClickListenerView = new View(context); + mTouchEventWindow.setContentView(mTouchEventWindowClickListenerView); + } + + @Override + public void disposeUi() { + if (mLocalRootView != null) { + final ViewParent parent = mLocalRootView.getParent(); + if (parent != null && parent instanceof ViewGroup) { + ((ViewGroup) parent).removeView(mLocalRootView); + } + mLocalRootView.removeAllViews(); + } + if (mTouchEventWindow != null) { + mTouchEventWindow.dismiss(); + } + } + + @Override + public void hideUi() { + mAddToDictionaryIndicatorView.setVisibility(View.GONE); + mTouchEventWindow.dismiss(); + } + + private static final RectF getIndicatorBoundsInScreenCoordinates(final Matrix matrix, + final RectF composingTextBounds, final boolean showAtLeftSide) { + final float indicatorSize = composingTextBounds.height(); + final RectF indicatorBounds; + if (showAtLeftSide) { + indicatorBounds = new RectF(composingTextBounds.left - indicatorSize, + composingTextBounds.top, composingTextBounds.left, + composingTextBounds.top + indicatorSize); + } else { + indicatorBounds = new RectF(composingTextBounds.right, composingTextBounds.top, + composingTextBounds.right + indicatorSize, + composingTextBounds.top + indicatorSize); + } + matrix.mapRect(indicatorBounds); + return indicatorBounds; + } + + @Override + public void layoutUi(final Matrix matrix, final RectF composingTextBounds, + final boolean useRtlLayout) { + RectF indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix, + composingTextBounds, useRtlLayout /* showAtLeftSide */); + if (indicatorBoundsInScreenCoordinates.left < mDisplayRect.left || + mDisplayRect.right < indicatorBoundsInScreenCoordinates.right) { + // The indicator is clipped by the screen. Show the indicator at the opposite side. + indicatorBoundsInScreenCoordinates = getIndicatorBoundsInScreenCoordinates(matrix, + composingTextBounds, !useRtlLayout /* showAtLeftSide */); + } + + mAddToDictionaryIndicatorView.setBounds(indicatorBoundsInScreenCoordinates); + + final RectF hitAreaBoundsInScreenCoordinates = new RectF(); + matrix.mapRect(hitAreaBoundsInScreenCoordinates, composingTextBounds); + hitAreaBoundsInScreenCoordinates.union(indicatorBoundsInScreenCoordinates); + hitAreaBoundsInScreenCoordinates.inset(-mHitAreaMarginInPixels, -mHitAreaMarginInPixels); + + final int[] originScreen = new int[2]; + mLocalRootView.getLocationOnScreen(originScreen); + final int viewOriginX = originScreen[0]; + final int viewOriginY = originScreen[1]; + mAddToDictionaryIndicatorView.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX); + mAddToDictionaryIndicatorView.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY); + mAddToDictionaryIndicatorView.setVisibility(View.VISIBLE); + + if (mTouchEventWindow.isShowing()) { + mTouchEventWindow.update((int)hitAreaBoundsInScreenCoordinates.left - viewOriginX, + (int)hitAreaBoundsInScreenCoordinates.top - viewOriginY, + (int)hitAreaBoundsInScreenCoordinates.width(), + (int)hitAreaBoundsInScreenCoordinates.height()); + } else { + mTouchEventWindow.setWidth((int)hitAreaBoundsInScreenCoordinates.width()); + mTouchEventWindow.setHeight((int)hitAreaBoundsInScreenCoordinates.height()); + mTouchEventWindow.showAtLocation(mLocalRootView, Gravity.NO_GRAVITY, + (int)hitAreaBoundsInScreenCoordinates.left - viewOriginX, + (int)hitAreaBoundsInScreenCoordinates.top - viewOriginY); + } + } + + @Override + public void setOnClickListener(final Runnable listener) { + mTouchEventWindowClickListenerView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(final View arg0) { + listener.run(); + } + }); + } + + private static class IndicatorView extends View { + private final Path mPath; + private final Path mTmpPath = new Path(); + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Matrix mMatrix = new Matrix(); + private final int mBackgroundColor; + private final int mForegroundColor; + private final RectF mBounds = new RectF(); + public IndicatorView(Context context, final int pathResourceId, + final int sizeResourceId, final int backgroundColorResourceId, + final int foregroundColroResourceId) { + super(context); + final Resources resources = context.getResources(); + mPath = createPath(resources, pathResourceId, sizeResourceId); + mBackgroundColor = resources.getColor(backgroundColorResourceId); + mForegroundColor = resources.getColor(foregroundColroResourceId); + } + + public void setBounds(final RectF rect) { + mBounds.set(rect); + } + + @Override + protected void onDraw(Canvas canvas) { + mPaint.setColor(mBackgroundColor); + mPaint.setStyle(Paint.Style.FILL); + canvas.drawRect(0.0f, 0.0f, mBounds.width(), mBounds.height(), mPaint); + + mMatrix.reset(); + mMatrix.postScale(mBounds.width(), mBounds.height()); + mPath.transform(mMatrix, mTmpPath); + mPaint.setColor(mForegroundColor); + canvas.drawPath(mTmpPath, mPaint); + } + + private static Path createPath(final Resources resources, final int pathResourceId, + final int sizeResourceId) { + final int size = resources.getInteger(sizeResourceId); + final float normalizationFactor = 1.0f / size; + final int[] array = resources.getIntArray(pathResourceId); + + final Path path = new Path(); + for (int i = 0; i < array.length; i += 2) { + if (i == 0) { + path.moveTo(array[i] * normalizationFactor, array[i + 1] * normalizationFactor); + } else { + path.lineTo(array[i] * normalizationFactor, array[i + 1] * normalizationFactor); + } + } + path.close(); + return path; + } + } + + private static ViewGroup getContentView(final View view) { + final View rootView = view.getRootView(); + if (rootView == null) { + return null; + } + + final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content); + if (windowContentView == null) { + return null; + } + return windowContentView; + } + + private static final class AddToDictionaryIndicatorView extends TextDecoratorUi.IndicatorView { + public AddToDictionaryIndicatorView(final Context context) { + super(context, R.array.text_decorator_add_to_dictionary_indicator_path, + R.integer.text_decorator_add_to_dictionary_indicator_path_size, + R.color.text_decorator_add_to_dictionary_indicator_background_color, + R.color.text_decorator_add_to_dictionary_indicator_foreground_color); + } + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java new file mode 100644 index 000000000..9e30e417e --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java @@ -0,0 +1,51 @@ +/* + * 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.graphics.Matrix; +import android.graphics.RectF; + +/** + * This interface defines how UI operations required for {@link TextDecorator} are delegated to + * the actual UI implementation class. + */ +public interface TextDecoratorUiOperator { + /** + * Called to notify that the UI is ready to be disposed. + */ + void disposeUi(); + + /** + * Called when the UI should become invisible. + */ + void hideUi(); + + /** + * Called to set the new click handler. + * @param onClickListener the callback object whose {@link Runnable#run()} should be called when + * the indicator is clicked. + */ + void setOnClickListener(final Runnable onClickListener); + + /** + * Called when the layout should be updated. + * @param matrix The matrix that transforms the local coordinates into the screen coordinates. + * @param composingTextBounds The bounding box of the composing text, in local coordinates. + * @param useRtlLayout {@code true} if the indicator should be optimized for RTL layout. + */ + void layoutUi(final Matrix matrix, final RectF composingTextBounds, final boolean useRtlLayout); +} diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java index 512d4615d..0f9dc855b 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -24,6 +24,7 @@ import android.os.Build; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; @@ -121,9 +122,7 @@ final class EmojiCategory { sCategoryTabIconAttr[i], 0); } addShownCategoryId(EmojiCategory.ID_RECENTS); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KeyLimePie") - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KitKat")) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.KITKAT) { addShownCategoryId(EmojiCategory.ID_PEOPLE); addShownCategoryId(EmojiCategory.ID_OBJECTS); addShownCategoryId(EmojiCategory.ID_NATURE); diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java index 17dfc9cce..925ec6bfb 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java @@ -104,7 +104,8 @@ final class EmojiPageKeyboardView extends KeyboardView implements public boolean onHoverEvent(final MotionEvent event) { final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate = mAccessibilityDelegate; - if (accessibilityDelegate != null) { + if (accessibilityDelegate != null + && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { return accessibilityDelegate.onHoverEvent(event); } return super.onHoverEvent(event); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java index 07ac06bab..df50efdc1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java @@ -26,7 +26,6 @@ public final class KeyDrawParams { public int mLetterSize; public int mLabelSize; public int mLargeLetterSize; - public int mLargeLabelSize; public int mHintLetterSize; public int mShiftedLetterHintSize; public int mHintLabelSize; @@ -42,6 +41,10 @@ public final class KeyDrawParams { public int mShiftedLetterHintActivatedColor; public int mPreviewTextColor; + public float mHintLabelVerticalAdjustment; + public float mLabelOffCenterRatio; + public float mHintLabelOffCenterRatio; + public int mAnimAlpha; public KeyDrawParams() {} @@ -52,7 +55,6 @@ public final class KeyDrawParams { mLetterSize = copyFrom.mLetterSize; mLabelSize = copyFrom.mLabelSize; mLargeLetterSize = copyFrom.mLargeLetterSize; - mLargeLabelSize = copyFrom.mLargeLabelSize; mHintLetterSize = copyFrom.mHintLetterSize; mShiftedLetterHintSize = copyFrom.mShiftedLetterHintSize; mHintLabelSize = copyFrom.mHintLabelSize; @@ -68,6 +70,10 @@ public final class KeyDrawParams { mShiftedLetterHintActivatedColor = copyFrom.mShiftedLetterHintActivatedColor; mPreviewTextColor = copyFrom.mPreviewTextColor; + mHintLabelVerticalAdjustment = copyFrom.mHintLabelVerticalAdjustment; + mLabelOffCenterRatio = copyFrom.mLabelOffCenterRatio; + mHintLabelOffCenterRatio = copyFrom.mHintLabelOffCenterRatio; + mAnimAlpha = copyFrom.mAnimAlpha; } @@ -84,7 +90,6 @@ public final class KeyDrawParams { attr.mLetterSize, attr.mLetterRatio, mLetterSize); mLabelSize = selectTextSizeFromDimensionOrRatio(keyHeight, attr.mLabelSize, attr.mLabelRatio, mLabelSize); - mLargeLabelSize = selectTextSize(keyHeight, attr.mLargeLabelRatio, mLargeLabelSize); mLargeLetterSize = selectTextSize(keyHeight, attr.mLargeLetterRatio, mLargeLetterSize); mHintLetterSize = selectTextSize(keyHeight, attr.mHintLetterRatio, mHintLetterSize); mShiftedLetterHintSize = selectTextSize(keyHeight, @@ -103,6 +108,13 @@ public final class KeyDrawParams { mShiftedLetterHintActivatedColor = selectColor( attr.mShiftedLetterHintActivatedColor, mShiftedLetterHintActivatedColor); mPreviewTextColor = selectColor(attr.mPreviewTextColor, mPreviewTextColor); + + mHintLabelVerticalAdjustment = selectFloatIfNonZero( + attr.mHintLabelVerticalAdjustment, mHintLabelVerticalAdjustment); + mLabelOffCenterRatio = selectFloatIfNonZero( + attr.mLabelOffCenterRatio, mLabelOffCenterRatio); + mHintLabelOffCenterRatio = selectFloatIfNonZero( + attr.mHintLabelOffCenterRatio, mHintLabelOffCenterRatio); } public KeyDrawParams mayCloneAndUpdateParams(final int keyHeight, @@ -115,7 +127,7 @@ public final class KeyDrawParams { return newParams; } - private static final int selectTextSizeFromDimensionOrRatio(final int keyHeight, + private static int selectTextSizeFromDimensionOrRatio(final int keyHeight, final int dimens, final float ratio, final int defaultDimens) { if (ResourceUtils.isValidDimensionPixelSize(dimens)) { return dimens; @@ -126,7 +138,7 @@ public final class KeyDrawParams { return defaultDimens; } - private static final int selectTextSize(final int keyHeight, final float ratio, + private static int selectTextSize(final int keyHeight, final float ratio, final int defaultSize) { if (ResourceUtils.isValidFraction(ratio)) { return (int)(keyHeight * ratio); @@ -134,10 +146,17 @@ public final class KeyDrawParams { return defaultSize; } - private static final int selectColor(final int attrColor, final int defaultColor) { + private static int selectColor(final int attrColor, final int defaultColor) { if (attrColor != 0) { return attrColor; } return defaultColor; } + + private static float selectFloatIfNonZero(final float attrFloat, final float defaultFloat) { + if (attrFloat != 0) { + return attrFloat; + } + return defaultFloat; + } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java index cd29c8d17..5005b7d7d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java @@ -18,13 +18,9 @@ package com.android.inputmethod.keyboard.internal; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.content.Context; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.utils.CoordinateUtils; @@ -89,9 +85,9 @@ public final class KeyPreviewChoreographer { } final Object tag = keyPreviewView.getTag(); if (withAnimation) { - if (tag instanceof KeyPreviewAnimations) { - final KeyPreviewAnimations animation = (KeyPreviewAnimations)tag; - animation.startDismiss(); + if (tag instanceof KeyPreviewAnimators) { + final KeyPreviewAnimators animators = (KeyPreviewAnimators)tag; + animators.startDismiss(); return; } } @@ -161,87 +157,60 @@ public final class KeyPreviewChoreographer { } // Show preview with animation. - final Animator showUpAnimation = createShowUpAniation(key, keyPreviewView); - final Animator dismissAnimation = createDismissAnimation(key, keyPreviewView); - final KeyPreviewAnimations animation = new KeyPreviewAnimations( - showUpAnimation, dismissAnimation); - keyPreviewView.setTag(animation); - animation.startShowUp(); + final Animator showUpAnimator = createShowUpAnimator(key, keyPreviewView); + final Animator dismissAnimator = createDismissAnimator(key, keyPreviewView); + final KeyPreviewAnimators animators = new KeyPreviewAnimators( + showUpAnimator, dismissAnimator); + keyPreviewView.setTag(animators); + animators.startShowUp(); } - private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f; - private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = - new AccelerateInterpolator(); - private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = - new DecelerateInterpolator(); - - private Animator createShowUpAniation(final Key key, final KeyPreviewView keyPreviewView) { - // TODO: Optimization for no scale animation and no duration. - final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_X, mParams.getShowUpStartScale(), - KEY_PREVIEW_SHOW_UP_END_SCALE); - final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_Y, mParams.getShowUpStartScale(), - KEY_PREVIEW_SHOW_UP_END_SCALE); - final AnimatorSet showUpAnimation = new AnimatorSet(); - showUpAnimation.play(scaleXAnimation).with(scaleYAnimation); - showUpAnimation.setDuration(mParams.getShowUpDuration()); - showUpAnimation.setInterpolator(DECELERATE_INTERPOLATOR); - showUpAnimation.addListener(new AnimatorListenerAdapter() { + public Animator createShowUpAnimator(final Key key, final KeyPreviewView keyPreviewView) { + final Animator animator = mParams.createShowUpAnimator(keyPreviewView); + animator.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationStart(final Animator animation) { + public void onAnimationStart(final Animator animator) { showKeyPreview(key, keyPreviewView, false /* withAnimation */); } }); - return showUpAnimation; + return animator; } - private Animator createDismissAnimation(final Key key, final KeyPreviewView keyPreviewView) { - // TODO: Optimization for no scale animation and no duration. - final ObjectAnimator scaleXAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_X, mParams.getDismissEndScale()); - final ObjectAnimator scaleYAnimation = ObjectAnimator.ofFloat( - keyPreviewView, View.SCALE_Y, mParams.getDismissEndScale()); - final AnimatorSet dismissAnimation = new AnimatorSet(); - dismissAnimation.play(scaleXAnimation).with(scaleYAnimation); - final int dismissDuration = Math.min( - mParams.getDismissDuration(), mParams.getLingerTimeout()); - dismissAnimation.setDuration(dismissDuration); - dismissAnimation.setInterpolator(ACCELERATE_INTERPOLATOR); - dismissAnimation.addListener(new AnimatorListenerAdapter() { + private Animator createDismissAnimator(final Key key, final KeyPreviewView keyPreviewView) { + final Animator animator = mParams.createDismissAnimator(keyPreviewView); + animator.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationEnd(final Animator animation) { + public void onAnimationEnd(final Animator animator) { dismissKeyPreview(key, false /* withAnimation */); } }); - return dismissAnimation; + return animator; } - private static class KeyPreviewAnimations extends AnimatorListenerAdapter { - private final Animator mShowUpAnimation; - private final Animator mDismissAnimation; + private static class KeyPreviewAnimators extends AnimatorListenerAdapter { + private final Animator mShowUpAnimator; + private final Animator mDismissAnimator; - public KeyPreviewAnimations(final Animator showUpAnimation, - final Animator dismissAnimation) { - mShowUpAnimation = showUpAnimation; - mDismissAnimation = dismissAnimation; + public KeyPreviewAnimators(final Animator showUpAnimator, final Animator dismissAnimator) { + mShowUpAnimator = showUpAnimator; + mDismissAnimator = dismissAnimator; } public void startShowUp() { - mShowUpAnimation.start(); + mShowUpAnimator.start(); } public void startDismiss() { - if (mShowUpAnimation.isRunning()) { - mShowUpAnimation.addListener(this); + if (mShowUpAnimator.isRunning()) { + mShowUpAnimator.addListener(this); return; } - mDismissAnimation.start(); + mDismissAnimator.start(); } @Override - public void onAnimationEnd(final Animator animation) { - mDismissAnimation.start(); + public void onAnimationEnd(final Animator animator) { + mDismissAnimator.start(); } } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java index 68c9831fa..5ed39f986 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java @@ -16,8 +16,14 @@ package com.android.inputmethod.keyboard.internal; +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; import android.content.res.TypedArray; import android.view.View; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; import com.android.inputmethod.latin.R; @@ -26,10 +32,15 @@ public final class KeyPreviewDrawParams { public final int mPreviewOffset; public final int mPreviewHeight; public final int mPreviewBackgroundResId; + private final int mShowUpAnimatorResId; + private final int mDismissAnimatorResId; + private boolean mHasCustomAnimationParams; private int mShowUpDuration; private int mDismissDuration; - private float mShowUpStartScale; - private float mDismissEndScale; + private float mShowUpStartXScale; + private float mShowUpStartYScale; + private float mDismissEndXScale; + private float mDismissEndYScale; private int mLingerTimeout; private boolean mShowPopup = true; @@ -67,6 +78,10 @@ public final class KeyPreviewDrawParams { R.styleable.MainKeyboardView_keyPreviewBackground, 0); mLingerTimeout = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_keyPreviewLingerTimeout, 0); + mShowUpAnimatorResId = mainKeyboardViewAttr.getResourceId( + R.styleable.MainKeyboardView_keyPreviewShowUpAnimator, 0); + mDismissAnimatorResId = mainKeyboardViewAttr.getResourceId( + R.styleable.MainKeyboardView_keyPreviewDismissAnimator, 0); } public void setVisibleOffset(final int previewVisibleOffset) { @@ -112,27 +127,62 @@ public final class KeyPreviewDrawParams { return mLingerTimeout; } - public void setAnimationParams(final float showUpStartScale, final int showUpDuration, - final float dismissEndScale, final int dismissDuration) { - mShowUpStartScale = showUpStartScale; + public void setAnimationParams(final boolean hasCustomAnimationParams, + final float showUpStartXScale, final float showUpStartYScale, final int showUpDuration, + final float dismissEndXScale, final float dismissEndYScale, final int dismissDuration) { + mHasCustomAnimationParams = hasCustomAnimationParams; + mShowUpStartXScale = showUpStartXScale; + mShowUpStartYScale = showUpStartYScale; mShowUpDuration = showUpDuration; - mDismissEndScale = dismissEndScale; + mDismissEndXScale = dismissEndXScale; + mDismissEndYScale = dismissEndYScale; mDismissDuration = dismissDuration; } - public float getShowUpStartScale() { - return mShowUpStartScale; + private static final float KEY_PREVIEW_SHOW_UP_END_SCALE = 1.0f; + private static final AccelerateInterpolator ACCELERATE_INTERPOLATOR = + new AccelerateInterpolator(); + private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = + new DecelerateInterpolator(); + + public Animator createShowUpAnimator(final View target) { + if (mHasCustomAnimationParams) { + final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_X, mShowUpStartXScale, + KEY_PREVIEW_SHOW_UP_END_SCALE); + final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_Y, mShowUpStartYScale, + KEY_PREVIEW_SHOW_UP_END_SCALE); + final AnimatorSet showUpAnimator = new AnimatorSet(); + showUpAnimator.play(scaleXAnimator).with(scaleYAnimator); + showUpAnimator.setDuration(mShowUpDuration); + showUpAnimator.setInterpolator(DECELERATE_INTERPOLATOR); + return showUpAnimator; + } + final Animator animator = AnimatorInflater.loadAnimator( + target.getContext(), mShowUpAnimatorResId); + animator.setTarget(target); + animator.setInterpolator(DECELERATE_INTERPOLATOR); + return animator; } - public int getShowUpDuration() { - return mShowUpDuration; - } - - public float getDismissEndScale() { - return mDismissEndScale; - } - - public int getDismissDuration() { - return mDismissDuration; + public Animator createDismissAnimator(final View target) { + if (mHasCustomAnimationParams) { + final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_X, mDismissEndXScale); + final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat( + target, View.SCALE_Y, mDismissEndYScale); + final AnimatorSet dismissAnimator = new AnimatorSet(); + dismissAnimator.play(scaleXAnimator).with(scaleYAnimator); + final int dismissDuration = Math.min(mDismissDuration, mLingerTimeout); + dismissAnimator.setDuration(dismissDuration); + dismissAnimator.setInterpolator(ACCELERATE_INTERPOLATOR); + return dismissAnimator; + } + final Animator animator = AnimatorInflater.loadAnimator( + target.getContext(), mDismissAnimatorResId); + animator.setTarget(target); + animator.setInterpolator(ACCELERATE_INTERPOLATOR); + return animator; } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java index 360faf829..24538605a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java @@ -17,7 +17,10 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; +import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.text.TextPaint; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -26,6 +29,8 @@ import android.widget.TextView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.R; +import java.util.HashSet; + /** * The pop up key preview view. */ @@ -34,6 +39,9 @@ public class KeyPreviewView extends TextView { public static final int POSITION_LEFT = 1; public static final int POSITION_RIGHT = 2; + private final Rect mBackgroundPadding = new Rect(); + private static final HashSet<String> sNoScaleXTextSet = new HashSet<>(); + public KeyPreviewView(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } @@ -58,7 +66,48 @@ public class KeyPreviewView extends TextView { setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)); setTypeface(key.selectPreviewTypeface(drawParams)); // TODO Should take care of temporaryShiftLabel here. - setText(key.getPreviewLabel()); + setTextAndScaleX(key.getPreviewLabel()); + } + + private void setTextAndScaleX(final String text) { + setTextScaleX(1.0f); + setText(text); + if (sNoScaleXTextSet.contains(text)) { + return; + } + // TODO: Override {@link #setBackground(Drawable)} that is supported from API 16 and + // calculate maximum text width. + final Drawable background = getBackground(); + if (background == null) { + return; + } + background.getPadding(mBackgroundPadding); + final int maxWidth = background.getIntrinsicWidth() - mBackgroundPadding.left + - mBackgroundPadding.right; + final float width = getTextWidth(text, getPaint()); + if (width <= maxWidth) { + sNoScaleXTextSet.add(text); + return; + } + setTextScaleX(maxWidth / width); + } + + public static void clearTextCache() { + sNoScaleXTextSet.clear(); + } + + private static float getTextWidth(final String text, final TextPaint paint) { + if (TextUtils.isEmpty(text)) { + return 0.0f; + } + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + float width = 0; + for (int i = 0; i < count; i++) { + width += widths[i]; + } + return width; } // Background state set diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java index 133462ac7..c60d587db 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java @@ -31,7 +31,6 @@ public final class KeyVisualAttributes { public final float mLabelRatio; public final int mLabelSize; public final float mLargeLetterRatio; - public final float mLargeLabelRatio; public final float mHintLetterRatio; public final float mShiftedLetterHintRatio; public final float mHintLabelRatio; @@ -48,13 +47,14 @@ public final class KeyVisualAttributes { public final int mPreviewTextColor; public final float mHintLabelVerticalAdjustment; + public final float mLabelOffCenterRatio; + public final float mHintLabelOffCenterRatio; private static final int[] VISUAL_ATTRIBUTE_IDS = { R.styleable.Keyboard_Key_keyTypeface, R.styleable.Keyboard_Key_keyLetterSize, R.styleable.Keyboard_Key_keyLabelSize, R.styleable.Keyboard_Key_keyLargeLetterRatio, - R.styleable.Keyboard_Key_keyLargeLabelRatio, R.styleable.Keyboard_Key_keyHintLetterRatio, R.styleable.Keyboard_Key_keyShiftedLetterHintRatio, R.styleable.Keyboard_Key_keyHintLabelRatio, @@ -69,6 +69,8 @@ public final class KeyVisualAttributes { R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, R.styleable.Keyboard_Key_keyPreviewTextColor, R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, + R.styleable.Keyboard_Key_keyLabelOffCenterRatio, + R.styleable.Keyboard_Key_keyHintLabelOffCenterRatio }; private static final SparseIntArray sVisualAttributeIds = new SparseIntArray(); private static final int ATTR_DEFINED = 1; @@ -109,8 +111,6 @@ public final class KeyVisualAttributes { R.styleable.Keyboard_Key_keyLabelSize); mLargeLetterRatio = ResourceUtils.getFraction(keyAttr, R.styleable.Keyboard_Key_keyLargeLetterRatio); - mLargeLabelRatio = ResourceUtils.getFraction(keyAttr, - R.styleable.Keyboard_Key_keyLargeLabelRatio); mHintLetterRatio = ResourceUtils.getFraction(keyAttr, R.styleable.Keyboard_Key_keyHintLetterRatio); mShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr, @@ -135,5 +135,9 @@ public final class KeyVisualAttributes { mHintLabelVerticalAdjustment = ResourceUtils.getFraction(keyAttr, R.styleable.Keyboard_Key_keyHintLabelVerticalAdjustment, 0.0f); + mLabelOffCenterRatio = ResourceUtils.getFraction(keyAttr, + R.styleable.Keyboard_Key_keyLabelOffCenterRatio, 0.0f); + mHintLabelOffCenterRatio = ResourceUtils.getFraction(keyAttr, + R.styleable.Keyboard_Key_keyHintLabelOffCenterRatio, 0.0f); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 8bff27574..fa4192790 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -31,6 +31,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardTheme; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.utils.ResourceUtils; @@ -643,6 +644,9 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr, R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId, KeyboardId.elementIdToName(id.mElementId)); + final boolean keyboardThemeMacthed = matchTypedValue(caseAttr, + R.styleable.Keyboard_Case_keyboardTheme, mParams.mThemeId, + KeyboardTheme.getKeyboardThemeName(mParams.mThemeId)); final boolean modeMatched = matchTypedValue(caseAttr, R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); final boolean navigateNextMatched = matchBoolean(caseAttr, @@ -671,19 +675,21 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final boolean countryCodeMatched = matchString(caseAttr, R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched - && modeMatched && navigateNextMatched && navigatePreviousMatched - && passwordInputMatched && clobberSettingsKeyMatched && hasShortcutKeyMatched - && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched - && isIconDefinedMatched && localeCodeMatched && languageCodeMatched - && countryCodeMatched; + && keyboardThemeMacthed && modeMatched && navigateNextMatched + && navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched + && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched + && isMultiLineMatched && imeActionMatched && isIconDefinedMatched + && localeCodeMatched && languageCodeMatched && countryCodeMatched; if (DEBUG) { - startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, + startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, textAttr(caseAttr.getString( R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"), textAttr(caseAttr.getString( R.styleable.Keyboard_Case_keyboardLayoutSetElement), "keyboardLayoutSetElement"), + textAttr(caseAttr.getString( + R.styleable.Keyboard_Case_keyboardTheme), "keyboardTheme"), textAttr(caseAttr.getString(R.styleable.Keyboard_Case_mode), "mode"), textAttr(caseAttr.getString(R.styleable.Keyboard_Case_imeAction), "imeAction"), diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 09550c4cb..e1f302c1e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -53,7 +53,8 @@ public final class KeyboardIconsSet { public static final String NAME_LANGUAGE_SWITCH_KEY = "language_switch_key"; public static final String NAME_ZWNJ_KEY = "zwnj_key"; public static final String NAME_ZWJ_KEY = "zwj_key"; - public static final String NAME_EMOJI_KEY = "emoji_key"; + public static final String NAME_EMOJI_ACTION_KEY = "emoji_action_key"; + public static final String NAME_EMOJI_NORMAL_KEY = "emoji_normal_key"; private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); @@ -81,7 +82,8 @@ public final class KeyboardIconsSet { NAME_LANGUAGE_SWITCH_KEY, R.styleable.Keyboard_iconLanguageSwitchKey, NAME_ZWNJ_KEY, R.styleable.Keyboard_iconZwnjKey, NAME_ZWJ_KEY, R.styleable.Keyboard_iconZwjKey, - NAME_EMOJI_KEY, R.styleable.Keyboard_iconEmojiKey, + NAME_EMOJI_ACTION_KEY, R.styleable.Keyboard_iconEmojiActionKey, + NAME_EMOJI_NORMAL_KEY, R.styleable.Keyboard_iconEmojiNormalKey, }; private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index f18ebd1fe..31bc549ca 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -86,15 +86,15 @@ public final class KeyboardTextsTable { /* 0:32 */ "morekeys_a", /* 1:32 */ "morekeys_o", /* 2:30 */ "morekeys_u", - /* 3:29 */ "morekeys_e", - /* 4:28 */ "morekeys_i", - /* 5:28 */ "keylabel_to_alpha", + /* 3:30 */ "keylabel_to_alpha", + /* 4:29 */ "morekeys_e", + /* 5:28 */ "morekeys_i", /* 6:23 */ "morekeys_c", /* 7:23 */ "double_quotes", /* 8:22 */ "morekeys_n", /* 9:22 */ "single_quotes", /* 10:20 */ "morekeys_s", - /* 11:15 */ "keyspec_currency", + /* 11:17 */ "keyspec_currency", /* 12:14 */ "morekeys_y", /* 13:13 */ "morekeys_d", /* 14:12 */ "morekeys_z", @@ -209,7 +209,7 @@ public final class KeyboardTextsTable { /* 123: 1 */ "morekeys_less_than", /* 124: 1 */ "morekeys_greater_than", /* 125: 1 */ "morekeys_exclamation", - /* 126: 0 */ "morekeys_currency", + /* 126: 0 */ "morekeys_currency_generic", /* 127: 0 */ "morekeys_symbols_1", /* 128: 0 */ "morekeys_symbols_2", /* 129: 0 */ "morekeys_symbols_3", @@ -250,7 +250,7 @@ public final class KeyboardTextsTable { /* 164: 0 */ "morekeys_single_quote", /* 165: 0 */ "morekeys_double_quote", /* 166: 0 */ "morekeys_tablet_double_quote", - /* 167: 0 */ "keyspec_emoji_key", + /* 167: 0 */ "keyspec_emoji_action_key", }; private static final String EMPTY = ""; @@ -258,11 +258,13 @@ public final class KeyboardTextsTable { /* Default texts */ private static final String[] TEXTS_DEFAULT = { /* morekeys_a ~ */ - EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, - /* ~ morekeys_i */ + EMPTY, EMPTY, EMPTY, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. /* keylabel_to_alpha */ "ABC", - /* morekeys_c */ EMPTY, + /* morekeys_e ~ */ + EMPTY, EMPTY, EMPTY, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_lqm_rqm", /* morekeys_n */ EMPTY, /* single_quotes */ "!text/single_lqm_rqm", @@ -378,7 +380,7 @@ public final class KeyboardTextsTable { /* morekeys_greater_than */ "!fixedColumnOrder!3,!text/keyspec_right_single_angle_quote,!text/keyspec_greater_than_equal,!text/keyspec_right_double_angle_quote", // U+00A1: "¡" INVERTED EXCLAMATION MARK /* morekeys_exclamation */ "\u00A1", - /* morekeys_currency */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", + /* morekeys_currency_generic */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1", // U+00B9: "¹" SUPERSCRIPT ONE // U+00BD: "½" VULGAR FRACTION ONE HALF // U+2153: "⅓" VULGAR FRACTION ONE THIRD @@ -462,7 +464,7 @@ public final class KeyboardTextsTable { /* morekeys_single_quote */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", /* morekeys_double_quote */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", /* morekeys_tablet_double_quote */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", - /* keyspec_emoji_key */ "!icon/emoji_key|!code/key_emoji", + /* keyspec_emoji_action_key */ "!icon/emoji_action_key|!code/key_emoji", }; /* Locale af: Afrikaans */ @@ -492,6 +494,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -508,9 +511,8 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133", - /* keylabel_to_alpha ~ */ - null, null, null, - /* ~ double_quotes */ + /* morekeys_c */ null, + /* double_quotes */ null, // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE /* morekeys_n */ "\u00F1,\u0144", @@ -525,17 +527,17 @@ public final class KeyboardTextsTable { /* Locale ar: Arabic */ private static final String[] TEXTS_ar = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0623: "أ" ARABIC LETTER ALEF WITH HAMZA ABOVE // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+062C: "ج" ARABIC LETTER JEEM /* keylabel_to_alpha */ "\u0623\u200C\u0628\u200C\u062C", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, null, /* ~ morekeys_cyrillic_soft_sign */ // U+0661: "١" ARABIC-INDIC DIGIT ONE /* keyspec_symbols_1 */ "\u0661", @@ -673,6 +675,7 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + /* keylabel_to_alpha */ null, // U+0259: "ə" LATIN SMALL LETTER SCHWA /* morekeys_e */ "\u0259", // U+0131: "ı" LATIN SMALL LETTER DOTLESS I @@ -683,7 +686,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -706,14 +708,16 @@ public final class KeyboardTextsTable { /* Locale be_BY: Belarusian (Belarus) */ private static final String[] TEXTS_be_BY = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -740,14 +744,16 @@ public final class KeyboardTextsTable { /* Locale bg: Bulgarian */ private static final String[] TEXTS_bg = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ // single_quotes of Bulgarian is default single_quotes_right_left. /* double_quotes */ "!text/double_9qm_lqm", }; @@ -755,18 +761,18 @@ public final class KeyboardTextsTable { /* Locale bn_IN: Bengali (India) */ private static final String[] TEXTS_bn_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0995: "क" BENGALI LETTER KA // U+0996: "ख" BENGALI LETTER KHA // U+0997: "ग" BENGALI LETTER GA /* keylabel_to_alpha */ "\u0995\u0996\u0997", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ - // U+09F3: "৳" BENGALI RUPEE SIGN - /* keyspec_currency */ "\u09F3", + // U+20B9: "₹" INDIAN RUPEE SIGN + /* keyspec_currency */ "\u20B9", }; /* Locale ca: Catalan */ @@ -798,6 +804,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS @@ -813,7 +820,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -872,6 +878,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+011B: "ě" LATIN SMALL LETTER E WITH CARON // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE @@ -888,7 +895,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u00EC,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -945,13 +951,13 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS /* morekeys_e */ "\u00E9,\u00EB", // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS /* morekeys_i */ "\u00ED,\u00EF", - /* keylabel_to_alpha */ null, /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE @@ -1020,15 +1026,15 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,%,\u00FB,\u00F9,\u00FA,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0117", - /* morekeys_i ~ */ - null, null, null, - /* ~ morekeys_c */ + /* morekeys_i */ null, + /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_lqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE @@ -1065,8 +1071,8 @@ public final class KeyboardTextsTable { /* Locale el: Greek */ private static final String[] TEXTS_el = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0391: "Α" GREEK CAPITAL LETTER ALPHA // U+0392: "Β" GREEK CAPITAL LETTER BETA @@ -1100,6 +1106,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -1112,7 +1119,6 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", /* double_quotes */ null, @@ -1159,6 +1165,7 @@ public final class KeyboardTextsTable { // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK // U+00B5: "µ" MICRO SIGN /* morekeys_u */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B,\u0169,\u0171,\u0173,\u00B5", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+011B: "ě" LATIN SMALL LETTER E WITH CARON // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE @@ -1178,7 +1185,6 @@ public final class KeyboardTextsTable { // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u0129,\u00EC,\u012F,\u012B,\u0131,\u0133", - /* keylabel_to_alpha */ null, // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA @@ -1300,6 +1306,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS @@ -1315,7 +1322,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -1364,6 +1370,7 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u00FC,\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u016F,\u0171", + /* keylabel_to_alpha */ null, // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE @@ -1381,7 +1388,6 @@ public final class KeyboardTextsTable { // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+0131: "ı" LATIN SMALL LETTER DOTLESS I /* morekeys_i */ "\u012B,\u00EC,\u012F,\u00ED,\u00EE,\u00EF,\u0131", - /* keylabel_to_alpha */ null, // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -1466,6 +1472,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS @@ -1481,7 +1488,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -1495,16 +1501,16 @@ public final class KeyboardTextsTable { /* Locale fa: Persian */ private static final String[] TEXTS_fa = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0627: "ا" ARABIC LETTER ALEF // U+200C: ZERO WIDTH NON-JOINER // U+0628: "ب" ARABIC LETTER BEH // U+067E: "پ" ARABIC LETTER PEH /* keylabel_to_alpha */ "\u0627\u200C\u0628\u200C\u067E", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+FDFC: "﷼" RIAL SIGN /* keyspec_currency */ "\uFDFC", @@ -1655,7 +1661,7 @@ public final class KeyboardTextsTable { /* morekeys_o */ "\u00F8,\u00F4,\u00F2,\u00F3,\u00F5,\u0153,\u014D", // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* morekeys_u */ "\u00FC", - /* morekeys_e ~ */ + /* keylabel_to_alpha ~ */ null, null, null, null, null, null, null, /* ~ single_quotes */ // U+0161: "š" LATIN SMALL LETTER S WITH CARON @@ -1716,6 +1722,7 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00F9,\u00FB,%,\u00FC,\u00FA,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -1731,7 +1738,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EE,%,\u00EF,\u00EC,\u00ED,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -1789,6 +1795,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS @@ -1804,7 +1811,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -1818,15 +1824,15 @@ public final class KeyboardTextsTable { /* Locale hi: Hindi */ private static final String[] TEXTS_hi = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -1872,7 +1878,7 @@ public final class KeyboardTextsTable { private static final String[] TEXTS_hr = { /* morekeys_a ~ */ null, null, null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ morekeys_i */ // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA @@ -1929,6 +1935,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u0171,\u00FB,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -1944,7 +1951,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u00EC,\u012F,\u012B", - /* keylabel_to_alpha */ null, /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, @@ -1959,18 +1965,18 @@ public final class KeyboardTextsTable { /* Locale hy_AM: Armenian (Armenia) */ private static final String[] TEXTS_hy_AM = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0531: "Ա" ARMENIAN CAPITAL LETTER AYB // U+0532: "Բ" ARMENIAN CAPITAL LETTER BEN // U+0533: "Գ" ARMENIAN CAPITAL LETTER GIM /* keylabel_to_alpha */ "\u0531\u0532\u0533", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, + null, null, null, null, null, /* ~ morekeys_nordic_row2_11 */ // U+055E: "՞" ARMENIAN QUESTION MARK // U+055C: "՜" ARMENIAN EXCLAMATION MARK @@ -2043,6 +2049,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE @@ -2058,7 +2065,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EE,\u00EC,\u012F,\u012B", - /* keylabel_to_alpha */ null, /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, @@ -2103,6 +2109,7 @@ public final class KeyboardTextsTable { // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u016B", + /* keylabel_to_alpha */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -2118,11 +2125,11 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u012F,\u012B", - /* keylabel_to_alpha ~ */ + /* morekeys_c ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, + null, null, null, null, null, /* ~ keyspec_tablet_comma */ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS /* keyspec_swiss_row1_11 */ "\u00FC", @@ -2141,14 +2148,16 @@ public final class KeyboardTextsTable { /* Locale iw: Hebrew */ private static final String[] TEXTS_iw = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+05D0: "א" HEBREW LETTER ALEF // U+05D1: "ב" HEBREW LETTER BET // U+05D2: "ג" HEBREW LETTER GIMEL /* keylabel_to_alpha */ "\u05D0\u05D1\u05D2", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_rqm_9qm", /* morekeys_n */ null, /* single_quotes */ "!text/single_rqm_9qm", @@ -2198,14 +2207,16 @@ public final class KeyboardTextsTable { /* Locale ka_GE: Georgian (Georgia) */ private static final String[] TEXTS_ka_GE = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+10D0: "ა" GEORGIAN LETTER AN // U+10D1: "ბ" GEORGIAN LETTER BAN // U+10D2: "გ" GEORGIAN LETTER GAN /* keylabel_to_alpha */ "\u10D0\u10D1\u10D2", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -2214,16 +2225,16 @@ public final class KeyboardTextsTable { /* Locale kk: Kazakh */ private static final String[] TEXTS_kk = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, + null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2272,14 +2283,14 @@ public final class KeyboardTextsTable { /* Locale km_KH: Khmer (Cambodia) */ private static final String[] TEXTS_km_KH = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+1780: "ក" KHMER LETTER KA // U+1781: "ខ" KHMER LETTER KHA // U+1782: "គ" KHMER LETTER KO /* keylabel_to_alpha */ "\u1780\u1781\u1782", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, @@ -2287,7 +2298,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, /* ~ morekeys_cyrillic_a */ // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL /* morekeys_currency_dollar */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1", @@ -2296,15 +2307,15 @@ public final class KeyboardTextsTable { /* Locale kn_IN: Kannada (India) */ private static final String[] TEXTS_kn_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0C85: "ಅ" KANNADA LETTER A // U+0C86: "ಆ" KANNADA LETTER AA // U+0C87: "ಇ" KANNADA LETTER I /* keylabel_to_alpha */ "\u0C85\u0C86\u0C87", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2313,16 +2324,16 @@ public final class KeyboardTextsTable { /* Locale ky: Kirghiz */ private static final String[] TEXTS_ky = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, + null, null, null, /* ~ morekeys_k */ // U+0451: "ё" CYRILLIC SMALL LETTER IO /* morekeys_cyrillic_ie */ "\u0451", @@ -2357,15 +2368,15 @@ public final class KeyboardTextsTable { /* Locale lo_LA: Lao (Laos) */ private static final String[] TEXTS_lo_LA = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0E81: "ກ" LAO LETTER KO // U+0E82: "ຂ" LAO LETTER KHO SUNG // U+0E84: "ຄ" LAO LETTER KHO TAM /* keylabel_to_alpha */ "\u0E81\u0E82\u0E84", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20AD: "₭" KIP SIGN /* keyspec_currency */ "\u20AD", @@ -2402,6 +2413,7 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u016B,\u0173,\u00FC,\u016B,\u00F9,\u00FA,\u00FB,\u016F,\u0171", + /* keylabel_to_alpha */ null, // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON @@ -2419,7 +2431,6 @@ public final class KeyboardTextsTable { // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+0131: "ı" LATIN SMALL LETTER DOTLESS I /* morekeys_i */ "\u012F,\u012B,\u00EC,\u00ED,\u00EE,\u00EF,\u0131", - /* keylabel_to_alpha */ null, // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -2496,6 +2507,7 @@ public final class KeyboardTextsTable { // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u016B,\u0173,\u00F9,\u00FA,\u00FB,\u00FC,\u016F,\u0171", + /* keylabel_to_alpha */ null, // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE @@ -2513,7 +2525,6 @@ public final class KeyboardTextsTable { // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+0131: "ı" LATIN SMALL LETTER DOTLESS I /* morekeys_i */ "\u012B,\u012F,\u00EC,\u00ED,\u00EE,\u00EF,\u0131", - /* keylabel_to_alpha */ null, // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -2563,14 +2574,16 @@ public final class KeyboardTextsTable { /* Locale mk: Macedonian */ private static final String[] TEXTS_mk = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -2601,13 +2614,13 @@ public final class KeyboardTextsTable { /* Locale ml_IN: Malayalam (India) */ private static final String[] TEXTS_ml_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0D05: "അ" MALAYALAM LETTER A /* keylabel_to_alpha */ "\u0D05", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2616,15 +2629,15 @@ public final class KeyboardTextsTable { /* Locale mn_MN: Mongolian (Mongolia) */ private static final String[] TEXTS_mn_MN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20AE: "₮" TUGRIK SIGN /* keyspec_currency */ "\u20AE", @@ -2633,15 +2646,15 @@ public final class KeyboardTextsTable { /* Locale mr_IN: Marathi (India) */ private static final String[] TEXTS_mr_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -2686,18 +2699,18 @@ public final class KeyboardTextsTable { /* Locale my_MM: Burmese (Myanmar) */ private static final String[] TEXTS_my_MM = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+1000: "က" MYANMAR LETTER KA // U+1001: "ခ" MYANMAR LETTER KHA // U+1002: "ဂ" MYANMAR LETTER GA /* keylabel_to_alpha */ "\u1000\u1001\u1002", - /* morekeys_c ~ */ + /* morekeys_e ~ */ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, + null, null, null, null, null, /* ~ morekeys_nordic_row2_11 */ /* morekeys_punctuation */ "!autoColumnOrder!9,\u104A,.,?,!,#,),(,/,;,...,',@,:,-,\",+,\\%,&", // U+104A: "၊" MYANMAR SIGN LITTLE SECTION @@ -2744,6 +2757,7 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -2752,9 +2766,8 @@ public final class KeyboardTextsTable { // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113", - /* morekeys_i ~ */ - null, null, null, - /* ~ morekeys_c */ + /* morekeys_i */ null, + /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_rqm", @@ -2780,15 +2793,15 @@ public final class KeyboardTextsTable { /* Locale ne_NP: Nepali (Nepal) */ private static final String[] TEXTS_ne_NP = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0915: "क" DEVANAGARI LETTER KA // U+0916: "ख" DEVANAGARI LETTER KHA // U+0917: "ग" DEVANAGARI LETTER GA /* keylabel_to_alpha */ "\u0915\u0916\u0917", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN /* keyspec_currency */ "\u0930\u0941.", @@ -2856,6 +2869,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00FB,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -2872,7 +2886,6 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B,\u0133", - /* keylabel_to_alpha */ null, /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE @@ -2907,6 +2920,7 @@ public final class KeyboardTextsTable { // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON /* morekeys_o */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D", /* morekeys_u */ null, + /* keylabel_to_alpha */ null, // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE @@ -2916,7 +2930,6 @@ public final class KeyboardTextsTable { // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON /* morekeys_e */ "\u0119,\u00E8,\u00E9,\u00EA,\u00EB,\u0117,\u0113", /* morekeys_i */ null, - /* keylabel_to_alpha */ null, // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -2969,6 +2982,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE @@ -2984,7 +2998,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EE,\u00EC,\u00EF,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -3017,7 +3030,7 @@ public final class KeyboardTextsTable { // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON /* morekeys_a */ "\u00E2,\u00E3,\u0103,\u00E0,\u00E1,\u00E4,\u00E6,\u00E5,\u0101", /* morekeys_o ~ */ - null, null, null, + null, null, null, null, /* ~ morekeys_e */ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS @@ -3026,7 +3039,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B", - /* keylabel_to_alpha */ null, /* morekeys_c */ null, /* double_quotes */ "!text/double_9qm_rqm", /* morekeys_n */ null, @@ -3046,14 +3058,16 @@ public final class KeyboardTextsTable { /* Locale ru: Russian */ private static final String[] TEXTS_ru = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -3080,12 +3094,17 @@ public final class KeyboardTextsTable { /* Locale si_LK: Sinhalese (Sri Lanka) */ private static final String[] TEXTS_si_LK = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0D85: "අ" SINHALA LETTER AYANNA // U+0D86: "ආ" SINHALA LETTER AAYANNA /* keylabel_to_alpha */ "\u0D85,\u0D86", + /* morekeys_e ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_s */ + // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA + /* keyspec_currency */ "\u0DBB\u0DD4", }; /* Locale sk: Slovak */ @@ -3118,6 +3137,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE /* morekeys_u */ "\u00FA,\u016F,\u00FC,\u016B,\u0173,\u00F9,\u00FB,\u0171", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+011B: "ě" LATIN SMALL LETTER E WITH CARON // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON @@ -3135,7 +3155,6 @@ public final class KeyboardTextsTable { // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS // U+0131: "ı" LATIN SMALL LETTER DOTLESS I /* morekeys_i */ "\u00ED,\u012B,\u012F,\u00EC,\u00EE,\u00EF,\u0131", - /* keylabel_to_alpha */ null, // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE @@ -3187,7 +3206,7 @@ public final class KeyboardTextsTable { private static final String[] TEXTS_sl = { /* morekeys_a ~ */ null, null, null, null, null, null, - /* ~ keylabel_to_alpha */ + /* ~ morekeys_i */ // U+010D: "č" LATIN SMALL LETTER C WITH CARON // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE /* morekeys_c */ "\u010D,\u0107", @@ -3212,15 +3231,17 @@ public final class KeyboardTextsTable { /* Locale sr: Serbian */ private static final String[] TEXTS_sr = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // END: More keys definitions for Serbian (Cyrillic) // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -3290,6 +3311,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FA,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -3301,7 +3323,6 @@ public final class KeyboardTextsTable { // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS /* morekeys_i */ "\u00ED,\u00EC,\u00EE,\u00EF", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -3384,6 +3405,7 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B", + /* keylabel_to_alpha */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -3396,7 +3418,6 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE /* morekeys_i */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", /* double_quotes */ null, @@ -3414,32 +3435,61 @@ public final class KeyboardTextsTable { /* Locale ta_IN: Tamil (India) */ private static final String[] TEXTS_ta_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0BA4: "த" TAMIL LETTER TA // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+0BF9: "௹" TAMIL RUPEE SIGN /* keyspec_currency */ "\u0BF9", }; + /* Locale ta_LK: Tamil (Sri Lanka) */ + private static final String[] TEXTS_ta_LK = { + /* morekeys_a ~ */ + null, null, null, + /* ~ morekeys_u */ + // Label for "switch to alphabetic" key. + // U+0BA4: "த" TAMIL LETTER TA + // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I + // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA + /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", + /* morekeys_e ~ */ + null, null, null, null, null, null, null, + /* ~ morekeys_s */ + // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA + /* keyspec_currency */ "\u0DBB\u0DD4", + }; + + /* Locale ta_SG: Tamil (Singapore) */ + private static final String[] TEXTS_ta_SG = { + /* morekeys_a ~ */ + null, null, null, + /* ~ morekeys_u */ + // Label for "switch to alphabetic" key. + // U+0BA4: "த" TAMIL LETTER TA + // U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I + // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA + /* keylabel_to_alpha */ "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", + }; + /* Locale te_IN: Telugu (India) */ private static final String[] TEXTS_te_IN = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0C05: "అ" TELUGU LETTER A // U+0C06: "ఆ" TELUGU LETTER AA // U+0C07: "ఇ" TELUGU LETTER I /* keylabel_to_alpha */ "\u0C05\u0C06\u0C07", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+20B9: "₹" INDIAN RUPEE SIGN /* keyspec_currency */ "\u20B9", @@ -3448,15 +3498,15 @@ public final class KeyboardTextsTable { /* Locale th: Thai */ private static final String[] TEXTS_th = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0E01: "ก" THAI CHARACTER KO KAI // U+0E02: "ข" THAI CHARACTER KHO KHAI // U+0E04: "ค" THAI CHARACTER KHO KHWAI /* keylabel_to_alpha */ "\u0E01\u0E02\u0E04", - /* morekeys_c ~ */ - null, null, null, null, null, + /* morekeys_e ~ */ + null, null, null, null, null, null, null, /* ~ morekeys_s */ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT /* keyspec_currency */ "\u0E3F", @@ -3491,6 +3541,7 @@ public final class KeyboardTextsTable { // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS @@ -3506,7 +3557,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -3536,6 +3586,7 @@ public final class KeyboardTextsTable { // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B", + /* keylabel_to_alpha */ null, /* morekeys_e */ null, // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX @@ -3545,7 +3596,6 @@ public final class KeyboardTextsTable { // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON /* morekeys_i */ "\u0131,\u00EE,\u00EF,\u00EC,\u00ED,\u012F,\u012B", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+010D: "č" LATIN SMALL LETTER C WITH CARON @@ -3568,14 +3618,16 @@ public final class KeyboardTextsTable { /* Locale uk: Ukrainian */ private static final String[] TEXTS_uk = { /* morekeys_a ~ */ - null, null, null, null, null, - /* ~ morekeys_i */ + null, null, null, + /* ~ morekeys_u */ // Label for "switch to alphabetic" key. // U+0410: "А" CYRILLIC CAPITAL LETTER A // U+0411: "Б" CYRILLIC CAPITAL LETTER BE // U+0412: "В" CYRILLIC CAPITAL LETTER VE /* keylabel_to_alpha */ "\u0410\u0411\u0412", - /* morekeys_c */ null, + /* morekeys_e ~ */ + null, null, null, + /* ~ morekeys_c */ /* double_quotes */ "!text/double_9qm_lqm", /* morekeys_n */ null, /* single_quotes */ "!text/single_9qm_lqm", @@ -3659,6 +3711,7 @@ public final class KeyboardTextsTable { // U+1EEF: "ữ" LATIN SMALL LETTER U WITH HORN AND TILDE // U+1EF1: "ự" LATIN SMALL LETTER U WITH HORN AND DOT BELOW /* morekeys_u */ "\u00F9,\u00FA,\u1EE7,\u0169,\u1EE5,\u01B0,\u1EEB,\u1EE9,\u1EED,\u1EEF,\u1EF1", + /* keylabel_to_alpha */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+1EBB: "ẻ" LATIN SMALL LETTER E WITH HOOK ABOVE @@ -3677,8 +3730,8 @@ public final class KeyboardTextsTable { // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE // U+1ECB: "ị" LATIN SMALL LETTER I WITH DOT BELOW /* morekeys_i */ "\u00EC,\u00ED,\u1EC9,\u0129,\u1ECB", - /* keylabel_to_alpha ~ */ - null, null, null, null, null, null, + /* morekeys_c ~ */ + null, null, null, null, null, /* ~ morekeys_s */ // U+20AB: "₫" DONG SIGN /* keyspec_currency */ "\u20AB", @@ -3719,6 +3772,7 @@ public final class KeyboardTextsTable { // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON /* morekeys_u */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B", + /* keylabel_to_alpha */ null, // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -3731,7 +3785,6 @@ public final class KeyboardTextsTable { // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE /* morekeys_i */ "\u00ED,\u00EE,\u00EF,\u012B,\u00EC", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA /* morekeys_c */ "\u00E7", /* double_quotes */ null, @@ -3779,6 +3832,7 @@ public final class KeyboardTextsTable { // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK /* morekeys_u */ "\u00F9,\u00FA,\u00FB,\u00FC,\u0169,\u016B,\u016D,\u016F,\u0171,\u0173", + /* keylabel_to_alpha */ null, // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX @@ -3800,7 +3854,6 @@ public final class KeyboardTextsTable { // U+0131: "ı" LATIN SMALL LETTER DOTLESS I // U+0133: "ij" LATIN SMALL LIGATURE IJ /* morekeys_i */ "\u00EC,\u00ED,\u00EE,\u00EF,\u0129,\u012B,\u012D,\u012F,\u0131,\u0133", - /* keylabel_to_alpha */ null, // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX @@ -3893,7 +3946,7 @@ public final class KeyboardTextsTable { "cs" , TEXTS_cs, /* 17/ 21 Czech */ "da" , TEXTS_da, /* 19/ 54 Danish */ "de" , TEXTS_de, /* 16/ 62 German */ - "el" , TEXTS_el, /* 1/ 6 Greek */ + "el" , TEXTS_el, /* 1/ 4 Greek */ "en" , TEXTS_en, /* 8/ 11 English */ "eo" , TEXTS_eo, /* 26/118 Esperanto */ "es" , TEXTS_es, /* 8/ 55 Spanish */ @@ -3931,13 +3984,15 @@ public final class KeyboardTextsTable { "rm" , TEXTS_rm, /* 1/ 2 Raeto-Romance */ "ro" , TEXTS_ro, /* 6/ 16 Romanian */ "ru" , TEXTS_ru, /* 9/ 32 Russian */ - "si_LK" , TEXTS_si_LK, /* 1/ 6 Sinhalese (Sri Lanka) */ + "si_LK" , TEXTS_si_LK, /* 2/ 12 Sinhalese (Sri Lanka) */ "sk" , TEXTS_sk, /* 20/ 22 Slovak */ "sl" , TEXTS_sl, /* 8/ 20 Slovenian */ "sr" , TEXTS_sr, /* 11/ 94 Serbian */ "sv" , TEXTS_sv, /* 21/ 54 Swedish */ "sw" , TEXTS_sw, /* 9/ 18 Swahili */ "ta_IN" , TEXTS_ta_IN, /* 2/ 12 Tamil (India) */ + "ta_LK" , TEXTS_ta_LK, /* 2/ 12 Tamil (Sri Lanka) */ + "ta_SG" , TEXTS_ta_SG, /* 1/ 4 Tamil (Singapore) */ "te_IN" , TEXTS_te_IN, /* 2/ 12 Telugu (India) */ "th" , TEXTS_th, /* 2/ 12 Thai */ "tl" , TEXTS_tl, /* 7/ 9 Tagalog */ diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index ad14c06ef..162a209e3 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -231,7 +231,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { // Don't add single letter words, possibly confuses // capitalization of i. final int wordLen = StringUtils.codePointCount(word); - if (wordLen < MAX_WORD_LENGTH && wordLen > 1) { + if (wordLen <= MAX_WORD_LENGTH && wordLen > 1) { if (DEBUG) { Log.d(TAG, "addName " + name + ", " + word + ", " + prevWordsInfo); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index d6e6656ab..fd1f51dd6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -468,7 +468,9 @@ public class DictionaryFacilitator { // We don't add words with 0-frequency (assuming they would be profanity etc.). final boolean isValid = maxFreq > 0; UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWordsInfo, secondWord, - isValid, timeStampInSeconds, mDistracterFilter); + isValid, timeStampInSeconds, + new DistracterFilterCheckingIsInDictionary( + mDistracterFilter, userHistoryDictionary)); } private void removeWord(final String dictName, final String word) { @@ -489,8 +491,9 @@ public class DictionaryFacilitator { final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { final Dictionaries dictionaries = mDictionaries; - final SuggestionResults suggestionResults = - new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); + final SuggestionResults suggestionResults = new SuggestionResults( + dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS, + prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 5808b9e4e..c11a220a4 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -163,9 +164,31 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) { + asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); + } + + private void asyncPreCheckAndExecuteTaskWithWriteLock( + final Callable<Boolean> preCheckTask, final Runnable task) { + asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, task); + + } + + // Execute task with lock when the result of preCheckTask is true or preCheckTask is null. + private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, + final Callable<Boolean> preCheckTask, final Runnable task) { ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { @Override public void run() { + if (preCheckTask != null) { + try { + if (!preCheckTask.call().booleanValue()) { + return; + } + } catch (final Exception e) { + Log.e(TAG, "The pre check task throws an exception.", e); + return; + } + } lock.lock(); try { task.run(); @@ -278,22 +301,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { final boolean isBlacklisted, final int timestamp, final DistracterFilter distracterFilter) { reloadDictionaryIfRequired(); - asyncExecuteTaskWithWriteLock(new Runnable() { - @Override - public void run() { - if (mBinaryDictionary == null) { - return; - } - if (distracterFilter.isDistracterToWordsInDictionaries( - PrevWordsInfo.EMPTY_PREV_WORDS_INFO, word, mLocale)) { - // The word is a distracter. - return; - } - runGCIfRequiredLocked(true /* mindsBlockByGC */); - addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, - isNotAWord, isBlacklisted, timestamp); - } - }); + asyncPreCheckAndExecuteTaskWithWriteLock( + new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + return !distracterFilter.isDistracterToWordsInDictionaries( + PrevWordsInfo.EMPTY_PREV_WORDS_INFO, word, mLocale); + } + }, + new Runnable() { + @Override + public void run() { + if (mBinaryDictionary == null) { + return; + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, + isNotAWord, isBlacklisted, timestamp); + } + }); } protected void addUnigramLocked(final String word, final int frequency, diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index ebe436128..fecb0ef94 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -42,6 +42,7 @@ public final class InputAttributes { final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final public boolean mShouldShowVoiceInputKey; + final public boolean mIsGeneralTextInput; final private int mInputType; final private EditorInfo mEditorInfo; final private String mPackageNameForPrivateImeOptions; @@ -76,6 +77,7 @@ public final class InputAttributes { mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; mShouldShowVoiceInputKey = false; + mIsGeneralTextInput = false; return; } // inputClass == InputType.TYPE_CLASS_TEXT @@ -102,7 +104,7 @@ public final class InputAttributes { mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); final boolean noMicrophone = mIsPasswordField - || InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS == variation + || InputTypeUtils.isEmailVariation(variation) || InputType.TYPE_TEXT_VARIATION_URI == variation || hasNoMicrophoneKeyOption(); mShouldShowVoiceInputKey = !noMicrophone; @@ -117,6 +119,15 @@ public final class InputAttributes { || (!flagAutoCorrect && !flagMultiLine); mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode; + + // If we come here, inputClass is always TYPE_CLASS_TEXT + mIsGeneralTextInput = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS != variation + && InputType.TYPE_TEXT_VARIATION_PASSWORD != variation + && InputType.TYPE_TEXT_VARIATION_PHONETIC != variation + && InputType.TYPE_TEXT_VARIATION_URI != variation + && InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD != variation + && InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != variation + && InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD != variation; } public boolean isTypeNull() { diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java index e9e12f09f..7fa935413 100644 --- a/java/src/com/android/inputmethod/latin/InputView.java +++ b/java/src/com/android/inputmethod/latin/InputView.java @@ -21,14 +21,14 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; -import android.widget.LinearLayout; +import android.widget.FrameLayout; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.suggestions.MoreSuggestionsView; import com.android.inputmethod.latin.suggestions.SuggestionStripView; -public final class InputView extends LinearLayout { +public final class InputView extends FrameLayout { private final Rect mInputViewRect = new Rect(); private MainKeyboardView mMainKeyboardView; private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d2a2fbdd8..d57db8e9a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -20,7 +20,6 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; -import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -30,7 +29,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.net.ConnectivityManager; @@ -44,18 +42,22 @@ import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; +import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.LayoutParams; +import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; +import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.compat.InputConnectionCompatUtils; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.Event; @@ -67,6 +69,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.keyboard.TextDecoratorUi; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.DebugFlags; @@ -84,14 +87,16 @@ 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.CoordinateUtils; +import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils; import com.android.inputmethod.latin.utils.DialogUtils; -import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import com.android.inputmethod.latin.utils.StatsUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; +import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -127,7 +132,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private final Settings mSettings; private final DictionaryFacilitator mDictionaryFacilitator = - new DictionaryFacilitator(new DistracterFilterCheckingExactMatches(this /* context */)); + new DictionaryFacilitator( + new DistracterFilterCheckingExactMatchesAndSuggestions(this /* context */)); // TODO: Move from LatinIME. private final PersonalizationDictionaryUpdater mPersonalizationDictionaryUpdater = new PersonalizationDictionaryUpdater(this /* context */, mDictionaryFacilitator); @@ -136,7 +142,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen new Runnable() { @Override public void run() { - mHandler.postUpdateSuggestionStrip(); + mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } }); private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, @@ -145,14 +151,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If it turns out we need several, it will get grown seamlessly. final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1); - private View mExtractArea; - private View mKeyPreviewBackingView; + // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. + private View mInputView; private SuggestionStripView mSuggestionStripView; + private TextView mExtractEditText; private RichInputMethodManager mRichImm; @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); + private final SpecialKeyDetector mSpecialKeyDetector; + // Working variable for {@link #startShowingInputView()} and + // {@link #onEvaluateInputViewShown()}. + private boolean mIsExecutingStartShowingInputView; // Object for reacting to adding/removing a dictionary pack. private final BroadcastReceiver mDictionaryPackInstallReceiver = @@ -187,8 +198,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_FALSE = 0; private static final int ARG1_TRUE = 1; - private int mDelayUpdateSuggestions; - private int mDelayUpdateShiftState; + private int mDelayInMillisecondsToUpdateSuggestions; + private int mDelayInMillisecondsToUpdateShiftState; public UIHandler(final LatinIME ownerInstance) { super(ownerInstance); @@ -200,8 +211,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } final Resources res = latinIme.getResources(); - mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions); - mDelayUpdateShiftState = res.getInteger(R.integer.config_delay_update_shift_state); + mDelayInMillisecondsToUpdateSuggestions = res.getInteger( + R.integer.config_delay_in_milliseconds_to_update_suggestions); + mDelayInMillisecondsToUpdateShiftState = res.getInteger( + R.integer.config_delay_in_milliseconds_to_update_shift_state); } @Override @@ -215,7 +228,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_UPDATE_SUGGESTION_STRIP: cancelUpdateSuggestionStrip(); latinIme.mInputLogic.performUpdateSuggestionStripSync( - latinIme.mSettings.getCurrent()); + latinIme.mSettings.getCurrent(), msg.arg1 /* inputStyle */); break; case MSG_UPDATE_SHIFT_STATE: switcher.requestUpdatingShiftState(latinIme.getCurrentAutoCapsState(), @@ -237,10 +250,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId()); break; case MSG_REOPEN_DICTIONARIES: - latinIme.resetSuggest(); // We need to re-evaluate the currently composing word in case the script has // changed. postWaitForDictionaryLoad(); + latinIme.resetSuggest(); break; case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED: latinIme.mInputLogic.onUpdateTailBatchInputCompleted( @@ -249,8 +262,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_RESET_CACHES: final SettingsValues settingsValues = latinIme.mSettings.getCurrent(); - if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess(settingsValues, - msg.arg1 == 1 /* tryResumeSuggestions */, + if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess( + msg.arg1 == ARG1_TRUE /* tryResumeSuggestions */, msg.arg2 /* remainingTries */, this /* handler */)) { // If we were able to reset the caches, then we can reload the keyboard. // Otherwise, we'll do it when we can. @@ -265,8 +278,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - public void postUpdateSuggestionStrip() { - sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions); + public void postUpdateSuggestionStrip(final int inputStyle) { + sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP, inputStyle, + 0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions); } public void postReopenDictionaries() { @@ -279,16 +293,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (latinIme == null) { return; } - if (!latinIme.mSettings.getCurrent() - .isSuggestionsEnabledPerUserSettings()) { + if (!latinIme.mSettings.getCurrent().isSuggestionsEnabledPerUserSettings()) { return; } removeMessages(MSG_RESUME_SUGGESTIONS); if (shouldDelay) { sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, - shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, - 0 /* ignored */), - mDelayUpdateSuggestions); + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */), mDelayInMillisecondsToUpdateSuggestions); } else { sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS, shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, @@ -329,7 +341,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void postUpdateShiftState() { removeMessages(MSG_UPDATE_SHIFT_STATE); - sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), mDelayUpdateShiftState); + sendMessageDelayed(obtainMessage(MSG_UPDATE_SHIFT_STATE), + mDelayInMillisecondsToUpdateShiftState); } @UsedForTesting @@ -414,15 +427,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (latinIme != null) { executePendingImsCallback(latinIme, editorInfo, restarting); latinIme.onStartInputInternal(editorInfo, restarting); - if (ProductionFlags.ENABLE_CURSOR_RECT_CALLBACK) { - InputConnectionCompatUtils.requestCursorRect( - latinIme.getCurrentInputConnection(), true /* enableMonitor */); - } - if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { - InputConnectionCompatUtils.requestCursorAnchorInfo( - latinIme.getCurrentInputConnection(), true /* enableMonitor */, - true /* requestImmediateCallback */); - } } } } @@ -514,6 +518,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSettings = Settings.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); + mSpecialKeyDetector = new SpecialKeyDetector(this); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); @@ -689,11 +694,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(final Configuration conf) { - final SettingsValues settingsValues = mSettings.getCurrent(); + SettingsValues settingsValues = mSettings.getCurrent(); if (settingsValues.mDisplayOrientation != conf.orientation) { mHandler.startOrientationChanging(); mInputLogic.onOrientationChange(mSettings.getCurrent()); } + if (settingsValues.mHasHardwareKeyboard != Settings.readHasHardwareKeyboard(conf)) { + // If the state of having a hardware keyboard changed, then we want to reload the + // settings to adjust for that. + // TODO: we should probably do this unconditionally here, rather than only when we + // have a change in hardware keyboard configuration. + loadSettings(); + settingsValues = mSettings.getCurrent(); + if (settingsValues.mHasHardwareKeyboard) { + // We call cleanupInternalStateForFinishInput() because it's the right thing to do; + // however, it seems at the moment the framework is passing us a seemingly valid + // but actually non-functional InputConnection object. So if this bug ever gets + // fixed we'll be able to remove the composition, but until it is this code is + // actually not doing much. + cleanupInternalStateForFinishInput(); + } + } // TODO: Remove this test. if (!conf.locale.equals(mPersonalizationDictionaryUpdater.getLocale())) { refreshPersonalizationDictionarySession(settingsValues); @@ -709,13 +730,55 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void setInputView(final View view) { super.setInputView(view); - mExtractArea = getWindow().getWindow().getDecorView() - .findViewById(android.R.id.extractArea); - mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); + mInputView = view; mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); if (hasSuggestionStripView()) { mSuggestionStripView.setListener(this, view); } + mInputLogic.setTextDecoratorUi(new TextDecoratorUi(this, view)); + } + + @Override + public void setExtractView(final View view) { + final TextView prevExtractEditText = mExtractEditText; + super.setExtractView(view); + TextView nextExtractEditText = null; + if (view != null) { + final View extractEditText = view.findViewById(android.R.id.inputExtractEditText); + if (extractEditText instanceof TextView) { + nextExtractEditText = (TextView)extractEditText; + } + } + if (prevExtractEditText == nextExtractEditText) { + return; + } + if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && prevExtractEditText != null) { + prevExtractEditText.getViewTreeObserver().removeOnPreDrawListener( + mExtractTextViewPreDrawListener); + } + mExtractEditText = nextExtractEditText; + if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK && mExtractEditText != null) { + mExtractEditText.getViewTreeObserver().addOnPreDrawListener( + mExtractTextViewPreDrawListener); + } + } + + private final ViewTreeObserver.OnPreDrawListener mExtractTextViewPreDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + onExtractTextViewPreDraw(); + return true; + } + }; + + private void onExtractTextViewPreDraw() { + if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || !isFullscreenMode() + || mExtractEditText == null) { + return; + } + final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText); + mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); } @Override @@ -749,7 +812,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged() // is not guaranteed. It may even be called at the same time on a different thread. mSubtypeSwitcher.onSubtypeChanged(subtype); - mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype)); + mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype), + mSettings.getCurrent()); loadKeyboard(); } @@ -819,39 +883,52 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note: This call should be done by InputMethodService? updateFullscreenMode(); - // The app calling setText() has the effect of clearing the composing - // span, so we should reset our state unconditionally, even if restarting is true. - // We also tell the input logic about the combining rules for the current subtype, so - // it can adjust its combiners if needed. - mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype()); + // ALERT: settings have not been reloaded and there is a chance they may be stale. + // In the practice, if it is, we should have gotten onConfigurationChanged so it should + // be fine, but this is horribly confusing and must be fixed AS SOON AS POSSIBLE. - // Note: the following does a round-trip IPC on the main thread: be careful - final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); + // In some cases the input connection has not been reset yet and we can't access it. In + // this case we will need to call loadKeyboard() later, when it's accessible, so that we + // can go into the correct mode, so we need to do some housekeeping here. + final boolean needToCallLoadKeyboardLater; final Suggest suggest = mInputLogic.mSuggest; - if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) { - // TODO: Do this automatically. - resetSuggest(); - } + if (!currentSettingsValues.mHasHardwareKeyboard) { + // The app calling setText() has the effect of clearing the composing + // span, so we should reset our state unconditionally, even if restarting is true. + // We also tell the input logic about the combining rules for the current subtype, so + // it can adjust its combiners if needed. + mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(), + currentSettingsValues); + + // Note: the following does a round-trip IPC on the main thread: be careful + final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); + if (null != currentLocale && !currentLocale.equals(suggest.getLocale())) { + // TODO: Do this automatically. + resetSuggest(); + } - // TODO[IL]: Can the following be moved to InputLogic#startInput? - final boolean canReachInputConnection; - if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess( - editorInfo.initialSelStart, editorInfo.initialSelEnd, - false /* shouldFinishComposition */)) { - // Sometimes, while rotating, for some reason the framework tells the app we are not - // connected to it and that means we can't refresh the cache. In this case, schedule a - // refresh later. - // We try resetting the caches up to 5 times before giving up. - mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */); - // mLastSelection{Start,End} are reset later in this method, don't need to do it here - canReachInputConnection = false; + // TODO[IL]: Can the following be moved to InputLogic#startInput? + if (!mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess( + editorInfo.initialSelStart, editorInfo.initialSelEnd, + false /* shouldFinishComposition */)) { + // Sometimes, while rotating, for some reason the framework tells the app we are not + // connected to it and that means we can't refresh the cache. In this case, schedule + // a refresh later. + // We try resetting the caches up to 5 times before giving up. + mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */); + // mLastSelection{Start,End} are reset later in this method, no need to do it here + needToCallLoadKeyboardLater = true; + } else { + // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best + // effort to work around this bug. + mInputLogic.mConnection.tryFixLyingCursorPosition(); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, + true /* shouldDelay */); + needToCallLoadKeyboardLater = false; + } } else { - // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best - // effort to work around this bug. - mInputLogic.mConnection.tryFixLyingCursorPosition(); - mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, - true /* shouldDelay */); - canReachInputConnection = true; + // If we have a hardware keyboard we don't need to call loadKeyboard later anyway. + needToCallLoadKeyboardLater = false; } if (isDifferentTextField || @@ -869,9 +946,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(), getCurrentRecapitalizeState()); - if (!canReachInputConnection) { - // If we can't reach the input connection, we will call loadKeyboard again later, - // so we need to save its state now. The call will be done in #retryResetCaches. + if (needToCallLoadKeyboardLater) { + // If we need to call loadKeyboard again later, we need to save its state now. The + // later call will be done in #retryResetCaches. switcher.saveKeyboardState(); } } else if (restarting) { @@ -928,6 +1005,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void onFinishInputViewInternal(final boolean finishingInput) { super.onFinishInputView(finishingInput); + cleanupInternalStateForFinishInput(); + } + + private void cleanupInternalStateForFinishInput() { mKeyboardSwitcher.deallocateMemory(); // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestionStrip(); @@ -947,24 +1028,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd); } - // If the keyboard is not visible, we don't need to do all the housekeeping work, as it - // will be reset when the keyboard shows up anyway. - // TODO: revisit this when LatinIME supports hardware keyboards. - // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown(). - // TODO: find a better way to simulate actual execution. - if (isInputViewShown() && - mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { + // This call happens when we have a hardware keyboard as well as when we don't. While we + // don't support hardware keyboards yet we should avoid doing the processing associated + // with cursor movement when we have a hardware keyboard since we are not in charge. + final SettingsValues settingsValues = mSettings.getCurrent(); + if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) + && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, + settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } } - @Override - public void onUpdateCursor(final Rect rect) { - if (DEBUG) { - Log.i(TAG, "onUpdateCursor:" + rect.toShortString()); + // We cannot mark this method as @Override until new SDK becomes publicly available. + // @Override + public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) { + if (!ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK || isFullscreenMode()) { + return; } - super.onUpdateCursor(rect); + mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); } /** @@ -1040,78 +1122,76 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords, null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, false /* isPrediction */); + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */); // When in fullscreen mode, show completions generated by the application forcibly setSuggestedWords(suggestedWords); } - private int getAdjustedBackingViewHeight() { - final int currentHeight = mKeyPreviewBackingView.getHeight(); - if (currentHeight > 0) { - return currentHeight; - } - - final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); - if (visibleKeyboardView == null) { - return 0; - } - // TODO: !!!!!!!!!!!!!!!!!!!! Handle different backing view heights between the main !!! - // keyboard and the emoji keyboard. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - final int keyboardHeight = visibleKeyboardView.getHeight(); - final int suggestionsHeight = mSuggestionStripView.getHeight(); - final int displayHeight = getResources().getDisplayMetrics().heightPixels; - final Rect rect = new Rect(); - mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect); - final int notificationBarHeight = rect.top; - final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight - - keyboardHeight; - - final LayoutParams params = mKeyPreviewBackingView.getLayoutParams(); - params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight); - mKeyPreviewBackingView.setLayoutParams(params); - return params.height; - } - @Override public void onComputeInsets(final InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); + final SettingsValues settingsValues = mSettings.getCurrent(); final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView(); if (visibleKeyboardView == null || !hasSuggestionStripView()) { return; } - final int adjustedBackingHeight = getAdjustedBackingViewHeight(); - final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE); - final int backingHeight = backingGone ? 0 : adjustedBackingHeight; - // In fullscreen mode, the height of the extract area managed by InputMethodService should - // be considered. - // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}. - final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0; - final int suggestionsHeight = (mSuggestionStripView.getVisibility() == View.GONE) ? 0 - : mSuggestionStripView.getHeight(); - final int extraHeight = extractHeight + backingHeight + suggestionsHeight; - int visibleTopY = extraHeight; - // Need to set touchable region only if input view is being shown + final int inputHeight = mInputView.getHeight(); + final boolean hasHardwareKeyboard = settingsValues.mHasHardwareKeyboard; + if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { + // If there is a hardware keyboard and a visible software keyboard view has been hidden, + // no visual element will be shown on the screen. + outInsets.touchableInsets = inputHeight; + outInsets.visibleTopInsets = inputHeight; + return; + } + final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes() + && mSuggestionStripView.getVisibility() == View.VISIBLE) + ? mSuggestionStripView.getHeight() : 0; + final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight; + mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY); + // Need to set touchable region only if a keyboard view is being shown. if (visibleKeyboardView.isShown()) { - // Note that the height of Emoji layout is the same as the height of the main keyboard - // and the suggestion strip - if (mKeyboardSwitcher.isShowingEmojiPalettes() - || mSuggestionStripView.getVisibility() == View.VISIBLE) { - visibleTopY -= suggestionsHeight; - } - final int touchY = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; - final int touchWidth = visibleKeyboardView.getWidth(); - final int touchHeight = visibleKeyboardView.getHeight() + extraHeight + final int touchLeft = 0; + final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY; + final int touchRight = visibleKeyboardView.getWidth(); + final int touchBottom = inputHeight // Extend touchable region below the keyboard. + EXTENDED_TOUCHABLE_REGION_HEIGHT; outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION; - outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight); + outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom); } outInsets.contentTopInsets = visibleTopY; outInsets.visibleTopInsets = visibleTopY; } + public void startShowingInputView() { + mIsExecutingStartShowingInputView = true; + // This {@link #showWindow(boolean)} will eventually call back + // {@link #onEvaluateInputViewShown()}. + showWindow(true /* showInput */); + mIsExecutingStartShowingInputView = false; + } + + public void stopShowingInputView() { + showWindow(false /* showInput */); + } + + @Override + public boolean onEvaluateInputViewShown() { + if (mIsExecutingStartShowingInputView) { + return true; + } + return super.onEvaluateInputViewShown(); + } + @Override public boolean onEvaluateFullscreenMode() { + final SettingsValues settingsValues = mSettings.getCurrent(); + if (settingsValues.mHasHardwareKeyboard) { + // If there is a hardware keyboard, disable full screen mode. + return false; + } // Reread resource value here, because this method is called by the framework as needed. final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources()); if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) { @@ -1121,19 +1201,34 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // hack for now. Let's get rid of this once the framework gets fixed. final EditorInfo ei = getCurrentInputEditorInfo(); return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0)); - } else { - return false; } + return false; } @Override public void updateFullscreenMode() { + // Override layout parameters to expand {@link SoftInputWindow} to the entire screen. + // See {@link InputMethodService#setinputView(View) and + // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}. + final Window window = getWindow().getWindow(); + ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT); + // This method may be called before {@link #setInputView(View)}. + if (mInputView != null) { + // In non-fullscreen mode, {@link InputView} and its parent inputArea should expand to + // the entire screen and be placed at the bottom of {@link SoftInputWindow}. + // In fullscreen mode, these shouldn't expand to the entire screen and should be + // coexistent with {@link #mExtractedArea} above. + // See {@link InputMethodService#setInputView(View) and + // com.android.internal.R.layout.input_method.xml. + final int layoutHeight = isFullscreenMode() + ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT; + final View inputArea = window.findViewById(android.R.id.inputArea); + ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight); + ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM); + ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight); + } super.updateFullscreenMode(); - - if (mKeyPreviewBackingView == null) return; - // In fullscreen mode, no need to have extra space to show the key preview. - // If not, we should have extra space above the keyboard to show the key preview. - mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE); + mInputLogic.onUpdateFullscreenMode(isFullscreenMode()); } private int getCurrentAutoCapsState() { @@ -1157,9 +1252,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (null == keyboard) { return CoordinateUtils.newCoordinateArray(codePoints.length, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } else { - return keyboard.getCoordinates(codePoints); } + return keyboard.getCoordinates(codePoints); } // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is @@ -1177,6 +1271,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen wordToEdit = word; } mDictionaryFacilitator.addWordToUserDictionary(this /* context */, wordToEdit); + mInputLogic.onAddWordToUserDictionary(); } // Callback for the {@link SuggestionStripView}, to call when the important notice strip is @@ -1365,7 +1460,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void setSuggestedWords(final SuggestedWords suggestedWords) { - mInputLogic.setSuggestedWords(suggestedWords); + final SettingsValues currentSettingsValues = mSettings.getCurrent(); + mInputLogic.setSuggestedWords(suggestedWords, currentSettingsValues, mHandler); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { return; @@ -1374,7 +1470,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final SettingsValues currentSettingsValues = mSettings.getCurrent(); final boolean shouldShowImportantNotice = ImportantNoticeUtils.shouldShowImportantNotice(this); final boolean shouldShowSuggestionCandidates = @@ -1394,26 +1489,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean isEmptyApplicationSpecifiedCompletions = currentSettingsValues.isApplicationSpecifiedCompletionsOn() && suggestedWords.isEmpty(); - final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords) + final boolean noSuggestionsFromDictionaries = (SuggestedWords.EMPTY == suggestedWords) || suggestedWords.isPunctuationSuggestions() || isEmptyApplicationSpecifiedCompletions; - if (shouldShowImportantNotice && noSuggestionsToShow) { + final boolean isBeginningOfSentencePrediction = (suggestedWords.mInputStyle + == SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION); + final boolean noSuggestionsToOverrideImportantNotice = noSuggestionsFromDictionaries + || isBeginningOfSentencePrediction; + if (shouldShowImportantNotice && noSuggestionsToOverrideImportantNotice) { if (mSuggestionStripView.maybeShowImportantNoticeTitle()) { return; } } if (currentSettingsValues.isSuggestionsEnabledPerUserSettings() - // We should clear suggestions if there is no suggestion to show. - || noSuggestionsToShow - || currentSettingsValues.isApplicationSpecifiedCompletionsOn()) { + || currentSettingsValues.isApplicationSpecifiedCompletionsOn() + // We should clear the contextual strip if there is no suggestion from dictionaries. + || noSuggestionsFromDictionaries) { mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } } // TODO[IL]: Move this out of LatinIME. - public void getSuggestedWords(final int sessionId, final int sequenceNumber, + public void getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); if (keyboard == null) { @@ -1421,7 +1520,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard.getProximityInfo(), - mKeyboardSwitcher.getKeyboardShiftMode(), sessionId, sequenceNumber, callback); + mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback); } @Override @@ -1505,7 +1604,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen default: // SHIFT_NO_UPDATE } if (inputTransaction.requiresUpdateSuggestions()) { - mHandler.postUpdateSuggestionStrip(); + final int inputStyle; + if (inputTransaction.mEvent.isSuggestionStripPress()) { + // Suggestion strip press: no input. + inputStyle = SuggestedWords.INPUT_STYLE_NONE; + } else if (inputTransaction.mEvent.isGesture()) { + inputStyle = SuggestedWords.INPUT_STYLE_TAIL_BATCH; + } else { + inputStyle = SuggestedWords.INPUT_STYLE_TYPING; + } + mHandler.postUpdateSuggestionStrip(inputStyle); } if (inputTransaction.didAffectContents()) { mSubtypeState.setCurrentSubtypeHasBeenUsed(); @@ -1568,6 +1676,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { + mSpecialKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1587,12 +1696,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public boolean onKeyUp(final int keyCode, final KeyEvent event) { - final long keyIdentifier = event.getDeviceId() << 32 + event.getKeyCode(); + public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { + mSpecialKeyDetector.onKeyUp(keyEvent); + if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { + return super.onKeyUp(keyCode, keyEvent); + } + final long keyIdentifier = keyEvent.getDeviceId() << 32 + keyEvent.getKeyCode(); if (mInputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) { return true; } - return super.onKeyUp(keyCode, event); + return super.onKeyUp(keyCode, keyEvent); } // onKeyDown and onKeyUp are the main events we are interested in. There are two more events @@ -1621,24 +1734,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mainKeyboardView != null) { mainKeyboardView.closing(); } - launchSubActivity(SettingsActivity.class); - } - - private void launchSubActivity(final Class<? extends Activity> activityClass) { - Intent intent = new Intent(); - intent.setClass(LatinIME.this, activityClass); + final Intent intent = new Intent(); + intent.setClass(LatinIME.this, SettingsActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(SettingsActivity.EXTRA_SHOW_HOME_AS_UP, false); startActivity(intent); } private void showSubtypeSelectorAndSettings() { final CharSequence title = getString(R.string.english_ime_input_options); + // TODO: Should use new string "Select active input modes". + final CharSequence languageSelectionTitle = getString(R.string.language_selection_title); final CharSequence[] items = new CharSequence[] { - // TODO: Should use new string "Select active input modes". - getString(R.string.language_selection_title), - getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)), + languageSelectionTitle, + getString(ApplicationUtils.getActivityTitleResId(this, SettingsActivity.class)) }; final OnClickListener listener = new OnClickListener() { @Override @@ -1651,6 +1762,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(Intent.EXTRA_TITLE, languageSelectionTitle); startActivity(intent); break; case 1: diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index 0fba37c8a..56014cbad 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -35,7 +35,7 @@ public final class PunctuationSuggestions extends SuggestedWords { false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, false /* isObsoleteSuggestions */, - false /* isPrediction */); + INPUT_STYLE_NONE /* inputStyle */); } /** diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index a6b3b710b..744b0321a 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -16,8 +16,13 @@ package com.android.inputmethod.latin; +import android.graphics.Color; import android.inputmethodservice.InputMethodService; +import android.os.Build; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.BackgroundColorSpan; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -25,7 +30,9 @@ import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; @@ -80,6 +87,18 @@ public final class RichInputConnection { */ private final StringBuilder mComposingText = new StringBuilder(); + /** + * This variable is a temporary object used in + * {@link #commitTextWithBackgroundColor(CharSequence, int, int)} to avoid object creation. + */ + private SpannableStringBuilder mTempObjectForCommitText = new SpannableStringBuilder(); + /** + * This variable is used to track whether the last committed text had the background color or + * not. + * TODO: Omit this flag if possible. + */ + private boolean mLastCommittedTextHasBackgroundColor = false; + private final InputMethodService mParent; InputConnection mIC; int mNestLevel; @@ -218,12 +237,39 @@ public final class RichInputConnection { // it works, but it's wrong and should be fixed. mCommittedTextBeforeComposingText.append(mComposingText); mComposingText.setLength(0); + // TODO: Clear this flag in setComposingRegion() and setComposingText() as well if needed. + mLastCommittedTextHasBackgroundColor = false; if (null != mIC) { mIC.finishComposingText(); } } - public void commitText(final CharSequence text, final int i) { + /** + * Synonym of {@code commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT}. + * @param text The text to commit. This may include styles. + * See {@link InputConnection#commitText(CharSequence, int)}. + * @param newCursorPosition The new cursor position around the text. + * See {@link InputConnection#commitText(CharSequence, int)}. + */ + public void commitText(final CharSequence text, final int newCursorPosition) { + commitTextWithBackgroundColor(text, newCursorPosition, Color.TRANSPARENT, text.length()); + } + + /** + * Calls {@link InputConnection#commitText(CharSequence, int)} with the given background color. + * @param text The text to commit. This may include styles. + * See {@link InputConnection#commitText(CharSequence, int)}. + * @param newCursorPosition The new cursor position around the text. + * See {@link InputConnection#commitText(CharSequence, int)}. + * @param color The background color to be attached. Set {@link Color#TRANSPARENT} to disable + * the background color. Note that this method specifies {@link BackgroundColorSpan} with + * {@link Spanned#SPAN_COMPOSING} flag, meaning that the background color persists until + * {@link #finishComposingText()} is called. + * @param coloredTextLength the length of text, in Java chars, which should be rendered with + * the given background color. + */ + public void commitTextWithBackgroundColor(final CharSequence text, final int newCursorPosition, + final int color, final int coloredTextLength) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(text); @@ -233,11 +279,44 @@ public final class RichInputConnection { mExpectedSelStart += text.length() - mComposingText.length(); mExpectedSelEnd = mExpectedSelStart; mComposingText.setLength(0); + mLastCommittedTextHasBackgroundColor = false; if (null != mIC) { - mIC.commitText(text, i); + if (color == Color.TRANSPARENT) { + mIC.commitText(text, newCursorPosition); + } else { + mTempObjectForCommitText.clear(); + mTempObjectForCommitText.append(text); + final BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(color); + final int spanLength = Math.min(coloredTextLength, text.length()); + mTempObjectForCommitText.setSpan(backgroundColorSpan, 0, spanLength, + Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + mIC.commitText(mTempObjectForCommitText, newCursorPosition); + mLastCommittedTextHasBackgroundColor = true; + } } } + /** + * Removes the background color from the highlighted text if necessary. Should be called while + * there is no on-going composing text. + * + * <p>CAVEAT: This method internally calls {@link InputConnection#finishComposingText()}. + * Be careful of any unexpected side effects.</p> + */ + public void removeBackgroundColorFromHighlightedTextIfNecessary() { + // TODO: We haven't yet full tested if we really need to check this flag or not. Omit this + // flag if everything works fine without this condition. + if (!mLastCommittedTextHasBackgroundColor) { + return; + } + if (mComposingText.length() > 0) { + Log.e(TAG, "clearSpansWithComposingFlags should be called when composing text is " + + "empty. mComposingText=" + mComposingText); + return; + } + finishComposingText(); + } + public CharSequence getSelectedText(final int flags) { return (null == mIC) ? null : mIC.getSelectedText(flags); } @@ -547,14 +626,24 @@ public final class RichInputConnection { return Arrays.binarySearch(sortedSeparators, code) >= 0; } + private static boolean isPartOfCompositionForScript(final int codePoint, + final SpacingAndPunctuations spacingAndPunctuations, final int scriptId) { + // We always consider word connectors part of compositions. + return spacingAndPunctuations.isWordConnector(codePoint) + // Otherwise, it's part of composition if it's part of script and not a separator. + || (!spacingAndPunctuations.isWordSeparator(codePoint) + && ScriptUtils.isLetterPartOfScript(codePoint, scriptId)); + } + /** * Returns the text surrounding the cursor. * - * @param sortedSeparators a sorted array of code points that split words. + * @param spacingAndPunctuations the rules for spacing and punctuation * @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_* * @return a range containing the text surrounding the cursor */ - public TextRange getWordRangeAtCursor(final int[] sortedSeparators, final int scriptId) { + public TextRange getWordRangeAtCursor(final SpacingAndPunctuations spacingAndPunctuations, + final int scriptId) { mIC = mParent.getCurrentInputConnection(); if (mIC == null) { return null; @@ -571,8 +660,7 @@ public final class RichInputConnection { int startIndexInBefore = before.length(); while (startIndexInBefore > 0) { final int codePoint = Character.codePointBefore(before, startIndexInBefore); - if (isSeparator(codePoint, sortedSeparators) - || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) { + if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) { break; } --startIndexInBefore; @@ -585,8 +673,7 @@ public final class RichInputConnection { int endIndexInAfter = -1; while (++endIndexInAfter < after.length()) { final int codePoint = Character.codePointAt(after, endIndexInAfter); - if (isSeparator(codePoint, sortedSeparators) - || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) { + if (!isPartOfCompositionForScript(codePoint, spacingAndPunctuations, scriptId)) { break; } if (Character.isSupplementaryCodePoint(codePoint)) { @@ -811,4 +898,60 @@ public final class RichInputConnection { public boolean isCursorPositionKnown() { return INVALID_CURSOR_POSITION != mExpectedSelStart; } + + /** + * Work around a bug that was present before Jelly Bean upon rotation. + * + * Before Jelly Bean, there is a bug where setComposingRegion and other committing + * functions on the input connection get ignored until the cursor moves. This method works + * around the bug by wiggling the cursor first, which reactivates the connection and has + * the subsequent methods work, then restoring it to its original position. + * + * On platforms on which this method is not present, this is a no-op. + */ + public void maybeMoveTheCursorAroundAndRestoreToWorkaroundABug() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + if (mExpectedSelStart > 0) { + mIC.setSelection(mExpectedSelStart - 1, mExpectedSelStart - 1); + } else { + mIC.setSelection(mExpectedSelStart + 1, mExpectedSelStart + 1); + } + mIC.setSelection(mExpectedSelStart, mExpectedSelEnd); + } + } + + private boolean mCursorAnchorInfoMonitorEnabled = false; + + /** + * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}. + * @param enableMonitor {@code true} to request the editor to call back the method whenever the + * cursor/anchor position is changed. + * @param requestImmediateCallback {@code true} to request the editor to call back the method + * as soon as possible to notify the current cursor/anchor position to the input method. + * @return {@code true} if the request is accepted. Returns {@code false} otherwise, which + * includes "not implemented" or "rejected" or "temporarily unavailable" or whatever which + * prevents the application from fulfilling the request. (TODO: Improve the API when it turns + * out that we actually need more detailed error codes) + */ + public boolean requestCursorUpdates(final boolean enableMonitor, + final boolean requestImmediateCallback) { + mIC = mParent.getCurrentInputConnection(); + final boolean scheduled; + if (null != mIC) { + scheduled = InputConnectionCompatUtils.requestCursorUpdates(mIC, enableMonitor, + requestImmediateCallback); + } else { + scheduled = false; + } + mCursorAnchorInfoMonitorEnabled = (scheduled && enableMonitor); + return scheduled; + } + + /** + * @return {@code true} if the application reported that the monitor mode of + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is currently enabled. + */ + public boolean isCursorAnchorInfoMonitorEnabled() { + return mCursorAnchorInfoMonitorEnabled; + } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index b8b6d6471..b03818c1d 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -40,13 +40,8 @@ public final class Suggest { // Session id for // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. // We are sharing the same ID between typing and gesture to save RAM footprint. - public static final int SESSION_TYPING = 0; - public static final int SESSION_GESTURE = 0; - - // TODO: rename this to CORRECTION_OFF - public static final int CORRECTION_NONE = 0; - // TODO: rename this to CORRECTION_ON - public static final int CORRECTION_FULL = 1; + public static final int SESSION_ID_TYPING = 0; + public static final int SESSION_ID_GESTURE = 0; // Close to -2**31 private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; @@ -75,14 +70,15 @@ public final class Suggest { public void getSuggestedWords(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, - final boolean isCorrectionEnabled, final int sessionId, final int sequenceNumber, + final boolean isCorrectionEnabled, final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { if (wordComposer.isBatchMode()) { getSuggestedWordsForBatchInput(wordComposer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, sessionId, sequenceNumber, callback); + settingsValuesForSuggestion, inputStyle, sequenceNumber, callback); } else { - getSuggestedWordsForTypingInput(wordComposer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, isCorrectionEnabled, sequenceNumber, callback); + getSuggestedWordsForNonBatchInput(wordComposer, prevWordsInfo, proximityInfo, + settingsValuesForSuggestion, inputStyle, isCorrectionEnabled, + sequenceNumber, callback); } } @@ -120,13 +116,13 @@ public final class Suggest { return firstSuggestedWordInfo.mWord; } - // Retrieves suggestions for the typing input + // Retrieves suggestions for non-batch input (typing, recorrection, predictions...) // and calls the callback function with the suggestions. - private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, + private void getSuggestedWordsForNonBatchInput(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, - final boolean isCorrectionEnabled, final int sequenceNumber, - final OnGetSuggestedWordsCallback callback) { + final int inputStyleIfNotPrediction, final boolean isCorrectionEnabled, + final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final String typedWord = wordComposer.getTypedWord(); final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWord); final String consideredWord = trailingSingleQuotesCount > 0 @@ -135,7 +131,7 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, - SESSION_TYPING); + SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount); @@ -190,6 +186,14 @@ public final class Suggest { suggestionsList = suggestionsContainer; } + final int inputStyle; + if (resultsArePredictions) { + inputStyle = suggestionResults.mIsBeginningOfSentence + ? SuggestedWords.INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION + : SuggestedWords.INPUT_STYLE_PREDICTION; + } else { + inputStyle = inputStyleIfNotPrediction; + } callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, suggestionResults.mRawSuggestions, // TODO: this first argument is lying. If this is a whitelisted word which is an @@ -197,7 +201,7 @@ public final class Suggest { // rename the attribute or change the value. !resultsArePredictions && !allowsToBeAutoCorrected /* typedWordValid */, hasAutoCorrection /* willAutoCorrect */, - false /* isObsoleteSuggestions */, resultsArePredictions, sequenceNumber)); + false /* isObsoleteSuggestions */, inputStyle, sequenceNumber)); } // Retrieves suggestions for the batch input @@ -205,10 +209,11 @@ public final class Suggest { private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, - final int sessionId, final int sequenceNumber, + final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, sessionId); + wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, + SESSION_ID_GESTURE); final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); @@ -241,12 +246,14 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). + // Note that because this method is never used to get predictions, there is no need to + // modify inputType such in getSuggestedWordsForNonBatchInput. callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, suggestionResults.mRawSuggestions, true /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - false /* isPrediction */, sequenceNumber)); + inputStyle, sequenceNumber)); } private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 5231cc893..1d221b77f 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin; import android.text.TextUtils; import android.view.inputmethod.CompletionInfo; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.StringUtils; @@ -31,12 +32,22 @@ public class SuggestedWords { public static final int INDEX_OF_AUTO_CORRECTION = 1; public static final int NOT_A_SEQUENCE_NUMBER = -1; + public static final int INPUT_STYLE_NONE = 0; + public static final int INPUT_STYLE_TYPING = 1; + public static final int INPUT_STYLE_UPDATE_BATCH = 2; + public static final int INPUT_STYLE_TAIL_BATCH = 3; + public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4; + public static final int INPUT_STYLE_RECORRECTION = 5; + public static final int INPUT_STYLE_PREDICTION = 6; + public static final int INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION = 7; + // The maximum number of suggestions available. public static final int MAX_SUGGESTIONS = 18; private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); public static final SuggestedWords EMPTY = new SuggestedWords( - EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false); + EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */, + false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE); public final String mTypedWord; public final boolean mTypedWordValid; @@ -45,7 +56,9 @@ public class SuggestedWords { // whether this exactly matches the user entry or not. public final boolean mWillAutoCorrect; public final boolean mIsObsoleteSuggestions; - public final boolean mIsPrediction; + // How the input for these suggested words was done by the user. Must be one of the + // INPUT_STYLE_* constants above. + public final int mInputStyle; public final int mSequenceNumber; // Sequence number for auto-commit. protected final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; public final ArrayList<SuggestedWordInfo> mRawSuggestions; @@ -55,9 +68,9 @@ public class SuggestedWords { final boolean typedWordValid, final boolean willAutoCorrect, final boolean isObsoleteSuggestions, - final boolean isPrediction) { + final int inputStyle) { this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect, - isObsoleteSuggestions, isPrediction, NOT_A_SEQUENCE_NUMBER); + isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, @@ -65,13 +78,12 @@ public class SuggestedWords { final boolean typedWordValid, final boolean willAutoCorrect, final boolean isObsoleteSuggestions, - final boolean isPrediction, + final int inputStyle, final int sequenceNumber) { this(suggestedWordInfoList, rawSuggestions, - (suggestedWordInfoList.isEmpty() || isPrediction) ? null + (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, - typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction, - sequenceNumber); + typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, @@ -80,14 +92,14 @@ public class SuggestedWords { final boolean typedWordValid, final boolean willAutoCorrect, final boolean isObsoleteSuggestions, - final boolean isPrediction, + final int inputStyle, final int sequenceNumber) { mSuggestedWordInfoList = suggestedWordInfoList; mRawSuggestions = rawSuggestions; mTypedWordValid = typedWordValid; mWillAutoCorrect = willAutoCorrect; mIsObsoleteSuggestions = isObsoleteSuggestions; - mIsPrediction = isPrediction; + mInputStyle = inputStyle; mSequenceNumber = sequenceNumber; mTypedWord = typedWord; } @@ -159,6 +171,7 @@ public class SuggestedWords { return "SuggestedWords:" + " mTypedWordValid=" + mTypedWordValid + " mWillAutoCorrect=" + mWillAutoCorrect + + " mInputStyle=" + mInputStyle + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); } @@ -365,9 +378,19 @@ public class SuggestedWords { } } + private static boolean isPrediction(final int inputStyle) { + return INPUT_STYLE_PREDICTION == inputStyle + || INPUT_STYLE_BEGINNING_OF_SENTENCE_PREDICTION == inputStyle; + } + + public boolean isPrediction() { + return isPrediction(mInputStyle); + } + // SuggestedWords is an immutable object, as much as possible. We must not just remove // words from the member ArrayList as some other parties may expect the object to never change. - public SuggestedWords getSuggestedWordsExcludingTypedWord() { + // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker. + public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() { final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); String typedWord = null; for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { @@ -383,7 +406,7 @@ public class SuggestedWords { // no auto-correction should take place hence willAutoCorrect = false. return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord, true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions, - mIsPrediction, NOT_A_SEQUENCE_NUMBER); + SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER); } // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the @@ -402,6 +425,20 @@ public class SuggestedWords { SuggestedWordInfo.NOT_A_CONFIDENCE)); } return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid, - mWillAutoCorrect, mIsObsoleteSuggestions, mIsPrediction); + mWillAutoCorrect, mIsObsoleteSuggestions, INPUT_STYLE_TAIL_BATCH); + } + + /** + * @return the {@link SuggestedWordInfo} which corresponds to the word that is originally + * typed by the user. Otherwise returns {@code null}. Note that gesture input is not + * considered to be a typed word. + */ + @UsedForTesting + public SuggestedWordInfo getTypedWordInfoOrNull() { + if (SuggestedWords.INDEX_OF_TYPED_WORD >= size()) { + return null; + } + final SuggestedWordInfo info = getInfo(SuggestedWords.INDEX_OF_TYPED_WORD); + return (info.getKind() == SuggestedWordInfo.KIND_TYPED) ? info : null; } } diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java index e4ee42660..123ab208c 100644 --- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java +++ b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java @@ -17,21 +17,16 @@ package com.android.inputmethod.latin; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.os.Process; -import android.preference.PreferenceManager; import android.util.Log; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.IntentCompatUtils; -import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; -import com.android.inputmethod.latin.setup.SetupActivity; import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; /** @@ -58,6 +53,9 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; * When a multiuser account has been created, {@link Intent#ACTION_USER_INITIALIZE} is received * by this receiver and it checks the whether the setup wizard's icon should be appeared or not on * the launcher depending on which partition this IME is installed. + * + * When the system locale has been changed, {@link Intent#ACTION_LOCALE_CHANGED} is received by + * this receiver and the {@link KeyboardLayoutSet}'s cache is cleared. */ public final class SystemBroadcastReceiver extends BroadcastReceiver { private static final String TAG = SystemBroadcastReceiver.class.getSimpleName(); @@ -67,21 +65,22 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver { final String intentAction = intent.getAction(); if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { Log.i(TAG, "Package has been replaced: " + context.getPackageName()); - } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { - Log.i(TAG, "Boot has been completed"); - } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { - Log.i(TAG, "User initialize"); - } - - LauncherIconVisibilityManager.onReceiveGlobalIntent(intentAction, context); - - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(intentAction)) { // Need to restore additional subtypes because system always clears additional // subtypes when the package is replaced. RichInputMethodManager.init(context); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(context); richImm.setAdditionalInputMethodSubtypes(additionalSubtypes); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { + Log.i(TAG, "Boot has been completed"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (IntentCompatUtils.is_ACTION_USER_INITIALIZE(intentAction)) { + Log.i(TAG, "User initialize"); + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); + } else if (Intent.ACTION_LOCALE_CHANGED.equals(intentAction)) { + Log.i(TAG, "System locale changed"); + KeyboardLayoutSet.onSystemLocaleChanged(); } // The process that hosts this broadcast receiver is invoked and remains alive even after diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java index debaad13e..21014b378 100644 --- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java @@ -253,12 +253,12 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary { final int frequency = cursor.getInt(indexFrequency); final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency); // Safeguard against adding really long words. - if (word.length() < MAX_WORD_LENGTH) { + if (word.length() <= MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(word, adjustedFrequency, null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, BinaryDictionary.NOT_A_VALID_TIMESTAMP); - if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) { + if (null != shortcut && shortcut.length() <= MAX_WORD_LENGTH) { runGCIfRequiredLocked(true /* mindsBlockByGC */); addUnigramLocked(shortcut, adjustedFrequency, word, USER_DICT_SHORTCUT_FREQUENCY, true /* isNotAWord */, diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index cdd782244..32d1fe372 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -25,6 +25,8 @@ import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Collections; +import javax.annotation.Nonnull; + /** * A place to store the currently composing word with information such as adjacent key codes as well */ @@ -175,20 +177,34 @@ public final class WordComposer { } /** - * Process an input event. + * Process an event and return an event, and return a processed event to apply. + * @param event the unprocessed event. + * @return the processed event. Never null, but may be marked as consumed. + */ + @Nonnull + public Event processEvent(final Event event) { + final Event processedEvent = mCombinerChain.processEvent(mEvents, event); + // The retained state of the combiner chain may have changed while processing the event, + // so we need to update our cache. + refreshTypedWordCache(); + mEvents.add(event); + return processedEvent; + } + + /** + * Apply a processed input event. * * All input events should be supported, including software/hardware events, characters as well * as deletions, multiple inputs and gestures. * - * @param event the event to process. + * @param event the event to apply. Must not be null. */ - public void processEvent(final Event event) { + public void applyProcessedEvent(final Event event) { + mCombinerChain.applyProcessedEvent(event); final int primaryCode = event.mCodePoint; final int keyX = event.mX; final int keyY = event.mY; final int newIndex = size(); - mCombinerChain.processEvent(mEvents, event); - mEvents.add(event); refreshTypedWordCache(); mCursorPositionWithinWord = mCodePointSize; // We may have deleted the last one. @@ -281,7 +297,9 @@ public final class WordComposer { final int codePoint = Character.codePointAt(word, i); // We don't want to override the batch input points that are held in mInputPointers // (See {@link #add(int,int,int)}). - processEvent(Event.createEventForCodePointFromUnknownSource(codePoint)); + final Event processedEvent = + processEvent(Event.createEventForCodePointFromUnknownSource(codePoint)); + applyProcessedEvent(processedEvent); } } @@ -295,9 +313,11 @@ public final class WordComposer { reset(); final int length = codePoints.length; for (int i = 0; i < length; ++i) { - processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], + final Event processedEvent = + processEvent(Event.createEventForCodePointFromAlreadyTypedText(codePoints[i], CoordinateUtils.xFromArray(coordinates, i), CoordinateUtils.yFromArray(coordinates, i))); + applyProcessedEvent(processedEvent); } mIsResumed = true; } diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java index 7071d8689..a87785b1a 100644 --- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java @@ -168,7 +168,7 @@ public class ExternalDictionaryGetterForDebug { } catch (IOException e) { // There was an error: show a dialog new AlertDialog.Builder(DialogUtils.getPlatformDialogThemeContext(context)) - .setTitle(R.string.error) + .setTitle(R.string.read_external_dictionary_error) .setMessage(e.toString()) .setPositiveButton(android.R.string.ok, new OnClickListener() { @Override diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 74d879919..fdab7f25f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -16,21 +16,29 @@ package com.android.inputmethod.latin.inputlogic; +import android.graphics.Color; +import android.inputmethodservice.InputMethodService; import android.os.SystemClock; import android.text.SpannableString; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.BackgroundColorSpan; import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.event.Event; import com.android.inputmethod.event.InputTransaction; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.keyboard.TextDecorator; +import com.android.inputmethod.keyboard.TextDecoratorUiOperator; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; @@ -45,6 +53,7 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.define.DebugFlags; +import com.android.inputmethod.latin.define.ProductionFlags; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; @@ -80,6 +89,14 @@ public final class InputLogic { public final Suggest mSuggest; private final DictionaryFacilitator mDictionaryFacilitator; + private final TextDecorator mTextDecorator = new TextDecorator(new TextDecorator.Listener() { + @Override + public void onClickComposingTextToAddToDictionary(final String word) { + mLatinIME.addWordToUserDictionary(word); + mLatinIME.dismissAddToDictionaryHint(); + } + }); + public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; // This has package visibility so it can be accessed from InputLogicHandler. /* package */ final WordComposer mWordComposer; @@ -123,8 +140,9 @@ public final class InputLogic { * Call this when input starts or restarts in some editor (typically, in onStartInputView). * * @param combiningSpec the combining spec string for this subtype + * @param settingsValues the current settings values */ - public void startInput(final String combiningSpec) { + public void startInput(final String combiningSpec, final SettingsValues settingsValues) { mEnteredText = null; mWordComposer.restartCombining(combiningSpec); resetComposingState(true /* alsoResetLastComposedWord */); @@ -142,15 +160,25 @@ public final class InputLogic { } else { mInputLogicHandler.reset(); } + + if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { + // AcceptTypedWord feature relies on CursorAnchorInfo. + if (settingsValues.mShouldShowUiToAcceptTypedWord) { + mConnection.requestCursorUpdates(true /* enableMonitor */, + true /* requestImmediateCallback */); + } + mTextDecorator.reset(); + } } /** * Call this when the subtype changes. * @param combiningSpec the spec string for the combining rules + * @param settingsValues the current settings values */ - public void onSubtypeChanged(final String combiningSpec) { + public void onSubtypeChanged(final String combiningSpec, final SettingsValues settingsValues) { finishInput(); - startInput(combiningSpec); + startInput(combiningSpec, settingsValues); } /** @@ -206,7 +234,7 @@ public final class InputLogic { final int keyboardShiftMode, // TODO: remove this argument final LatinIME.UIHandler handler) { - final String rawText = event.mText.toString(); + final String rawText = event.getTextToCommit().toString(); final InputTransaction inputTransaction = new InputTransaction(settingsValues, event, SystemClock.uptimeMillis(), mSpaceState, getActualCapsMode(settingsValues, keyboardShiftMode)); @@ -216,7 +244,7 @@ public final class InputLogic { } else { resetComposingState(true /* alsoResetLastComposedWord */); } - handler.postUpdateSuggestionStrip(); + handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_TYPING); final String text = performSpecificTldProcessingOnTextInput(rawText); if (SpaceState.PHANTOM == mSpaceState) { promotePhantomSpace(settingsValues); @@ -232,6 +260,20 @@ public final class InputLogic { } /** + * Determines whether "Touch again to save" should be shown or not. + * @param suggestionInfo the suggested word chosen by the user. + * @return {@code true} if we should show the "Touch again to save" hint. + */ + private boolean shouldShowAddToDictionaryHint(final SuggestedWordInfo suggestionInfo) { + // We should show the "Touch again to save" hint if the user pressed the first entry + // AND it's in none of our current dictionaries (main, user or otherwise). + return (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_TYPED) + || suggestionInfo.isKindOf(SuggestedWordInfo.KIND_OOV_CORRECTION)) + && !mDictionaryFacilitator.isValidWord(suggestionInfo.mWord, true /* ignoreCase */) + && mDictionaryFacilitator.isUserDictionaryEnabled(); + } + + /** * A suggestion was picked from the suggestion strip. * @param settingsValues the current values of the settings. * @param suggestionInfo the suggestion info. @@ -288,11 +330,9 @@ public final class InputLogic { return inputTransaction; } - // We need to log before we commit, because the word composer will store away the user - // typed word. - final String replacedWord = mWordComposer.getTypedWord(); - commitChosenWord(settingsValues, suggestion, - LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); + final boolean shouldShowAddToDictionaryHint = shouldShowAddToDictionaryHint(suggestionInfo); + commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, + LastComposedWord.NOT_A_SEPARATOR); mConnection.endBatchEdit(); // Don't allow cancellation of manual pick mLastComposedWord.deactivate(); @@ -300,18 +340,12 @@ public final class InputLogic { mSpaceState = SpaceState.PHANTOM; inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - // We should show the "Touch again to save" hint if the user pressed the first entry - // AND it's in none of our current dictionaries (main, user or otherwise). - final boolean showingAddToDictionaryHint = - (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_TYPED) - || suggestionInfo.isKindOf(SuggestedWordInfo.KIND_OOV_CORRECTION)) - && !mDictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */); - - if (showingAddToDictionaryHint && mDictionaryFacilitator.isUserDictionaryEnabled()) { + if (shouldShowAddToDictionaryHint) { mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. - handler.postUpdateSuggestionStrip(); + // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE. + handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } return inputTransaction; } @@ -324,10 +358,11 @@ public final class InputLogic { * @param oldSelEnd old selection end * @param newSelStart new selection start * @param newSelEnd new selection end + * @param settingsValues the current values of the settings. * @return whether the cursor has moved as a result of user interaction. */ public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd) { + final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) { if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) { return false; } @@ -352,8 +387,9 @@ public final class InputLogic { // should be true, but that is if the framework had taken that wrong cursor position // into account, which means we have to reset the entire composing state whenever there // is or was a selection regardless of whether it changed or not. - if (hasOrHadSelection || (selectionChangedOrSafeToReset - && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions() + || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -380,6 +416,11 @@ public final class InputLogic { // The cursor has been moved : we now accept to perform recapitalization mRecapitalizeStatus.enable(); + // We moved the cursor and need to invalidate the indicator right now. + mTextDecorator.reset(); + // Remaining background color that was used for the add-to-dictionary indicator should be + // removed. + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); // We moved the cursor. If we are touching a word, we need to resume suggestion. mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */, true /* shouldDelay */); @@ -405,128 +446,44 @@ public final class InputLogic { final int keyboardShiftMode, // TODO: remove these arguments final int currentKeyboardScriptId, final LatinIME.UIHandler handler) { - final InputTransaction inputTransaction = new InputTransaction(settingsValues, event, - SystemClock.uptimeMillis(), mSpaceState, + final Event processedEvent = mWordComposer.processEvent(event); + final InputTransaction inputTransaction = new InputTransaction(settingsValues, + processedEvent, SystemClock.uptimeMillis(), mSpaceState, getActualCapsMode(settingsValues, keyboardShiftMode)); - if (event.mKeyCode != Constants.CODE_DELETE + if (processedEvent.mKeyCode != Constants.CODE_DELETE || inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) { mDeleteCount = 0; } mLastKeyTime = inputTransaction.mTimestamp; mConnection.beginBatchEdit(); if (!mWordComposer.isComposingWord()) { + // TODO: is this useful? It doesn't look like it should be done here, but rather after + // a word is committed. mIsAutoCorrectionIndicatorOn = false; } // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state. - if (event.mCodePoint != Constants.CODE_SPACE) { + if (processedEvent.mCodePoint != Constants.CODE_SPACE) { cancelDoubleSpacePeriodCountdown(); } - boolean didAutoCorrect = false; - if (event.isFunctionalKeyEvent()) { - // A special key, like delete, shift, emoji, or the settings key. - switch (event.mKeyCode) { - case Constants.CODE_DELETE: - handleBackspace(inputTransaction, currentKeyboardScriptId); - // Backspace is a functional key, but it affects the contents of the editor. - inputTransaction.setDidAffectContents(); - break; - case Constants.CODE_SHIFT: - performRecapitalization(inputTransaction.mSettingsValues); - inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - if (mSuggestedWords.mIsPrediction) { - inputTransaction.setRequiresUpdateSuggestions(); - } - break; - case Constants.CODE_CAPSLOCK: - // Note: Changing keyboard to shift lock state is handled in - // {@link KeyboardSwitcher#onCodeInput(int)}. - break; - case Constants.CODE_SYMBOL_SHIFT: - // Note: Calling back to the keyboard on the symbol Shift key is handled in - // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. - break; - case Constants.CODE_SWITCH_ALPHA_SYMBOL: - // Note: Calling back to the keyboard on symbol key is handled in - // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. - break; - case Constants.CODE_SETTINGS: - onSettingsKeyPressed(); - break; - case Constants.CODE_SHORTCUT: - // We need to switch to the shortcut IME. This is handled by LatinIME since the - // input logic has no business with IME switching. - break; - case Constants.CODE_ACTION_NEXT: - performEditorAction(EditorInfo.IME_ACTION_NEXT); - break; - case Constants.CODE_ACTION_PREVIOUS: - performEditorAction(EditorInfo.IME_ACTION_PREVIOUS); - break; - case Constants.CODE_LANGUAGE_SWITCH: - handleLanguageSwitchKey(); - break; - case Constants.CODE_EMOJI: - // Note: Switching emoji keyboard is being handled in - // {@link KeyboardState#onCodeInput(int,int)}. - 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; - case Constants.CODE_SHIFT_ENTER: - // TODO: remove this object - final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER, - event.mKeyCode, event.mX, event.mY, event.isKeyRepeat()); - final InputTransaction tmpTransaction = new InputTransaction( - inputTransaction.mSettingsValues, tmpEvent, - inputTransaction.mTimestamp, inputTransaction.mSpaceState, - inputTransaction.mShiftState); - didAutoCorrect = handleNonSpecialCharacter(tmpTransaction, handler); - // Shift + Enter is treated as a functional key but it results in adding a new - // line, so that does affect the contents of the editor. - inputTransaction.setDidAffectContents(); - break; - default: - throw new RuntimeException("Unknown key code : " + event.mKeyCode); - } - } else { - inputTransaction.setDidAffectContents(); - switch (event.mCodePoint) { - case Constants.CODE_ENTER: - final EditorInfo editorInfo = getCurrentInputEditorInfo(); - final int imeOptionsActionId = - InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo); - if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) { - // Either we have an actionLabel and we should performEditorAction with - // actionId regardless of its value. - performEditorAction(editorInfo.actionId); - } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) { - // We didn't have an actionLabel, but we had another action to execute. - // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast, - // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it - // means there should be an action and the app didn't bother to set a specific - // code for it - presumably it only handles one. It does not have to be treated - // in any specific way: anything that is not IME_ACTION_NONE should be sent to - // performEditorAction. - performEditorAction(imeOptionsActionId); - } 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(inputTransaction, handler); - } - break; - default: - didAutoCorrect = handleNonSpecialCharacter(inputTransaction, handler); - break; + Event currentEvent = processedEvent; + while (null != currentEvent) { + if (currentEvent.isConsumed()) { + handleConsumedEvent(currentEvent, inputTransaction); + } else if (currentEvent.isFunctionalKeyEvent()) { + handleFunctionalEvent(currentEvent, inputTransaction, currentKeyboardScriptId, + handler); + } else { + handleNonFunctionalEvent(currentEvent, inputTransaction, handler); } + currentEvent = currentEvent.mNextEvent; } - if (!didAutoCorrect && event.mKeyCode != Constants.CODE_SHIFT - && event.mKeyCode != Constants.CODE_CAPSLOCK - && event.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL) + if (!inputTransaction.didAutoCorrect() && processedEvent.mKeyCode != Constants.CODE_SHIFT + && processedEvent.mKeyCode != Constants.CODE_CAPSLOCK + && processedEvent.mKeyCode != Constants.CODE_SWITCH_ALPHA_SYMBOL) mLastComposedWord.deactivate(); - if (Constants.CODE_DELETE != event.mKeyCode) { + if (Constants.CODE_DELETE != processedEvent.mKeyCode) { mEnteredText = null; } mConnection.endBatchEdit(); @@ -542,7 +499,9 @@ public final class InputLogic { handler.cancelUpdateSuggestionStrip(); ++mAutoCommitSequenceNumber; mConnection.beginBatchEdit(); - if (mWordComposer.isComposingWord()) { + if (!mWordComposer.isComposingWord()) { + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); + } else { 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. @@ -639,7 +598,8 @@ public final class InputLogic { // TODO: on the long term, this method should become private, but it will be difficult. // Especially, how do we deal with InputMethodService.onDisplayCompletions? - public void setSuggestedWords(final SuggestedWords suggestedWords) { + public void setSuggestedWords(final SuggestedWords suggestedWords, + final SettingsValues settingsValues, final LatinIME.UIHandler handler) { if (SuggestedWords.EMPTY != suggestedWords) { final String autoCorrection; if (suggestedWords.mWillAutoCorrect) { @@ -653,6 +613,7 @@ public final class InputLogic { } mSuggestedWords = suggestedWords; final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect; + // Put a blue underline to a word in TextView which will be auto-corrected. if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator && mWordComposer.isComposingWord()) { @@ -663,7 +624,154 @@ public final class InputLogic { // message, this is called outside any batch edit. Potentially, this may result in some // janky flickering of the screen, although the display speed makes it unlikely in // the practice. - mConnection.setComposingText(textWithUnderline, 1); + setComposingTextInternal(textWithUnderline, 1); + } + } + + /** + * Handle a consumed event. + * + * Consumed events represent events that have already been consumed, typically by the + * combining chain. + * + * @param event The event to handle. + * @param inputTransaction The transaction in progress. + */ + private void handleConsumedEvent(final Event event, final InputTransaction inputTransaction) { + // A consumed event may have text to commit and an update to the composing state, so + // we evaluate both. With some combiners, it's possible than an event contains both + // and we enter both of the following if clauses. + final CharSequence textToCommit = event.getTextToCommit(); + if (!TextUtils.isEmpty(textToCommit)) { + mConnection.commitText(textToCommit, 1); + inputTransaction.setDidAffectContents(); + } + if (mWordComposer.isComposingWord()) { + setComposingTextInternal(mWordComposer.getTypedWord(), 1); + inputTransaction.setDidAffectContents(); + inputTransaction.setRequiresUpdateSuggestions(); + } + } + + /** + * Handle a functional key event. + * + * A functional event is a special key, like delete, shift, emoji, or the settings key. + * Non-special keys are those that generate a single code point. + * This includes all letters, digits, punctuation, separators, emoji. It excludes keys that + * 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 event The event to handle. + * @param inputTransaction The transaction in progress. + */ + private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction, + // TODO: remove these arguments + final int currentKeyboardScriptId, final LatinIME.UIHandler handler) { + switch (event.mKeyCode) { + case Constants.CODE_DELETE: + handleBackspaceEvent(event, inputTransaction, currentKeyboardScriptId); + // Backspace is a functional key, but it affects the contents of the editor. + inputTransaction.setDidAffectContents(); + break; + case Constants.CODE_SHIFT: + performRecapitalization(inputTransaction.mSettingsValues); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + if (mSuggestedWords.isPrediction()) { + inputTransaction.setRequiresUpdateSuggestions(); + } + break; + case Constants.CODE_CAPSLOCK: + // Note: Changing keyboard to shift lock state is handled in + // {@link KeyboardSwitcher#onCodeInput(int)}. + break; + case Constants.CODE_SYMBOL_SHIFT: + // Note: Calling back to the keyboard on the symbol Shift key is handled in + // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. + break; + case Constants.CODE_SWITCH_ALPHA_SYMBOL: + // Note: Calling back to the keyboard on symbol key is handled in + // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}. + break; + case Constants.CODE_SETTINGS: + onSettingsKeyPressed(); + break; + case Constants.CODE_SHORTCUT: + // We need to switch to the shortcut IME. This is handled by LatinIME since the + // input logic has no business with IME switching. + break; + case Constants.CODE_ACTION_NEXT: + performEditorAction(EditorInfo.IME_ACTION_NEXT); + break; + case Constants.CODE_ACTION_PREVIOUS: + performEditorAction(EditorInfo.IME_ACTION_PREVIOUS); + break; + case Constants.CODE_LANGUAGE_SWITCH: + handleLanguageSwitchKey(); + break; + case Constants.CODE_EMOJI: + // Note: Switching emoji keyboard is being handled in + // {@link KeyboardState#onCodeInput(int,int)}. + 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; + case Constants.CODE_SHIFT_ENTER: + // TODO: remove this object + final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER, + event.mKeyCode, event.mX, event.mY, event.isKeyRepeat()); + handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler); + // Shift + Enter is treated as a functional key but it results in adding a new + // line, so that does affect the contents of the editor. + inputTransaction.setDidAffectContents(); + break; + default: + throw new RuntimeException("Unknown key code : " + event.mKeyCode); + } + } + + /** + * Handle an event that is not a functional event. + * + * These events are generally events that cause input, but in some cases they may do other + * things like trigger an editor action. + * + * @param event The event to handle. + * @param inputTransaction The transaction in progress. + */ + private void handleNonFunctionalEvent(final Event event, + final InputTransaction inputTransaction, + // TODO: remove this argument + final LatinIME.UIHandler handler) { + inputTransaction.setDidAffectContents(); + switch (event.mCodePoint) { + case Constants.CODE_ENTER: + final EditorInfo editorInfo = getCurrentInputEditorInfo(); + final int imeOptionsActionId = + InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo); + if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) { + // Either we have an actionLabel and we should performEditorAction with + // actionId regardless of its value. + performEditorAction(editorInfo.actionId); + } else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) { + // We didn't have an actionLabel, but we had another action to execute. + // EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast, + // EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it + // means there should be an action and the app didn't bother to set a specific + // code for it - presumably it only handles one. It does not have to be treated + // in any specific way: anything that is not IME_ACTION_NONE should be sent to + // performEditorAction. + performEditorAction(imeOptionsActionId); + } else { + // No action label, and the action from imeOptions is NONE: this is a regular + // enter key that should input a carriage return. + handleNonSpecialCharacterEvent(event, inputTransaction, handler); + } + break; + default: + handleNonSpecialCharacterEvent(event, inputTransaction, handler); + break; } } @@ -675,21 +783,29 @@ 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 event The event to handle. * @param inputTransaction The transaction in progress. - * @return whether this caused an auto-correction to happen. */ - private boolean handleNonSpecialCharacter(final InputTransaction inputTransaction, + private void handleNonSpecialCharacterEvent(final Event event, + final InputTransaction inputTransaction, // TODO: remove this argument final LatinIME.UIHandler handler) { - final int codePoint = inputTransaction.mEvent.mCodePoint; + if (!mWordComposer.isComposingWord()) { + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); + // In case the "add to dictionary" hint was still displayed. + // TODO: Do we really need to check if we have composing text here? + if (mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) { + mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); + mTextDecorator.reset(); + } + } + + final int codePoint = event.mCodePoint; mSpaceState = SpaceState.NONE; - final boolean didAutoCorrect; if (inputTransaction.mSettingsValues.isWordSeparator(codePoint) || Character.getType(codePoint) == Character.OTHER_SYMBOL) { - didAutoCorrect = handleSeparator(inputTransaction, - inputTransaction.mEvent.isSuggestionStripPress(), handler); + handleSeparatorEvent(event, inputTransaction, handler); } else { - didAutoCorrect = false; if (SpaceState.PHANTOM == inputTransaction.mSpaceState) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection @@ -700,22 +816,23 @@ public final class InputLogic { commitTyped(inputTransaction.mSettingsValues, LastComposedWord.NOT_A_SEPARATOR); } } - handleNonSeparator(inputTransaction.mSettingsValues, inputTransaction); + handleNonSeparatorEvent(event, inputTransaction.mSettingsValues, inputTransaction); } - return didAutoCorrect; } /** * Handle a non-separator. + * @param event The event to handle. * @param settingsValues The current settings values. * @param inputTransaction The transaction in progress. */ - private void handleNonSeparator(final SettingsValues settingsValues, + private void handleNonSeparatorEvent(final Event event, final SettingsValues settingsValues, final InputTransaction inputTransaction) { - final int codePoint = inputTransaction.mEvent.mCodePoint; + final int codePoint = event.mCodePoint; // 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. + // make it shorter (possibly cut into several pieces). Also factor + // handleNonSpecialCharacterEvent which has the same name as other handle* methods but is + // not the same. boolean isComposingWord = mWordComposer.isComposingWord(); // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead. @@ -762,41 +879,35 @@ public final class InputLogic { resetComposingState(false /* alsoResetLastComposedWord */); } if (isComposingWord) { - mWordComposer.processEvent(inputTransaction.mEvent); + mWordComposer.applyProcessedEvent(event); // If it's the first letter, make note of auto-caps state if (mWordComposer.isSingleLetter()) { mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState); } - mConnection.setComposingText(getTextWithUnderline( - mWordComposer.getTypedWord()), 1); + setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( - inputTransaction, inputTransaction.mEvent.isSuggestionStripPress()); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event, + inputTransaction); - if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { + if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) { mSpaceState = SpaceState.WEAK; } else { sendKeyCodePoint(settingsValues, codePoint); } - // In case the "add to dictionary" hint was still displayed. - mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); } inputTransaction.setRequiresUpdateSuggestions(); } /** * Handle input of a separator code point. + * @param event The event to handle. * @param inputTransaction The transaction in progress. - * @param isFromSuggestionStrip whether this code point comes from the suggestion strip. - * @return whether this caused an auto-correction to happen. */ - private boolean handleSeparator(final InputTransaction inputTransaction, - final boolean isFromSuggestionStrip, + private void handleSeparatorEvent(final Event event, final InputTransaction inputTransaction, // TODO: remove this argument final LatinIME.UIHandler handler) { - final int codePoint = inputTransaction.mEvent.mCodePoint; + final int codePoint = event.mCodePoint; final SettingsValues settingsValues = inputTransaction.mSettingsValues; - boolean didAutoCorrect = false; final boolean wasComposingWord = mWordComposer.isComposingWord(); // We avoid sending spaces in languages without spaces if we were composing. final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint @@ -814,15 +925,15 @@ public final class InputLogic { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR : StringUtils.newSingleCodePointString(codePoint); commitCurrentAutoCorrection(settingsValues, separator, handler); - didAutoCorrect = true; + inputTransaction.setDidAutoCorrect(); } else { commitTyped(settingsValues, StringUtils.newSingleCodePointString(codePoint)); } } - final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( - inputTransaction, isFromSuggestionStrip); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event, + inputTransaction); final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint && mConnection.isInsideDoubleQuoteOrAfterDigit(); @@ -846,10 +957,10 @@ public final class InputLogic { promotePhantomSpace(settingsValues); } - if (tryPerformDoubleSpacePeriod(inputTransaction)) { + if (tryPerformDoubleSpacePeriod(event, inputTransaction)) { mSpaceState = SpaceState.DOUBLE; inputTransaction.setRequiresUpdateSuggestions(); - } else if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { + } else if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) { mSpaceState = SpaceState.SWAP_PUNCTUATION; mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); } else if (Constants.CODE_SPACE == codePoint) { @@ -892,14 +1003,14 @@ public final class InputLogic { } inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - return didAutoCorrect; } /** * Handle a press on the backspace key. + * @param event The event to handle. * @param inputTransaction The transaction in progress. */ - private void handleBackspace(final InputTransaction inputTransaction, + private void handleBackspaceEvent(final Event event, final InputTransaction inputTransaction, // TODO: remove this argument, put it into settingsValues final int currentKeyboardScriptId) { mSpaceState = SpaceState.NONE; @@ -913,7 +1024,7 @@ public final class InputLogic { // Then again, even in the case of a key repeat, if the cursor is at start of text, it // can't go any further back, so we can update right away even if it's a key repeat. final int shiftUpdateKind = - inputTransaction.mEvent.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0 + event.isKeyRepeat() && mConnection.getExpectedSelectionStart() > 0 ? InputTransaction.SHIFT_UPDATE_LATER : InputTransaction.SHIFT_UPDATE_NOW; inputTransaction.requireShiftUpdate(shiftUpdateKind); @@ -933,17 +1044,17 @@ public final class InputLogic { mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion); } } else { - mWordComposer.processEvent(inputTransaction.mEvent); + mWordComposer.applyProcessedEvent(event); } if (mWordComposer.isComposingWord()) { - mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { mConnection.commitText("", 1); } inputTransaction.setRequiresUpdateSuggestions(); } else { if (mLastComposedWord.canRevertCommit()) { - revertCommit(inputTransaction); + revertCommit(inputTransaction, inputTransaction.mSettingsValues); return; } if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) { @@ -1052,16 +1163,18 @@ 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 event The event to handle. * @param inputTransaction The transaction in progress. * @return true if the swap has been performed, false if it was prevented by preliminary checks. */ - private boolean trySwapSwapperAndSpace(final InputTransaction inputTransaction) { + private boolean trySwapSwapperAndSpace(final Event event, + final InputTransaction inputTransaction) { final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); if (Constants.CODE_SPACE != codePointBeforeCursor) { return false; } mConnection.deleteSurroundingText(1, 0); - final String text = inputTransaction.mEvent.getTextToCommit() + " "; + final String text = event.getTextToCommit() + " "; mConnection.commitText(text, 1); inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); return true; @@ -1069,13 +1182,14 @@ public final class InputLogic { /* * Strip a trailing space if necessary and returns whether it's a swap weak space situation. + * @param event The event to handle. * @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 tryStripSpaceAndReturnWhetherShouldSwapInstead( - final InputTransaction inputTransaction, final boolean isFromSuggestionStrip) { - final int codePoint = inputTransaction.mEvent.mCodePoint; + private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead(final Event event, + final InputTransaction inputTransaction) { + final int codePoint = event.mCodePoint; + final boolean isFromSuggestionStrip = event.isSuggestionStripPress(); if (Constants.CODE_ENTER == codePoint && SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { mConnection.removeTrailingSpace(); @@ -1120,14 +1234,16 @@ public final class InputLogic { * these conditions are fulfilled, this method applies the transformation and returns true. * Otherwise, it does nothing and returns false. * + * @param event The event to handle. * @param inputTransaction The transaction in progress. * @return true if we applied the double-space-to-period transformation, false otherwise. */ - private boolean tryPerformDoubleSpacePeriod(final InputTransaction inputTransaction) { + private boolean tryPerformDoubleSpacePeriod(final Event event, + final InputTransaction inputTransaction) { // Check the setting, the typed character and the countdown. If any of the conditions is // not fulfilled, return false. if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod - || Constants.CODE_SPACE != inputTransaction.mEvent.mCodePoint + || Constants.CODE_SPACE != event.mCodePoint || !isDoubleSpacePeriodCountdownActive(inputTransaction)) { return false; } @@ -1235,7 +1351,8 @@ public final class InputLogic { prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive); } - public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) { + public void performUpdateSuggestionStripSync(final SettingsValues settingsValues, + final int inputStyle) { // Check if we have a suggestion engine attached. if (!settingsValues.needsToLookupSuggestions()) { if (mWordComposer.isComposingWord()) { @@ -1253,8 +1370,8 @@ public final class InputLogic { } final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>(); - mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, - SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { + mInputLogicHandler.getSuggestedWords(inputStyle, SuggestedWords.NOT_A_SEQUENCE_NUMBER, + new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { final String typedWord = mWordComposer.getTypedWord(); @@ -1315,12 +1432,11 @@ public final class InputLogic { if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) { // Show predictions. mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF); - mLatinIME.mHandler.postUpdateSuggestionStrip(); + mLatinIME.mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_RECORRECTION); return; } final TextRange range = mConnection.getWordRangeAtCursor( - settingsValues.mSpacingAndPunctuations.mSortedWordSeparators, - currentKeyboardScriptId); + settingsValues.mSpacingAndPunctuations, currentKeyboardScriptId); if (null == range) return; // Happens if we don't have an input connection at all if (range.length() <= 0) { // Race condition, or touching a word in a non-supported script. @@ -1373,13 +1489,14 @@ public final class InputLogic { mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); mWordComposer.setCursorPositionWithinWord( typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); + mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug(); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) { // If there weren't any suggestion spans on this word, suggestions#size() will be 1 // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we // have no useful suggestions, so we will try to compute some for it instead. - mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, + mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords( @@ -1389,10 +1506,10 @@ public final class InputLogic { && !shouldIncludeResumedWordInSuggestions) { // We were able to compute new suggestions for this word. // Remove the typed word, since we don't want to display it in this - // case. The #getSuggestedWordsExcludingTypedWord() method sets - // willAutoCorrect to false. + // case. The #getSuggestedWordsExcludingTypedWordForRecorrection() + // method sets willAutoCorrect to false. suggestedWords = suggestedWordsIncludingTypedWord - .getSuggestedWordsExcludingTypedWord(); + .getSuggestedWordsExcludingTypedWordForRecorrection(); } else { // No saved suggestions, and we were unable to compute any good one // either. Rather than displaying an empty suggestion strip, we'll @@ -1409,10 +1526,9 @@ public final class InputLogic { // color of the word in the suggestion strip changes according to this parameter, // and false gives the correct color. final SuggestedWords suggestedWords = new SuggestedWords(suggestions, - null /* rawSuggestions */, typedWord, - false /* typedWordValid */, false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, false /* isPrediction */, - SuggestedWords.NOT_A_SEQUENCE_NUMBER); + null /* rawSuggestions */, typedWord, false /* typedWordValid */, + false /* willAutoCorrect */, false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER); mIsAutoCorrectionIndicatorOn = false; mLatinIME.mHandler.showSuggestionStrip(suggestedWords); } @@ -1424,14 +1540,19 @@ public final class InputLogic { * This is triggered upon pressing backspace just after a commit with auto-correction. * * @param inputTransaction The transaction in progress. + * @param settingsValues the current values of the settings. */ - private void revertCommit(final InputTransaction inputTransaction) { + private void revertCommit(final InputTransaction inputTransaction, + final SettingsValues settingsValues) { final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; + final String originallyTypedWordString = + originallyTypedWord != null ? originallyTypedWord.toString() : ""; final CharSequence committedWord = mLastComposedWord.mCommittedWord; final String committedWordString = committedWord.toString(); final int cancelLength = committedWord.length(); + final String separatorString = mLastComposedWord.mSeparatorString; // We want java chars, not codepoints for the following. - final int separatorLength = mLastComposedWord.mSeparatorString.length(); + final int separatorLength = separatorString.length(); // TODO: should we check our saved separator against the actual contents of the text view? final int deleteLength = cancelLength + separatorLength; if (DebugFlags.DEBUG_ENABLED) { @@ -1450,7 +1571,7 @@ public final class InputLogic { if (!TextUtils.isEmpty(committedWord)) { mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString); } - final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; + final String stringToCommit = originallyTypedWord + separatorString; final SpannableString textToCommit = new SpannableString(stringToCommit); if (committedWord instanceof SpannableString) { final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord; @@ -1487,23 +1608,53 @@ public final class InputLogic { suggestions.toArray(new String[suggestions.size()]), 0 /* flags */), 0 /* start */, lastCharIndex /* end */, 0 /* flags */); } + + final boolean shouldShowAddToDictionaryForTypedWord = + shouldShowAddToDictionaryForTypedWord(mLastComposedWord, settingsValues); + if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) { // For languages with spaces, we revert to the typed string, but the cursor is still // after the separator so we don't resume suggestions. If the user wants to correct // the word, they have to press backspace again. - mConnection.commitText(textToCommit, 1); + if (shouldShowAddToDictionaryForTypedWord) { + mConnection.commitTextWithBackgroundColor(textToCommit, 1, + settingsValues.mTextHighlightColorForAddToDictionaryIndicator, + originallyTypedWordString.length()); + } else { + mConnection.commitText(textToCommit, 1); + } } else { // For languages without spaces, we revert the typed string but the cursor is flush // with the typed word, so we need to resume suggestions right away. final int[] codePoints = StringUtils.toCodePointArray(stringToCommit); mWordComposer.setComposingWord(codePoints, mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); - mConnection.setComposingText(textToCommit, 1); + if (shouldShowAddToDictionaryForTypedWord) { + setComposingTextInternalWithBackgroundColor(textToCommit, 1, + settingsValues.mTextHighlightColorForAddToDictionaryIndicator, + originallyTypedWordString.length()); + } else { + setComposingTextInternal(textToCommit, 1); + } } // Don't restart suggestion yet. We'll restart if the user deletes the separator. mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; - // We have a separator between the word and the cursor: we should show predictions. - inputTransaction.setRequiresUpdateSuggestions(); + + if (shouldShowAddToDictionaryForTypedWord) { + // Due to the API limitation as of L, we cannot reliably retrieve the reverted text + // when the separator causes line breaking. Until this API limitation is addressed in + // the framework, show the indicator only when the separator doesn't contain + // line-breaking characters. + if (!StringUtils.hasLineBreakCharacter(separatorString)) { + mTextDecorator.showAddToDictionaryIndicator(originallyTypedWordString, + mConnection.getExpectedSelectionStart(), + mConnection.getExpectedSelectionEnd()); + } + mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString); + } else { + // We have a separator between the word and the cursor: we should show predictions. + inputTransaction.setRequiresUpdateSuggestions(); + } } /** @@ -1708,7 +1859,7 @@ public final class InputLogic { SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - true /* isObsoleteSuggestions */, false /* isPrediction */); + true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle); } /** @@ -1831,10 +1982,10 @@ public final class InputLogic { } final String lastWord = batchInputText.substring(indexOfLastSpace); mWordComposer.setBatchInputWord(lastWord); - mConnection.setComposingText(lastWord, 1); + setComposingTextInternal(lastWord, 1); } else { mWordComposer.setBatchInputWord(batchInputText); - mConnection.setComposingText(batchInputText, 1); + setComposingTextInternal(batchInputText, 1); } mConnection.endBatchEdit(); // Space state must be updated before calling updateShiftState @@ -1891,7 +2042,15 @@ public final class InputLogic { // Complete any pending suggestions query first if (handler.hasPendingUpdateSuggestions()) { handler.cancelUpdateSuggestionStrip(); - performUpdateSuggestionStripSync(settingsValues); + // To know the input style here, we should retrieve the in-flight "update suggestions" + // message and read its arg1 member here. However, the Handler class does not let + // us retrieve this message, so we can't do that. But in fact, we notice that + // we only ever come here when the input style was typing. In the case of batch + // input, we update the suggestions synchronously when the tail batch comes. Likewise + // for application-specified completions. As for recorrections, we never auto-correct, + // so we don't come here either. Hence, the input style is necessarily + // INPUT_STYLE_TYPING. + performUpdateSuggestionStripSync(settingsValues, SuggestedWords.INPUT_STYLE_TYPING); } final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); @@ -1955,14 +2114,13 @@ public final class InputLogic { * This method handles the retry, and re-schedules a new retry if we still can't access. * We only retry up to 5 times before giving up. * - * @param settingsValues the current values of the settings. * @param tryResumeSuggestions Whether we should resume suggestions or not. * @param remainingTries How many times we may try again before giving up. * @return whether true if the caches were successfully reset, false otherwise. */ // TODO: make this private - public boolean retryResetCachesAndReturnSuccess(final SettingsValues settingsValues, - final boolean tryResumeSuggestions, final int remainingTries, + public boolean retryResetCachesAndReturnSuccess(final boolean tryResumeSuggestions, + final int remainingTries, // TODO: remove these arguments final LatinIME.UIHandler handler) { final boolean shouldFinishComposition = mConnection.hasSelection() @@ -1988,7 +2146,7 @@ public final class InputLogic { } public void getSuggestedWords(final SettingsValues settingsValues, - final ProximityInfo proximityInfo, final int keyboardShiftMode, final int sessionId, + final ProximityInfo proximityInfo, final int keyboardShiftMode, final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions( getActualCapsMode(settingsValues, keyboardShiftMode)); @@ -2004,6 +2162,124 @@ public final class InputLogic { settingsValues.mPhraseGestureEnabled, settingsValues.mAdditionalFeaturesSettingValues), settingsValues.mAutoCorrectionEnabledPerUserSettings, - sessionId, sequenceNumber, callback); + inputStyle, sequenceNumber, callback); + } + + /** + * Used as an injection point for each call of + * {@link RichInputConnection#setComposingText(CharSequence, int)}. + * + * <p>Currently using this method is optional and you can still directly call + * {@link RichInputConnection#setComposingText(CharSequence, int)}, but it is recommended to + * use this method whenever possible to optimize the behavior of {@link TextDecorator}.<p> + * <p>TODO: Should we move this mechanism to {@link RichInputConnection}?</p> + * + * @param newComposingText the composing text to be set + * @param newCursorPosition the new cursor position + */ + private void setComposingTextInternal(final CharSequence newComposingText, + final int newCursorPosition) { + setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition, + Color.TRANSPARENT, newComposingText.length()); + } + + /** + * Equivalent to {@link #setComposingTextInternal(CharSequence, int)} except that this method + * allows to set {@link BackgroundColorSpan} to the composing text with the given color. + * + * <p>TODO: Currently the background color is exclusive with the black underline, which is + * automatically added by the framework. We need to change the framework if we need to have both + * of them at the same time.</p> + * <p>TODO: Should we move this method to {@link RichInputConnection}?</p> + * + * @param newComposingText the composing text to be set + * @param newCursorPosition the new cursor position + * @param backgroundColor the background color to be set to the composing text. Set + * {@link Color#TRANSPARENT} to disable the background color. + * @param coloredTextLength the length of text, in Java chars, which should be rendered with + * the given background color. + */ + private void setComposingTextInternalWithBackgroundColor(final CharSequence newComposingText, + final int newCursorPosition, final int backgroundColor, final int coloredTextLength) { + final CharSequence composingTextToBeSet; + if (backgroundColor == Color.TRANSPARENT) { + composingTextToBeSet = newComposingText; + } else { + final SpannableString spannable = new SpannableString(newComposingText); + final BackgroundColorSpan backgroundColorSpan = + new BackgroundColorSpan(backgroundColor); + final int spanLength = Math.min(coloredTextLength, spannable.length()); + spannable.setSpan(backgroundColorSpan, 0, spanLength, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING); + composingTextToBeSet = spannable; + } + mConnection.setComposingText(composingTextToBeSet, newCursorPosition); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Following methods are tentatively placed in this class for the integration with + // TextDecorator. + // TODO: Decouple things that are not related to the input logic. + ////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Sets the UI operator for {@link TextDecorator}. + * @param uiOperator the UI operator which should be associated with {@link TextDecorator}. + */ + public void setTextDecoratorUi(final TextDecoratorUiOperator uiOperator) { + mTextDecorator.setUiOperator(uiOperator); + } + + /** + * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is + * called. + * @param info The wrapper object with which we can access cursor/anchor info. + */ + public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) { + mTextDecorator.onUpdateCursorAnchorInfo(info); + } + + /** + * Must be called when {@link InputMethodService#updateFullscreenMode} is called. + * @param isFullscreen {@code true} if the input method is in full-screen mode. + */ + public void onUpdateFullscreenMode(final boolean isFullscreen) { + mTextDecorator.notifyFullScreenMode(isFullscreen); + } + + /** + * Must be called from {@link LatinIME#addWordToUserDictionary(String)}. + */ + public void onAddWordToUserDictionary() { + mConnection.removeBackgroundColorFromHighlightedTextIfNecessary(); + mTextDecorator.reset(); + } + + /** + * Returns whether the add to dictionary indicator should be shown or not. + * @param lastComposedWord the last composed word information. + * @param settingsValues the current settings value. + * @return {@code true} if the commit indicator should be shown. + */ + private boolean shouldShowAddToDictionaryForTypedWord(final LastComposedWord lastComposedWord, + final SettingsValues settingsValues) { + if (!mConnection.isCursorAnchorInfoMonitorEnabled()) { + // We cannot help in this case because we are heavily relying on this new API. + return false; + } + if (!settingsValues.mShouldShowUiToAcceptTypedWord) { + return false; + } + if (TextUtils.isEmpty(lastComposedWord.mTypedWord)) { + return false; + } + if (TextUtils.equals(lastComposedWord.mTypedWord, lastComposedWord.mCommittedWord)) { + return false; + } + if (!mDictionaryFacilitator.isUserDictionaryEnabled()) { + return false; + } + return !mDictionaryFacilitator.isValidWord(lastComposedWord.mTypedWord, + true /* ignoreCase */); } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index 9dbe2c38b..c6f83d0b9 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -96,7 +96,7 @@ class InputLogicHandler implements Handler.Callback { public boolean handleMessage(final Message msg) { switch (msg.what) { case MSG_GET_SUGGESTED_WORDS: - mLatinIME.getSuggestedWords(msg.arg1 /* sessionId */, + mLatinIME.getSuggestedWords(msg.arg1 /* inputStyle */, msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj); break; } @@ -134,7 +134,8 @@ class InputLogicHandler implements Handler.Callback { return; } mInputLogic.mWordComposer.setBatchInputPointers(batchPointers); - getSuggestedWords(Suggest.SESSION_GESTURE, sequenceNumber, + getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH + : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(SuggestedWords suggestedWords) { @@ -205,9 +206,9 @@ class InputLogicHandler implements Handler.Callback { updateBatchInput(batchPointers, sequenceNumber, true /* isTailBatchInput */); } - public void getSuggestedWords(final int sessionId, final int sequenceNumber, + public void getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { mNonUIThreadHandler.obtainMessage( - MSG_GET_SUGGESTED_WORDS, sessionId, sequenceNumber, callback).sendToTarget(); + MSG_GET_SUGGESTED_WORDS, inputStyle, sequenceNumber, callback).sendToTarget(); } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index aac40940b..331f85e0e 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -138,6 +138,7 @@ public class PersonalizationHelper { final File filesDir = context.getFilesDir(); if (filesDir == null) { Log.e(TAG, "context.getFilesDir() returned null."); + return; } if (!FileUtils.deleteFilteredFiles(filesDir, new DictFilter(dictNamePrefix))) { Log.e(TAG, "Cannot remove all existing dictionary files. filesDir: " diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java index 8e027e4f9..34d4d4ed7 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java @@ -62,8 +62,8 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid, final int timestamp, final DistracterFilter distracterFilter) { final CharSequence prevWord = prevWordsInfo.mPrevWordsInfo[0].mWord; - if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH || - (prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) { + if (word.length() > Constants.DICTIONARY_MAX_WORD_LENGTH || + (prevWord != null && prevWord.length() > Constants.DICTIONARY_MAX_WORD_LENGTH)) { return; } final int frequency = isValid ? diff --git a/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java new file mode 100644 index 000000000..00f2c73dd --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/AdvancedSettingsFragment.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.media.AudioManager; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.TwoStatePreference; + +import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.define.ProductionFlags; +import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; + +/** + * "Advanced" settings sub screen. + * + * This settings sub screen handles the following advanced preferences. + * - Key popup dismiss delay + * - Keypress vibration duration + * - Keypress sound volume + * - Show app icon + * - Improve keyboard + * - Debug settings + */ +public final class AdvancedSettingsFragment extends SubScreenFragment { + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_advanced); + + final Resources res = getResources(); + final Context context = getActivity(); + + // When we are called from the Settings application but we are not already running, some + // singleton and utility classes may not have been initialized. We have to call + // initialization method of these classes here. See {@link LatinIME#onCreate()}. + AudioAndHapticFeedbackManager.init(context); + + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + + if (!Settings.isInternal(prefs)) { + removePreference(Settings.SCREEN_DEBUG); + } + + if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { + removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS); + } + + // TODO: consolidate key preview dismiss delay with the key preview animation parameters. + if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { + removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + } else { + // TODO: Cleanup this setup. + final ListPreference keyPreviewPopupDismissDelay = + (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( + R.integer.config_key_preview_linger_timeout)); + keyPreviewPopupDismissDelay.setEntries(new String[] { + res.getString(R.string.key_preview_popup_dismiss_no_delay), + res.getString(R.string.key_preview_popup_dismiss_default_delay), + }); + keyPreviewPopupDismissDelay.setEntryValues(new String[] { + "0", + popupDismissDelayDefaultValue + }); + if (null == keyPreviewPopupDismissDelay.getValue()) { + keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); + } + keyPreviewPopupDismissDelay.setEnabled( + Settings.readKeyPreviewPopupEnabled(prefs, res)); + } + + if (!res.getBoolean(R.bool.config_setup_wizard_available)) { + removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); + } + + if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) { + final Preference enableMetricsLogging = + findPreference(Settings.PREF_ENABLE_METRICS_LOGGING); + if (enableMetricsLogging != null) { + final int applicationLabelRes = context.getApplicationInfo().labelRes; + final String applicationName = res.getString(applicationLabelRes); + final String enableMetricsLoggingTitle = res.getString( + R.string.enable_metrics_logging, applicationName); + enableMetricsLogging.setTitle(enableMetricsLoggingTitle); + } + } else { + removePreference(Settings.PREF_ENABLE_METRICS_LOGGING); + } + + setupKeypressVibrationDurationSettings(); + setupKeypressSoundVolumeSettings(); + refreshEnablingsOfKeypressSoundAndVibrationSettings(); + } + + @Override + public void onResume() { + super.onResume(); + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final TwoStatePreference showSetupWizardIcon = + (TwoStatePreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); + if (showSetupWizardIcon != null) { + showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity())); + } + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + final Resources res = getResources(); + if (key.equals(Settings.PREF_POPUP_ON)) { + setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, + Settings.readKeyPreviewPopupEnabled(prefs, res)); + } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { + LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); + } + updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + refreshEnablingsOfKeypressSoundAndVibrationSettings(); + } + + private void refreshEnablingsOfKeypressSoundAndVibrationSettings() { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, + Settings.readVibrationEnabled(prefs, res)); + setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME, + Settings.readKeypressSoundEnabled(prefs, res)); + } + + private void setupKeypressVibrationDurationSettings() { + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( + Settings.PREF_VIBRATION_DURATION_SETTINGS); + if (pref == null) { + return; + } + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeypressVibrationDuration(prefs, res); + } + + @Override + public int readDefaultValue(final String key) { + return Settings.readDefaultKeypressVibrationDuration(res); + } + + @Override + public void feedbackValue(final int value) { + AudioAndHapticFeedbackManager.getInstance().vibrate(value); + } + + @Override + public String getValueText(final int value) { + if (value < 0) { + return res.getString(R.string.settings_system_default); + } + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + }); + } + + private void setupKeypressSoundVolumeSettings() { + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( + Settings.PREF_KEYPRESS_SOUND_VOLUME); + if (pref == null) { + return; + } + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE); + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return getPercentageFromValue(Settings.readKeypressSoundVolume(prefs, res)); + } + + @Override + public int readDefaultValue(final String key) { + return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res)); + } + + @Override + public String getValueText(final int value) { + if (value < 0) { + return res.getString(R.string.settings_system_default); + } + return Integer.toString(value); + } + + @Override + public void feedbackValue(final int value) { + am.playSoundEffect( + AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value)); + } + }); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java new file mode 100644 index 000000000..f5e4d33a2 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/AppearanceSettingsFragment.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.os.Bundle; + +import com.android.inputmethod.latin.R; + + +/** + * "Appearance" settings sub screen. + */ +public final class AppearanceSettingsFragment extends SubScreenFragment { + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_appearance); + } + + @Override + public void onResume() { + super.onResume(); + CustomInputStyleSettingsFragment.updateCustomInputStylesSummary( + findPreference(Settings.PREF_CUSTOM_INPUT_STYLES)); + ThemeSettingsFragment.updateKeyboardThemeSummary(findPreference(Settings.SCREEN_THEME)); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java new file mode 100644 index 000000000..ec29a7eb2 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/CorrectionSettingsFragment.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; + +import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.userdictionary.UserDictionaryList; +import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; + +import java.util.TreeSet; + +/** + * "Text correction" settings sub screen. + * + * This settings sub screen handles the following text correction preferences. + * - Personal dictionary + * - Add-on dictionaries + * - Block offensive words + * - Auto-correction + * - Show correction suggestions + * - Personalized suggestions + * - Suggest Contact names + * - Next-word suggestions + */ +public final class CorrectionSettingsFragment extends SubScreenFragment { + private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false; + private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS = + DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS + || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; + + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_correction); + + final Context context = getActivity(); + final PackageManager pm = context.getPackageManager(); + + ensureConsistencyOfAutoCorrectionSettings(); + + final Preference dictionaryLink = findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); + final Intent intent = dictionaryLink.getIntent(); + intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName()); + final int number = pm.queryIntentActivities(intent, 0).size(); + if (0 >= number) { + removePreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); + } + + final Preference editPersonalDictionary = + findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY); + final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent(); + final ResolveInfo ri = USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS ? null + : pm.resolveActivity( + editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY); + if (ri == null) { + overwriteUserDictionaryPreference(editPersonalDictionary); + } + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + ensureConsistencyOfAutoCorrectionSettings(); + } + + private void ensureConsistencyOfAutoCorrectionSettings() { + final String autoCorrectionOff = getString( + R.string.auto_correction_threshold_mode_index_off); + final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference( + Settings.PREF_AUTO_CORRECTION_THRESHOLD); + final String currentSetting = autoCorrectionThresholdPref.getValue(); + setPreferenceEnabled( + Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff)); + } + + private void overwriteUserDictionaryPreference(final Preference userDictionaryPreference) { + final Activity activity = getActivity(); + final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity); + if (null == localeList) { + // The locale list is null if and only if the user dictionary service is + // not present or disabled. In this case we need to remove the preference. + getPreferenceScreen().removePreference(userDictionaryPreference); + } else if (localeList.size() <= 1) { + userDictionaryPreference.setFragment(UserDictionarySettings.class.getName()); + // If the size of localeList is 0, we don't set the locale parameter in the + // extras. This will be interpreted by the UserDictionarySettings class as + // meaning "the current locale". + // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet() + // the locale list always has at least one element, since it always includes the current + // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet(). + if (localeList.size() == 1) { + final String locale = (String)localeList.toArray()[0]; + userDictionaryPreference.getExtras().putString("locale", locale); + } + } else { + userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); + } + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java index 21f2afd01..9bc398654 100644 --- a/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStyleSettingsFragment.java @@ -30,11 +30,15 @@ import android.preference.DialogPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceGroup; +import android.support.v4.view.ViewCompat; +import android.text.TextUtils; import android.util.Pair; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import android.widget.ArrayAdapter; @@ -43,6 +47,7 @@ import android.widget.SpinnerAdapter; import android.widget.Toast; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; +import com.android.inputmethod.compat.ViewCompatUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; @@ -63,7 +68,6 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { private AlertDialog mSubtypeEnablerNotificationDialog; private String mSubtypePreferenceKeyForSubtypeEnabler; - private static final int MENU_ADD_SUBTYPE = Menu.FIRST; private static final String KEY_IS_ADDING_NEW_SUBTYPE = "is_adding_new_subtype"; private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN = "is_subtype_enabler_notification_dialog_open"; @@ -234,6 +238,12 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); + // All keyboard layout names are in the Latin script and thus left to right. That means + // the view would align them to the left even if the system locale is RTL, but that + // would look strange. To fix this, we align them to the view's start, which will be + // natural for any direction. + ViewCompatUtils.setTextAlignment( + mKeyboardLayoutSetSpinner, ViewCompatUtils.TEXT_ALIGNMENT_VIEW_START); return v; } @@ -387,6 +397,25 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { // Empty constructor for fragment generation. } + static void updateCustomInputStylesSummary(final Preference pref) { + // When we are called from the Settings application but we are not already running, some + // singleton and utility classes may not have been initialized. We have to call + // initialization method of these classes here. See {@link LatinIME#onCreate()}. + SubtypeLocaleUtils.init(pref.getContext()); + + final Resources res = pref.getContext().getResources(); + final SharedPreferences prefs = pref.getSharedPreferences(); + final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); + final InputMethodSubtype[] subtypes = + AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); + final ArrayList<String> subtypeNames = new ArrayList<>(); + for (final InputMethodSubtype subtype : subtypes) { + subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); + } + // TODO: A delimiter of custom input styles should be localized. + pref.setSummary(TextUtils.join(", ", subtypeNames)); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -399,6 +428,16 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { } @Override + public View onCreateView(final LayoutInflater inflater, final ViewGroup container, + final Bundle savedInstanceState) { + final View view = super.onCreateView(inflater, container, savedInstanceState); + // For correct display in RTL locales, we need to set the layout direction of the + // fragment's top view. + ViewCompat.setLayoutDirection(view, ViewCompat.LAYOUT_DIRECTION_LOCALE); + return view; + } + + @Override public void onActivityCreated(final Bundle savedInstanceState) { final Context context = getActivity(); mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); @@ -423,7 +462,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { KEY_SUBTYPE_FOR_SUBTYPE_ENABLER); final SubtypePreference subtypePref = (SubtypePreference)findPreference( mSubtypePreferenceKeyForSubtypeEnabler); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); } } @@ -477,7 +516,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { if (findDuplicatedSubtype(subtype) == null) { mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); - mSubtypeEnablerNotificationDialog = createDialog(subtypePref); + mSubtypeEnablerNotificationDialog = createDialog(); mSubtypeEnablerNotificationDialog.show(); return; } @@ -514,7 +553,7 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { localeString, keyboardLayoutSetName); } - private AlertDialog createDialog(final SubtypePreference subtypePref) { + private AlertDialog createDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder( DialogUtils.getPlatformDialogThemeContext(getActivity())); builder.setTitle(R.string.custom_input_styles_title) @@ -581,14 +620,13 @@ public final class CustomInputStyleSettingsFragment extends PreferenceFragment { @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - final MenuItem addSubtypeMenu = menu.add(0, MENU_ADD_SUBTYPE, 0, R.string.add_style); - addSubtypeMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + inflater.inflate(R.menu.add_style, menu); } @Override public boolean onOptionsItemSelected(final MenuItem item) { final int itemId = item.getItemId(); - if (itemId == MENU_ADD_SUBTYPE) { + if (itemId == R.id.action_add_style) { final SubtypePreference newSubtype = SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy); getPreferenceScreen().addPreference(newSubtype); diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index e4271adac..48f4c758c 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -16,283 +16,31 @@ package com.android.inputmethod.latin.settings; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.os.Bundle; -import android.os.Process; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceFragment; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; -import android.preference.TwoStatePreference; - -import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver; -import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug; -import com.android.inputmethod.latin.utils.ApplicationUtils; -import com.android.inputmethod.latin.utils.ResourceUtils; - -public final class DebugSettings extends PreferenceFragment - implements SharedPreferences.OnSharedPreferenceChangeListener { - +public final class DebugSettings { public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; - public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = - "pref_key_preview_show_up_start_scale"; - public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = - "pref_key_preview_dismiss_end_scale"; + public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY = + "force_physical_keyboard_special_key"; + public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD = + "pref_show_ui_to_accept_typed_word"; + public static final String PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS = + "pref_has_custom_key_preview_animation_params"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE = + "pref_key_preview_show_up_start_x_scale"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE = + "pref_key_preview_show_up_start_y_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_X_SCALE = + "pref_key_preview_dismiss_end_x_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE = + "pref_key_preview_dismiss_end_y_scale"; public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION = "pref_key_preview_show_up_duration"; public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = "pref_key_preview_dismiss_duration"; - private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary"; - private static final String PREF_KEY_DUMP_DICTS = "pref_key_dump_dictionaries"; - private static final String PREF_KEY_DUMP_DICT_PREFIX = "pref_key_dump_dictionaries"; - private static final String DICT_NAME_KEY_FOR_EXTRAS = "dict_name"; public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview"; public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; - private boolean mServiceNeedsRestart = false; - private TwoStatePreference mDebugMode; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - addPreferencesFromResource(R.xml.prefs_screen_debug); - TwoStatePreferenceHelper.replaceCheckBoxPreferencesBySwitchPreferences( - getPreferenceScreen()); - SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - prefs.registerOnSharedPreferenceChangeListener(this); - - final PreferenceScreen readExternalDictionary = - (PreferenceScreen) findPreference(PREF_READ_EXTERNAL_DICTIONARY); - if (null != readExternalDictionary) { - readExternalDictionary.setOnPreferenceClickListener( - new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(final Preference arg0) { - ExternalDictionaryGetterForDebug.chooseAndInstallDictionary( - getActivity()); - mServiceNeedsRestart = true; - return true; - } - }); - } - - final PreferenceGroup dictDumpPreferenceGroup = - (PreferenceGroup)findPreference(PREF_KEY_DUMP_DICTS); - final OnPreferenceClickListener dictDumpPrefClickListener = - new DictDumpPrefClickListener(this); - for (final String dictName : DictionaryFacilitator.DICT_TYPE_TO_CLASS.keySet()) { - final Preference preference = new Preference(getActivity()); - preference.setKey(PREF_KEY_DUMP_DICT_PREFIX + dictName); - preference.setTitle("Dump " + dictName + " dictionary"); - preference.setOnPreferenceClickListener(dictDumpPrefClickListener); - preference.getExtras().putString(DICT_NAME_KEY_FOR_EXTRAS, dictName); - dictDumpPreferenceGroup.addPreference(preference); - } - final Resources res = getResources(); - setupKeyLongpressTimeoutSettings(prefs, res); - setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_DURATION, - res.getInteger(R.integer.config_key_preview_show_up_duration)); - setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_DISMISS_DURATION, - res.getInteger(R.integer.config_key_preview_dismiss_duration)); - setupKeyPreviewAnimationScale(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - setupKeyPreviewAnimationScale(prefs, res, PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); - - mServiceNeedsRestart = false; - mDebugMode = (TwoStatePreference) findPreference(PREF_DEBUG_MODE); - updateDebugMode(); - } - - private static class DictDumpPrefClickListener implements OnPreferenceClickListener { - final PreferenceFragment mPreferenceFragment; - - public DictDumpPrefClickListener(final PreferenceFragment preferenceFragment) { - mPreferenceFragment = preferenceFragment; - } - - @Override - public boolean onPreferenceClick(final Preference arg0) { - final String dictName = arg0.getExtras().getString(DICT_NAME_KEY_FOR_EXTRAS); - if (dictName != null) { - final Intent intent = - new Intent(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); - intent.putExtra(DictionaryDumpBroadcastReceiver.DICTIONARY_NAME_KEY, dictName); - mPreferenceFragment.getActivity().sendBroadcast(intent); - } - return true; - } - } - - @Override - public void onStop() { - super.onStop(); - if (mServiceNeedsRestart) { - Process.killProcess(Process.myPid()); - } - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { - if (key.equals(PREF_DEBUG_MODE) && mDebugMode != null) { - mDebugMode.setChecked(prefs.getBoolean(PREF_DEBUG_MODE, false)); - updateDebugMode(); - mServiceNeedsRestart = true; - return; - } - if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { - mServiceNeedsRestart = true; - return; - } - } - - private void updateDebugMode() { - if (mDebugMode == null) { - return; - } - boolean isDebugMode = mDebugMode.isChecked(); - final String version = getResources().getString( - R.string.version_text, ApplicationUtils.getVersionName(getActivity())); - if (!isDebugMode) { - mDebugMode.setTitle(version); - mDebugMode.setSummary(""); - } else { - mDebugMode.setTitle(getResources().getString(R.string.prefs_debug_mode)); - mDebugMode.setSummary(version); - } - } - - private void setupKeyLongpressTimeoutSettings(final SharedPreferences sp, - final Resources res) { - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( - PREF_KEY_LONGPRESS_TIMEOUT); - if (pref == null) { - return; - } - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - @Override - public void writeValue(final int value, final String key) { - sp.edit().putInt(key, value).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - sp.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return Settings.readKeyLongpressTimeout(sp, res); - } - - @Override - public int readDefaultValue(final String key) { - return Settings.readDefaultKeyLongpressTimeout(res); - } - - @Override - public String getValueText(final int value) { - return res.getString(R.string.abbreviation_unit_milliseconds, value); - } - - @Override - public void feedbackValue(final int value) {} - }); - } - - private void setupKeyPreviewAnimationScale(final SharedPreferences sp, final Resources res, - final String prefKey, final float defaultValue) { - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); - if (pref == null) { - return; - } - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - private static final float PERCENTAGE_FLOAT = 100.0f; - - private float getValueFromPercentage(final int percentage) { - return percentage / PERCENTAGE_FLOAT; - } - - private int getPercentageFromValue(final float floatValue) { - return (int)(floatValue * PERCENTAGE_FLOAT); - } - - @Override - public void writeValue(final int value, final String key) { - sp.edit().putFloat(key, getValueFromPercentage(value)).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - sp.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return getPercentageFromValue( - Settings.readKeyPreviewAnimationScale(sp, key, defaultValue)); - } - - @Override - public int readDefaultValue(final String key) { - return getPercentageFromValue(defaultValue); - } - - @Override - public String getValueText(final int value) { - if (value < 0) { - return res.getString(R.string.settings_system_default); - } - return String.format("%d%%", value); - } - - @Override - public void feedbackValue(final int value) {} - }); - } - - private void setupKeyPreviewAnimationDuration(final SharedPreferences sp, final Resources res, - final String prefKey, final int defaultValue) { - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); - if (pref == null) { - return; - } - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - @Override - public void writeValue(final int value, final String key) { - sp.edit().putInt(key, value).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - sp.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return Settings.readKeyPreviewAnimationDuration(sp, key, defaultValue); - } - - @Override - public int readDefaultValue(final String key) { - return defaultValue; - } - - @Override - public String getValueText(final int value) { - return res.getString(R.string.abbreviation_unit_milliseconds, value); - } - - @Override - public void feedbackValue(final int value) {} - }); + private DebugSettings() { + // This class is not publicly instantiable. } } diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java new file mode 100644 index 000000000..5640e2039 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Process; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceGroup; +import android.preference.TwoStatePreference; + +import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver; +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug; +import com.android.inputmethod.latin.utils.ApplicationUtils; +import com.android.inputmethod.latin.utils.ResourceUtils; + +import java.util.Locale; + +/** + * "Debug mode" settings sub screen. + * + * This settings sub screen handles a several preference options for debugging. + */ +public final class DebugSettingsFragment extends SubScreenFragment + implements OnPreferenceClickListener { + private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary"; + private static final String PREF_KEY_DUMP_DICTS = "pref_key_dump_dictionaries"; + private static final String PREF_KEY_DUMP_DICT_PREFIX = "pref_key_dump_dictionaries"; + + private boolean mServiceNeedsRestart = false; + private Preference mReadExternalDictionaryPref; + private TwoStatePreference mDebugMode; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_debug); + + if (!Settings.HAS_UI_TO_ACCEPT_TYPED_WORD) { + removePreference(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD); + } + + mReadExternalDictionaryPref = findPreference(PREF_READ_EXTERNAL_DICTIONARY); + if (mReadExternalDictionaryPref != null) { + mReadExternalDictionaryPref.setOnPreferenceClickListener(this); + } + + final PreferenceGroup dictDumpPreferenceGroup = + (PreferenceGroup)findPreference(PREF_KEY_DUMP_DICTS); + for (final String dictName : DictionaryFacilitator.DICT_TYPE_TO_CLASS.keySet()) { + final Preference pref = new DictDumpPreference(getActivity(), dictName); + pref.setOnPreferenceClickListener(this); + dictDumpPreferenceGroup.addPreference(pref); + } + final Resources res = getResources(); + setupKeyLongpressTimeoutSettings(); + setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, + res.getInteger(R.integer.config_key_preview_show_up_duration)); + setupKeyPreviewAnimationDuration(DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, + res.getInteger(R.integer.config_key_preview_dismiss_duration)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + setupKeyPreviewAnimationScale(DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); + + mServiceNeedsRestart = false; + mDebugMode = (TwoStatePreference) findPreference(DebugSettings.PREF_DEBUG_MODE); + updateDebugMode(); + } + + private static class DictDumpPreference extends Preference { + public final String mDictName; + + public DictDumpPreference(final Context context, final String dictName) { + super(context); + setKey(PREF_KEY_DUMP_DICT_PREFIX + dictName); + setTitle("Dump " + dictName + " dictionary"); + mDictName = dictName; + } + } + + @Override + public boolean onPreferenceClick(final Preference pref) { + final Context context = getActivity(); + if (pref == mReadExternalDictionaryPref) { + ExternalDictionaryGetterForDebug.chooseAndInstallDictionary(context); + mServiceNeedsRestart = true; + return true; + } + if (pref instanceof DictDumpPreference) { + final DictDumpPreference dictDumpPref = (DictDumpPreference)pref; + final String dictName = dictDumpPref.mDictName; + final Intent intent = new Intent( + DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); + intent.putExtra(DictionaryDumpBroadcastReceiver.DICTIONARY_NAME_KEY, dictName); + context.sendBroadcast(intent); + return true; + } + return true; + } + + @Override + public void onStop() { + super.onStop(); + if (mServiceNeedsRestart) { + Process.killProcess(Process.myPid()); + } + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + if (key.equals(DebugSettings.PREF_DEBUG_MODE) && mDebugMode != null) { + mDebugMode.setChecked(prefs.getBoolean(DebugSettings.PREF_DEBUG_MODE, false)); + updateDebugMode(); + mServiceNeedsRestart = true; + return; + } + if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH) + || key.equals(DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY)) { + mServiceNeedsRestart = true; + return; + } + } + + private void updateDebugMode() { + boolean isDebugMode = mDebugMode.isChecked(); + final String version = getString( + R.string.version_text, ApplicationUtils.getVersionName(getActivity())); + if (!isDebugMode) { + mDebugMode.setTitle(version); + mDebugMode.setSummary(null); + } else { + mDebugMode.setTitle(getString(R.string.prefs_debug_mode)); + mDebugMode.setSummary(version); + } + } + + private void setupKeyLongpressTimeoutSettings() { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( + DebugSettings.PREF_KEY_LONGPRESS_TIMEOUT); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeyLongpressTimeout(prefs, res); + } + + @Override + public int readDefaultValue(final String key) { + return Settings.readDefaultKeyLongpressTimeout(res); + } + + @Override + public String getValueText(final int value) { + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } + + private void setupKeyPreviewAnimationScale(final String prefKey, final float defaultValue) { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putFloat(key, getValueFromPercentage(value)).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return getPercentageFromValue( + Settings.readKeyPreviewAnimationScale(prefs, key, defaultValue)); + } + + @Override + public int readDefaultValue(final String key) { + return getPercentageFromValue(defaultValue); + } + + @Override + public String getValueText(final int value) { + if (value < 0) { + return res.getString(R.string.settings_system_default); + } + return String.format(Locale.ROOT, "%d%%", value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } + + private void setupKeyPreviewAnimationDuration(final String prefKey, final int defaultValue) { + final SharedPreferences prefs = getSharedPreferences(); + final Resources res = getResources(); + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + prefs.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + prefs.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeyPreviewAnimationDuration(prefs, key, defaultValue); + } + + @Override + public int readDefaultValue(final String key) { + return defaultValue; + } + + @Override + public String getValueText(final int value) { + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java new file mode 100644 index 000000000..832fbf65a --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/GestureSettingsFragment.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.content.SharedPreferences; +import android.os.Bundle; + +import com.android.inputmethod.latin.R; + +/** + * "Gesture typing preferences" settings sub screen. + * + * This settings sub screen handles the following gesture typing preferences. + * - Enable gesture typing + * - Dynamic floating preview + * - Show gesture trail + * - Phrase gesture + */ +public final class GestureSettingsFragment extends SubScreenFragment { + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_gesture); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java index f40106ba9..b073c50a4 100644 --- a/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/MultiLingualSettingsFragment.java @@ -16,71 +16,27 @@ package com.android.inputmethod.latin.settings; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; import android.os.Bundle; -import android.preference.PreferenceScreen; -import android.text.TextUtils; -import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; -import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.ArrayList; /** - * "Multi lingual options" settings sub screen. + * "Multilingual options" settings sub screen. * * This settings sub screen handles the following input preferences. * - Language switch key * - Switch to other input methods - * - Custom input styles */ public final class MultiLingualSettingsFragment extends SubScreenFragment { @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); - addPreferencesFromResource(R.xml.prefs_screen_multi_lingual); - - final Context context = getActivity(); - - // When we are called from the Settings application but we are not already running, some - // singleton and utility classes may not have been initialized. We have to call - // initialization method of these classes here. See {@link LatinIME#onCreate()}. - SubtypeLocaleUtils.init(context); - + addPreferencesFromResource(R.xml.prefs_screen_multilingual); if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) { removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY); removePreference(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST); } } - - @Override - public void onResume() { - super.onResume(); - updateCustomInputStylesSummary(); - } - - @Override - public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - // Nothing to do here. - } - - private void updateCustomInputStylesSummary() { - final SharedPreferences prefs = getSharedPreferences(); - final Resources res = getResources(); - final PreferenceScreen customInputStyles = - (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES); - final String prefSubtype = Settings.readPrefAdditionalSubtypes(prefs, res); - final InputMethodSubtype[] subtypes = - AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefSubtype); - final ArrayList<String> subtypeNames = new ArrayList<>(); - for (final InputMethodSubtype subtype : subtypes) { - subtypeNames.add(SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(subtype)); - } - // TODO: A delimiter of custom input styles should be localized. - customInputStyles.setSummary(TextUtils.join(", ", subtypeNames)); - } } diff --git a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java index f459d68dd..49db2bdc0 100644 --- a/java/src/com/android/inputmethod/latin/settings/InputSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java @@ -27,7 +27,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; /** - * "Input preferences" settings sub screen. + * "Preferences" settings sub screen. * * This settings sub screen handles the following input preferences. * - Auto-capitalization @@ -37,11 +37,11 @@ import com.android.inputmethod.latin.SubtypeSwitcher; * - Popup on keypress * - Voice input key */ -public final class InputSettingsFragment extends SubScreenFragment { +public final class PreferencesSettingsFragment extends SubScreenFragment { @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); - addPreferencesFromResource(R.xml.prefs_screen_input); + addPreferencesFromResource(R.xml.prefs_screen_preferences); final Resources res = getResources(); final Context context = getActivity(); diff --git a/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java b/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java new file mode 100644 index 000000000..c173d4706 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/RadioButtonPreference.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RadioButton; + +import com.android.inputmethod.latin.R; + +/** + * Radio Button preference + */ +public class RadioButtonPreference extends Preference { + interface OnRadioButtonClickedListener { + /** + * Called when this preference needs to be saved its state. + * + * @param preference This preference. + */ + public void onRadioButtonClicked(RadioButtonPreference preference); + } + + private boolean mIsSelected; + private RadioButton mRadioButton; + private OnRadioButtonClickedListener mListener; + private final View.OnClickListener mClickListener = new View.OnClickListener() { + @Override + public void onClick(final View v) { + if (mListener != null) { + mListener.onRadioButtonClicked(RadioButtonPreference.this); + } + } + }; + + public RadioButtonPreference(final Context context) { + this(context, null); + } + + public RadioButtonPreference(final Context context, final AttributeSet attrs) { + this(context, attrs, android.R.attr.preferenceStyle); + } + + public RadioButtonPreference(final Context context, final AttributeSet attrs, + final int defStyleAttr) { + super(context, attrs, defStyleAttr); + setWidgetLayoutResource(R.layout.radio_button_preference_widget); + } + + public void setOnRadioButtonClickedListener(final OnRadioButtonClickedListener listener) { + mListener = listener; + } + + @Override + protected void onBindView(final View view) { + super.onBindView(view); + mRadioButton = (RadioButton)view.findViewById(R.id.radio_button); + mRadioButton.setChecked(mIsSelected); + mRadioButton.setOnClickListener(mClickListener); + view.setOnClickListener(mClickListener); + } + + public boolean isSelected() { + return mIsSelected; + } + + public void setSelected(final boolean selected) { + if (selected == mIsSelected) { + return; + } + mIsSelected = selected; + if (mRadioButton != null) { + mRadioButton.setChecked(selected); + } + notifyChanged(); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 0e6a15a7e..0de2d8831 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -19,11 +19,13 @@ package com.android.inputmethod.latin.settings; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; +import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; @@ -40,8 +42,10 @@ import java.util.concurrent.locks.ReentrantLock; public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = Settings.class.getSimpleName(); // Settings screens - public static final String SCREEN_INPUT = "screen_input"; - public static final String SCREEN_MULTI_LINGUAL = "screen_multi_lingual"; + public static final String SCREEN_PREFERENCES = "screen_preferences"; + public static final String SCREEN_APPEARANCE = "screen_appearance"; + public static final String SCREEN_THEME = "screen_theme"; + public static final String SCREEN_MULTILINGUAL = "screen_multilingual"; public static final String SCREEN_GESTURE = "screen_gesture"; public static final String SCREEN_CORRECTION = "screen_correction"; public static final String SCREEN_ADVANCED = "screen_advanced"; @@ -66,10 +70,13 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang "pref_key_use_double_space_period"; public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = "pref_key_block_potentially_offensive"; + // No multilingual options in Android L and above for now. + public static final boolean SHOW_MULTILINGUAL_SETTINGS = + BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS = - (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) - || (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT - && Build.VERSION.CODENAME.equals("REL")); + BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; + public static final boolean HAS_UI_TO_ACCEPT_TYPED_WORD = + BuildCompatUtils.EFFECTIVE_SDK_INT >= BuildCompatUtils.VERSION_CODES_LXX; public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = "pref_show_language_switch_key"; public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = @@ -366,6 +373,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(PREF_SHOW_SETUP_WIZARD_ICON, false); } + public static boolean readHasHardwareKeyboard(final Configuration conf) { + // The standard way of finding out whether we have a hardware keyboard. This code is taken + // from InputMethodService#onEvaluateInputShown, which canonically determines this. + // In a nutshell, we have a keyboard if the configuration says the type of hardware keyboard + // is NOKEYS and if it's not hidden (e.g. folded inside the device). + return conf.keyboard != Configuration.KEYBOARD_NOKEYS + && conf.hardKeyboardHidden != Configuration.HARDKEYBOARDHIDDEN_YES; + } + public static boolean isInternal(final SharedPreferences prefs) { return prefs.getBoolean(PREF_KEY_IS_INTERNAL, false); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java index c7b9dcdd9..b0c494098 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java @@ -18,11 +18,36 @@ package com.android.inputmethod.latin.settings; import com.android.inputmethod.latin.utils.FragmentUtils; +import android.app.ActionBar; import android.content.Intent; +import android.os.Bundle; import android.preference.PreferenceActivity; +import android.view.MenuItem; public final class SettingsActivity extends PreferenceActivity { + public static final String EXTRA_SHOW_HOME_AS_UP = "show_home_as_up"; private static final String DEFAULT_FRAGMENT = SettingsFragment.class.getName(); + private boolean mShowHomeAsUp; + + @Override + protected void onCreate(final Bundle savedState) { + super.onCreate(savedState); + final ActionBar actionBar = getActionBar(); + if (actionBar != null) { + mShowHomeAsUp = getIntent().getBooleanExtra(EXTRA_SHOW_HOME_AS_UP, true); + actionBar.setDisplayHomeAsUpEnabled(mShowHomeAsUp); + actionBar.setHomeButtonEnabled(mShowHomeAsUp); + } + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + if (mShowHomeAsUp && item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } @Override public Intent getIntent() { @@ -36,7 +61,7 @@ public final class SettingsActivity extends PreferenceActivity { } @Override - public boolean isValidFragment(String fragmentName) { + public boolean isValidFragment(final String fragmentName) { return FragmentUtils.isValidFragment(fragmentName); } } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index f0bc27972..4fc17387f 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -16,81 +16,26 @@ package com.android.inputmethod.latin.settings; -import android.app.Activity; -import android.app.backup.BackupManager; -import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Resources; -import android.media.AudioManager; -import android.os.Build; import android.os.Bundle; -import android.preference.ListPreference; import android.preference.Preference; -import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; -import android.preference.TwoStatePreference; -import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; -import com.android.inputmethod.keyboard.KeyboardTheme; -import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.define.ProductionFlags; -import com.android.inputmethod.latin.setup.LauncherIconVisibilityManager; -import com.android.inputmethod.latin.userdictionary.UserDictionaryList; -import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.FeedbackUtils; import com.android.inputmethodcommon.InputMethodSettingsFragment; -import java.util.TreeSet; - -public final class SettingsFragment extends InputMethodSettingsFragment - implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = SettingsFragment.class.getSimpleName(); - private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false; - private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS = - DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS - || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; - - private static final int NO_MENU_GROUP = Menu.NONE; // We don't care about menu grouping. - private static final int MENU_FEEDBACK = Menu.FIRST; // The first menu item id and order. - private static final int MENU_ABOUT = Menu.FIRST + 1; // The second menu item id and order. - - private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { - final Preference preference = findPreference(preferenceKey); - if (preference != null) { - preference.setEnabled(enabled); - } - } - - private void updateListPreferenceSummaryToCurrentValue(final String prefKey) { - // Because the "%s" summary trick of {@link ListPreference} doesn't work properly before - // KitKat, we need to update the summary programmatically. - final ListPreference listPreference = (ListPreference)findPreference(prefKey); - if (listPreference == null) { - return; - } - final CharSequence entries[] = listPreference.getEntries(); - final int entryIndex = listPreference.findIndexOfValue(listPreference.getValue()); - listPreference.setSummary(entryIndex < 0 ? null : entries[entryIndex]); - } - - private static void removePreference(final String preferenceKey, final PreferenceGroup parent) { - if (parent == null) { - return; - } - final Preference preference = parent.findPreference(preferenceKey); - if (preference != null) { - parent.removePreference(preference); - } - } +public final class SettingsFragment extends InputMethodSettingsFragment { + // We don't care about menu grouping. + private static final int NO_MENU_GROUP = Menu.NONE; + // The first menu item id and order. + private static final int MENU_ABOUT = Menu.FIRST; + // The second menu item id and order. + private static final int MENU_HELP_AND_FEEDBACK = Menu.FIRST + 1; @Override public void onCreate(final Bundle icicle) { @@ -100,320 +45,19 @@ public final class SettingsFragment extends InputMethodSettingsFragment setSubtypeEnablerTitle(R.string.select_language); addPreferencesFromResource(R.xml.prefs); final PreferenceScreen preferenceScreen = getPreferenceScreen(); - TwoStatePreferenceHelper.replaceCheckBoxPreferencesBySwitchPreferences(preferenceScreen); preferenceScreen.setTitle( ApplicationUtils.getActivityTitleResId(getActivity(), SettingsActivity.class)); - - final Resources res = getResources(); - final Context context = getActivity(); - - // When we are called from the Settings application but we are not already running, some - // singleton and utility classes may not have been initialized. We have to call - // initialization method of these classes here. See {@link LatinIME#onCreate()}. - AudioAndHapticFeedbackManager.init(context); - - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - prefs.registerOnSharedPreferenceChangeListener(this); - - ensureConsistencyOfAutoCorrectionSettings(); - - final PreferenceScreen gestureScreen = - (PreferenceScreen) findPreference(Settings.SCREEN_GESTURE); - final PreferenceScreen correctionScreen = - (PreferenceScreen) findPreference(Settings.SCREEN_CORRECTION); - final PreferenceScreen advancedScreen = - (PreferenceScreen) findPreference(Settings.SCREEN_ADVANCED); - final PreferenceScreen debugScreen = - (PreferenceScreen) findPreference(Settings.SCREEN_DEBUG); - - if (!Settings.isInternal(prefs)) { - advancedScreen.removePreference(debugScreen); - } - - if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { - removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedScreen); - } - - // TODO: consolidate key preview dismiss delay with the key preview animation parameters. - if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { - removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedScreen); - } else { - // TODO: Cleanup this setup. - final ListPreference keyPreviewPopupDismissDelay = - (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( - R.integer.config_key_preview_linger_timeout)); - keyPreviewPopupDismissDelay.setEntries(new String[] { - res.getString(R.string.key_preview_popup_dismiss_no_delay), - res.getString(R.string.key_preview_popup_dismiss_default_delay), - }); - keyPreviewPopupDismissDelay.setEntryValues(new String[] { - "0", - popupDismissDelayDefaultValue - }); - if (null == keyPreviewPopupDismissDelay.getValue()) { - keyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); - } - keyPreviewPopupDismissDelay.setEnabled( - Settings.readKeyPreviewPopupEnabled(prefs, res)); - } - - if (!res.getBoolean(R.bool.config_setup_wizard_available)) { - removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedScreen); - } - - final PreferenceScreen dictionaryLink = - (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); - final Intent intent = dictionaryLink.getIntent(); - intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName()); - final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); - if (0 >= number) { - correctionScreen.removePreference(dictionaryLink); - } - - if (ProductionFlags.IS_METRICS_LOGGING_SUPPORTED) { - final Preference enableMetricsLogging = - findPreference(Settings.PREF_ENABLE_METRICS_LOGGING); - if (enableMetricsLogging != null) { - final int applicationLabelRes = context.getApplicationInfo().labelRes; - final String applicationName = res.getString(applicationLabelRes); - final String enableMetricsLoggingTitle = res.getString( - R.string.enable_metrics_logging, applicationName); - enableMetricsLogging.setTitle(enableMetricsLoggingTitle); - } - } else { - removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, advancedScreen); - } - - final Preference editPersonalDictionary = - findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY); - final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent(); - final ResolveInfo ri = USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS ? null - : context.getPackageManager().resolveActivity( - editPersonalDictionaryIntent, PackageManager.MATCH_DEFAULT_ONLY); - if (ri == null) { - overwriteUserDictionaryPreference(editPersonalDictionary); - } - - if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) { - getPreferenceScreen().removePreference(gestureScreen); - } - - AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this); - - setupKeypressVibrationDurationSettings(prefs, res); - setupKeypressSoundVolumeSettings(prefs, res); - refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); - } - - @Override - public void onResume() { - super.onResume(); - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - final Resources res = getResources(); - final TwoStatePreference showSetupWizardIcon = - (TwoStatePreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); - if (showSetupWizardIcon != null) { - showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity())); - } - updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - final ListPreference keyboardThemePref = (ListPreference)findPreference( - Settings.PREF_KEYBOARD_THEME); - if (keyboardThemePref != null) { - final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); - final String value = Integer.toString(keyboardTheme.mThemeId); - final CharSequence entries[] = keyboardThemePref.getEntries(); - final int entryIndex = keyboardThemePref.findIndexOfValue(value); - keyboardThemePref.setSummary(entryIndex < 0 ? null : entries[entryIndex]); - keyboardThemePref.setValue(value); - } - } - - @Override - public void onPause() { - super.onPause(); - final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); - final ListPreference keyboardThemePref = (ListPreference)findPreference( - Settings.PREF_KEYBOARD_THEME); - if (keyboardThemePref != null) { - KeyboardTheme.saveKeyboardThemeId(keyboardThemePref.getValue(), prefs); - } - } - - @Override - public void onDestroy() { - getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( - this); - super.onDestroy(); - } - - @Override - public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - final Activity activity = getActivity(); - if (activity == null) { - // TODO: Introduce a static function to register this class and ensure that - // onCreate must be called before "onSharedPreferenceChanged" is called. - Log.w(TAG, "onSharedPreferenceChanged called before activity starts."); - return; - } - (new BackupManager(activity)).dataChanged(); - final Resources res = getResources(); - if (key.equals(Settings.PREF_POPUP_ON)) { - setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, - Settings.readKeyPreviewPopupEnabled(prefs, res)); - } else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) { - LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity()); - } - ensureConsistencyOfAutoCorrectionSettings(); - updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); - updateListPreferenceSummaryToCurrentValue(Settings.PREF_KEYBOARD_THEME); - refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); - } - - private void ensureConsistencyOfAutoCorrectionSettings() { - final String autoCorrectionOff = getResources().getString( - R.string.auto_correction_threshold_mode_index_off); - final ListPreference autoCorrectionThresholdPref = (ListPreference)findPreference( - Settings.PREF_AUTO_CORRECTION_THRESHOLD); - final String currentSetting = autoCorrectionThresholdPref.getValue(); - setPreferenceEnabled( - Settings.PREF_BIGRAM_PREDICTIONS, !currentSetting.equals(autoCorrectionOff)); - } - - private void refreshEnablingsOfKeypressSoundAndVibrationSettings( - final SharedPreferences sp, final Resources res) { - setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS, - Settings.readVibrationEnabled(sp, res)); - setPreferenceEnabled(Settings.PREF_KEYPRESS_SOUND_VOLUME, - Settings.readKeypressSoundEnabled(sp, res)); - } - - private void setupKeypressVibrationDurationSettings(final SharedPreferences sp, - final Resources res) { - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( - Settings.PREF_VIBRATION_DURATION_SETTINGS); - if (pref == null) { - return; - } - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - @Override - public void writeValue(final int value, final String key) { - sp.edit().putInt(key, value).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - sp.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return Settings.readKeypressVibrationDuration(sp, res); - } - - @Override - public int readDefaultValue(final String key) { - return Settings.readDefaultKeypressVibrationDuration(res); - } - - @Override - public void feedbackValue(final int value) { - AudioAndHapticFeedbackManager.getInstance().vibrate(value); - } - - @Override - public String getValueText(final int value) { - if (value < 0) { - return res.getString(R.string.settings_system_default); - } - return res.getString(R.string.abbreviation_unit_milliseconds, value); - } - }); - } - - private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) { - final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference( - Settings.PREF_KEYPRESS_SOUND_VOLUME); - if (pref == null) { - return; - } - final AudioManager am = (AudioManager)getActivity().getSystemService(Context.AUDIO_SERVICE); - pref.setInterface(new SeekBarDialogPreference.ValueProxy() { - private static final float PERCENTAGE_FLOAT = 100.0f; - - private float getValueFromPercentage(final int percentage) { - return percentage / PERCENTAGE_FLOAT; - } - - private int getPercentageFromValue(final float floatValue) { - return (int)(floatValue * PERCENTAGE_FLOAT); - } - - @Override - public void writeValue(final int value, final String key) { - sp.edit().putFloat(key, getValueFromPercentage(value)).apply(); - } - - @Override - public void writeDefaultValue(final String key) { - sp.edit().remove(key).apply(); - } - - @Override - public int readValue(final String key) { - return getPercentageFromValue(Settings.readKeypressSoundVolume(sp, res)); - } - - @Override - public int readDefaultValue(final String key) { - return getPercentageFromValue(Settings.readDefaultKeypressSoundVolume(res)); - } - - @Override - public String getValueText(final int value) { - if (value < 0) { - return res.getString(R.string.settings_system_default); - } - return Integer.toString(value); - } - - @Override - public void feedbackValue(final int value) { - am.playSoundEffect( - AudioManager.FX_KEYPRESS_STANDARD, getValueFromPercentage(value)); - } - }); - } - - private void overwriteUserDictionaryPreference(Preference userDictionaryPreference) { - final Activity activity = getActivity(); - final TreeSet<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity); - if (null == localeList) { - // The locale list is null if and only if the user dictionary service is - // not present or disabled. In this case we need to remove the preference. - getPreferenceScreen().removePreference(userDictionaryPreference); - } else if (localeList.size() <= 1) { - userDictionaryPreference.setFragment(UserDictionarySettings.class.getName()); - // If the size of localeList is 0, we don't set the locale parameter in the - // extras. This will be interpreted by the UserDictionarySettings class as - // meaning "the current locale". - // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesSet() - // the locale list always has at least one element, since it always includes the current - // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesSet(). - if (localeList.size() == 1) { - final String locale = (String)localeList.toArray()[0]; - userDictionaryPreference.getExtras().putString("locale", locale); - } - } else { - userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); + if (!Settings.SHOW_MULTILINGUAL_SETTINGS) { + final Preference multilingualOptions = findPreference(Settings.SCREEN_MULTILINGUAL); + preferenceScreen.removePreference(multilingualOptions); } } @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - if (FeedbackUtils.isFeedbackFormSupported()) { - menu.add(NO_MENU_GROUP, MENU_FEEDBACK /* itemId */, MENU_FEEDBACK /* order */, - R.string.send_feedback); + if (FeedbackUtils.isHelpAndFeedbackFormSupported()) { + menu.add(NO_MENU_GROUP, MENU_HELP_AND_FEEDBACK /* itemId */, + MENU_HELP_AND_FEEDBACK /* order */, R.string.help_and_feedback); } final int aboutResId = FeedbackUtils.getAboutKeyboardTitleResId(); if (aboutResId != 0) { @@ -424,8 +68,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment @Override public boolean onOptionsItemSelected(final MenuItem item) { final int itemId = item.getItemId(); - if (itemId == MENU_FEEDBACK) { - FeedbackUtils.showFeedbackForm(getActivity()); + if (itemId == MENU_HELP_AND_FEEDBACK) { + FeedbackUtils.showHelpAndFeedbackForm(getActivity()); return true; } if (itemId == MENU_ABOUT) { diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 39e834f84..d8c548d8b 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -50,9 +50,12 @@ public final class SettingsValues { // From resources: public final SpacingAndPunctuations mSpacingAndPunctuations; - public final int mDelayUpdateOldSuggestions; + public final int mDelayInMillisecondsToUpdateOldSuggestions; public final long mDoubleSpacePeriodTimeout; - + // From configuration: + public final Locale mLocale; + public final boolean mHasHardwareKeyboard; + public final int mDisplayOrientation; // From preferences, in the same order as xml/prefs.xml: public final boolean mAutoCap; public final boolean mVibrateOn; @@ -73,8 +76,8 @@ public final class SettingsValues { public final boolean mSlidingKeyInputPreviewEnabled; public final boolean mPhraseGestureEnabled; public final int mKeyLongpressTimeout; - public final Locale mLocale; public final boolean mEnableMetricsLogging; + public final boolean mShouldShowUiToAcceptTypedWord; // From the input box public final InputAttributes mInputAttributes; @@ -87,25 +90,31 @@ public final class SettingsValues { public final float mAutoCorrectionThreshold; public final boolean mAutoCorrectionEnabledPerUserSettings; private final boolean mSuggestionsEnabledPerUserSettings; - public final int mDisplayOrientation; private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds; // Setting values for additional features public final int[] mAdditionalFeaturesSettingValues = new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE]; + // TextDecorator + public final int mTextHighlightColorForAddToDictionaryIndicator; + // Debug settings public final boolean mIsInternal; + public final boolean mHasCustomKeyPreviewAnimationParams; public final int mKeyPreviewShowUpDuration; public final int mKeyPreviewDismissDuration; - public final float mKeyPreviewShowUpStartScale; - public final float mKeyPreviewDismissEndScale; + public final float mKeyPreviewShowUpStartXScale; + public final float mKeyPreviewShowUpStartYScale; + public final float mKeyPreviewDismissEndXScale; + public final float mKeyPreviewDismissEndYScale; public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, final InputAttributes inputAttributes) { mLocale = res.getConfiguration().locale; // Get the resources - mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); + mDelayInMillisecondsToUpdateOldSuggestions = + res.getInteger(R.integer.config_delay_in_milliseconds_to_update_old_suggestions); mSpacingAndPunctuations = new SpacingAndPunctuations(res); // Store the input attributes @@ -136,12 +145,16 @@ public final class SettingsValues { ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */; mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true); mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true); - mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true); + mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true) + && inputAttributes.mIsGeneralTextInput; mBlockPotentiallyOffensive = Settings.readBlockPotentiallyOffensive(prefs, res); mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res); mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout); + mHasHardwareKeyboard = Settings.readHasHardwareKeyboard(res.getConfiguration()); mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true); + mShouldShowUiToAcceptTypedWord = Settings.HAS_UI_TO_ACCEPT_TYPED_WORD + && prefs.getBoolean(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD, true); // Compute other readable settings mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res); mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); @@ -159,21 +172,33 @@ public final class SettingsValues { mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs); AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray( prefs, mAdditionalFeaturesSettingValues); + mTextHighlightColorForAddToDictionaryIndicator = res.getColor( + R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color); mIsInternal = Settings.isInternal(prefs); + mHasCustomKeyPreviewAnimationParams = prefs.getBoolean( + DebugSettings.PREF_HAS_CUSTOM_KEY_PREVIEW_ANIMATION_PARAMS, false); mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, res.getInteger(R.integer.config_key_preview_show_up_duration)); mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration( prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, res.getInteger(R.integer.config_key_preview_dismiss_duration)); - mKeyPreviewShowUpStartScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_show_up_start_scale)); - mKeyPreviewDismissEndScale = Settings.readKeyPreviewAnimationScale( - prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, - ResourceUtils.getFloatFromFraction( - res, R.fraction.config_key_preview_dismiss_end_scale)); + final float defaultKeyPreviewShowUpStartScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale); + final float defaultKeyPreviewDismissEndScale = ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale); + mKeyPreviewShowUpStartXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_X_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewShowUpStartYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_Y_SCALE, + defaultKeyPreviewShowUpStartScale); + mKeyPreviewDismissEndXScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_X_SCALE, + defaultKeyPreviewDismissEndScale); + mKeyPreviewDismissEndYScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_Y_SCALE, + defaultKeyPreviewDismissEndScale); mDisplayOrientation = res.getConfiguration().orientation; mAppWorkarounds = new AsyncResultHolder<>(); final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo( @@ -329,8 +354,8 @@ public final class SettingsValues { final StringBuilder sb = new StringBuilder("Current settings :"); sb.append("\n mSpacingAndPunctuations = "); sb.append("" + mSpacingAndPunctuations.dump()); - sb.append("\n mDelayUpdateOldSuggestions = "); - sb.append("" + mDelayUpdateOldSuggestions); + sb.append("\n mDelayInMillisecondsToUpdateOldSuggestions = "); + sb.append("" + mDelayInMillisecondsToUpdateOldSuggestions); sb.append("\n mAutoCap = "); sb.append("" + mAutoCap); sb.append("\n mVibrateOn = "); @@ -392,16 +417,22 @@ public final class SettingsValues { sb.append("" + (null == awu ? "null" : awu.toString())); sb.append("\n mAdditionalFeaturesSettingValues = "); sb.append("" + Arrays.toString(mAdditionalFeaturesSettingValues)); + sb.append("\n mTextHighlightColorForAddToDictionaryIndicator = "); + sb.append("" + mTextHighlightColorForAddToDictionaryIndicator); sb.append("\n mIsInternal = "); sb.append("" + mIsInternal); sb.append("\n mKeyPreviewShowUpDuration = "); sb.append("" + mKeyPreviewShowUpDuration); sb.append("\n mKeyPreviewDismissDuration = "); sb.append("" + mKeyPreviewDismissDuration); - sb.append("\n mKeyPreviewShowUpStartScale = "); - sb.append("" + mKeyPreviewShowUpStartScale); - sb.append("\n mKeyPreviewDismissEndScale = "); - sb.append("" + mKeyPreviewDismissEndScale); + sb.append("\n mKeyPreviewShowUpStartScaleX = "); + sb.append("" + mKeyPreviewShowUpStartXScale); + sb.append("\n mKeyPreviewShowUpStartScaleY = "); + sb.append("" + mKeyPreviewShowUpStartYScale); + sb.append("\n mKeyPreviewDismissEndScaleX = "); + sb.append("" + mKeyPreviewDismissEndXScale); + sb.append("\n mKeyPreviewDismissEndScaleY = "); + sb.append("" + mKeyPreviewDismissEndYScale); return sb.toString(); } } diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index b8d2a2248..49d81104d 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings; import android.content.res.Resources; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.PunctuationSuggestions; @@ -68,6 +69,22 @@ public final class SpacingAndPunctuations { mSuggestPuncList = PunctuationSuggestions.newPunctuationSuggestions(suggestPuncsSpec); } + @UsedForTesting + public SpacingAndPunctuations(final SpacingAndPunctuations model, + final int[] overrideSortedWordSeparators) { + mSortedSymbolsPrecededBySpace = model.mSortedSymbolsPrecededBySpace; + mSortedSymbolsFollowedBySpace = model.mSortedSymbolsFollowedBySpace; + mSortedSymbolsClusteringTogether = model.mSortedSymbolsClusteringTogether; + mSortedWordConnectors = model.mSortedWordConnectors; + mSortedWordSeparators = overrideSortedWordSeparators; + mSuggestPuncList = model.mSuggestPuncList; + mSentenceSeparator = model.mSentenceSeparator; + mSentenceSeparatorAndSpace = model.mSentenceSeparatorAndSpace; + mCurrentLanguageHasSpaces = model.mCurrentLanguageHasSpaces; + mUsesAmericanTypography = model.mUsesAmericanTypography; + mUsesGermanRules = model.mUsesGermanRules; + } + public boolean isWordSeparator(final int code) { return Arrays.binarySearch(mSortedWordSeparators, code) >= 0; } diff --git a/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java b/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java index c70bf2997..ca5b395ce 100644 --- a/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SubScreenFragment.java @@ -115,4 +115,9 @@ abstract class SubScreenFragment extends PreferenceFragment mSharedPreferenceChangeListener); super.onDestroy(); } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + // This method may be overridden by an extended class. + } } diff --git a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java new file mode 100644 index 000000000..5a3fc3600 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.settings; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceScreen; + +import com.android.inputmethod.keyboard.KeyboardTheme; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.settings.RadioButtonPreference.OnRadioButtonClickedListener; + +/** + * "Keyboard theme" settings sub screen. + */ +public final class ThemeSettingsFragment extends SubScreenFragment + implements OnRadioButtonClickedListener { + private String mSelectedThemeId; + + static class KeyboardThemePreference extends RadioButtonPreference { + final String mThemeId; + + KeyboardThemePreference(final Context context, final String name, final String id) { + super(context); + setTitle(name); + mThemeId = id; + } + } + + static void updateKeyboardThemeSummary(final Preference pref) { + final Resources res = pref.getContext().getResources(); + final SharedPreferences prefs = pref.getSharedPreferences(); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); + final String keyboardThemeId = String.valueOf(keyboardTheme.mThemeId); + final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); + final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + for (int index = 0; index < keyboardThemeNames.length; index++) { + if (keyboardThemeId.equals(keyboardThemeIds[index])) { + pref.setSummary(keyboardThemeNames[index]); + return; + } + } + } + + @Override + public void onCreate(final Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.prefs_screen_theme); + final PreferenceScreen screen = getPreferenceScreen(); + final Resources res = getResources(); + final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); + final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + for (int index = 0; index < keyboardThemeNames.length; index++) { + final KeyboardThemePreference pref = new KeyboardThemePreference( + getActivity(), keyboardThemeNames[index], keyboardThemeIds[index]); + screen.addPreference(pref); + pref.setOnRadioButtonClickedListener(this); + } + final SharedPreferences prefs = getSharedPreferences(); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); + mSelectedThemeId = String.valueOf(keyboardTheme.mThemeId); + } + + @Override + public void onRadioButtonClicked(final RadioButtonPreference preference) { + if (preference instanceof KeyboardThemePreference) { + final KeyboardThemePreference pref = (KeyboardThemePreference)preference; + mSelectedThemeId = pref.mThemeId; + updateSelected(); + } + } + + @Override + public void onResume() { + super.onResume(); + updateSelected(); + } + + @Override + public void onPause() { + super.onPause(); + KeyboardTheme.saveKeyboardThemeId(mSelectedThemeId, getSharedPreferences()); + } + + private void updateSelected() { + final PreferenceScreen screen = getPreferenceScreen(); + final int count = screen.getPreferenceCount(); + for (int index = 0; index < count; index++) { + final Preference preference = screen.getPreference(index); + if (preference instanceof KeyboardThemePreference) { + final KeyboardThemePreference pref = (KeyboardThemePreference)preference; + final boolean selected = mSelectedThemeId.equals(pref.mThemeId); + pref.setSelected(selected); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java index 9585736e7..3f0b10225 100644 --- a/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java +++ b/java/src/com/android/inputmethod/latin/setup/LauncherIconVisibilityManager.java @@ -24,7 +24,6 @@ import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.util.Log; -import com.android.inputmethod.compat.IntentCompatUtils; import com.android.inputmethod.latin.settings.Settings; /** @@ -55,14 +54,6 @@ import com.android.inputmethod.latin.settings.Settings; public final class LauncherIconVisibilityManager { private static final String TAG = LauncherIconVisibilityManager.class.getSimpleName(); - public static void onReceiveGlobalIntent(final String action, final Context context) { - if (Intent.ACTION_MY_PACKAGE_REPLACED.equals(action) || - Intent.ACTION_BOOT_COMPLETED.equals(action) || - IntentCompatUtils.is_ACTION_USER_INITIALIZE(action)) { - updateSetupWizardIconVisibility(context); - } - } - public static void updateSetupWizardIconVisibility(final Context context) { final ComponentName setupWizardActivity = new ComponentName(context, SetupActivity.class); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 346aea34a..9d186d44d 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -125,9 +125,9 @@ public final class MoreSuggestions extends Keyboard { } private static final int[][] COLUMN_ORDER_TO_NUMBER = { - { 0, }, - { 1, 0, }, - { 2, 0, 1}, + { 0 }, // center + { 1, 0 }, // right-left + { 1, 0, 2 }, // center-left-right }; public int getNumColumnInRow(final int index) { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index c5f062d5b..1e8df8986 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -45,11 +45,14 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.PunctuationSuggestions; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.DebugFlags; +import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -72,7 +75,7 @@ final class SuggestionStripLayoutHelper { private int mMaxMoreSuggestionsRow; public final float mMinMoreSuggestionsWidth; public final int mMoreSuggestionsBottomGap; - public boolean mMoreSuggestionsAvailable; + private boolean mMoreSuggestionsAvailable; // The index of these {@link ArrayList} is the position in the suggestion strip. The indices // increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. @@ -167,16 +170,14 @@ final class SuggestionStripLayoutHelper { return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; } - public int setMoreSuggestionsHeight(final int remainingHeight) { + public void setMoreSuggestionsHeight(final int remainingHeight) { final int currentHeight = getMoreSuggestionsHeight(); if (currentHeight <= remainingHeight) { - return currentHeight; + return; } mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) / mMoreSuggestionsRowHeight; - final int newHeight = getMoreSuggestionsHeight(); - return newHeight; } private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, @@ -225,11 +226,59 @@ final class SuggestionStripLayoutHelper { return spannedWord; } + /** + * Convert an index of {@link SuggestedWords} to position in the suggestion strip. + * @param indexInSuggestedWords the index of {@link SuggestedWords}. + * @param suggestedWords the suggested words list + * @return Non-negative integer of the position in the suggestion strip. + * Negative integer if the word of the index shouldn't be shown on the suggestion strip. + */ private int getPositionInSuggestionStrip(final int indexInSuggestedWords, final SuggestedWords suggestedWords) { + final SettingsValues settingsValues = Settings.getInstance().getCurrent(); + final boolean shouldOmitTypedWord = shouldOmitTypedWord(suggestedWords.mInputStyle, + settingsValues.mGestureFloatingPreviewTextEnabled, + settingsValues.mShouldShowUiToAcceptTypedWord); + return getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords.mWillAutoCorrect, + settingsValues.mShouldShowUiToAcceptTypedWord && shouldOmitTypedWord, + mCenterPositionInStrip, mTypedWordPositionWhenAutocorrect); + } + + @UsedForTesting + static boolean shouldOmitTypedWord(final int inputStyle, + final boolean gestureFloatingPreviewTextEnabled, + final boolean shouldShowUiToAcceptTypedWord) { + final boolean omitTypedWord = (inputStyle == SuggestedWords.INPUT_STYLE_TYPING) + || (inputStyle == SuggestedWords.INPUT_STYLE_TAIL_BATCH) + || (inputStyle == SuggestedWords.INPUT_STYLE_UPDATE_BATCH + && gestureFloatingPreviewTextEnabled); + return shouldShowUiToAcceptTypedWord && omitTypedWord; + } + + @UsedForTesting + static int getPositionInSuggestionStrip(final int indexInSuggestedWords, + final boolean willAutoCorrect, final boolean omitTypedWord, + final int centerPositionInStrip, final int typedWordPositionWhenAutoCorrect) { + if (omitTypedWord) { + if (indexInSuggestedWords == SuggestedWords.INDEX_OF_TYPED_WORD) { + // Ignore. + return -1; + } + if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION) { + // Center in the suggestion strip. + return centerPositionInStrip; + } + // If neither of those, the order in the suggestion strip is left of the center first + // then right of the center, to both edges of the suggestion strip. + // For example, center-1, center+1, center-2, center+2, and so on. + final int n = indexInSuggestedWords; + final int offsetFromCenter = (n % 2) == 0 ? -(n / 2) : (n / 2); + final int positionInSuggestionStrip = centerPositionInStrip + offsetFromCenter; + return positionInSuggestionStrip; + } final int indexToDisplayMostImportantSuggestion; final int indexToDisplaySecondMostImportantSuggestion; - if (suggestedWords.mWillAutoCorrect) { + if (willAutoCorrect) { indexToDisplayMostImportantSuggestion = SuggestedWords.INDEX_OF_AUTO_CORRECTION; indexToDisplaySecondMostImportantSuggestion = SuggestedWords.INDEX_OF_TYPED_WORD; } else { @@ -237,25 +286,31 @@ final class SuggestionStripLayoutHelper { indexToDisplaySecondMostImportantSuggestion = SuggestedWords.INDEX_OF_AUTO_CORRECTION; } if (indexInSuggestedWords == indexToDisplayMostImportantSuggestion) { - return mCenterPositionInStrip; + // Center in the suggestion strip. + return centerPositionInStrip; } if (indexInSuggestedWords == indexToDisplaySecondMostImportantSuggestion) { - return mTypedWordPositionWhenAutocorrect; + // Center-1. + return typedWordPositionWhenAutoCorrect; } - // If neither of those, the order in the suggestion strip is the same as in SuggestedWords. - return indexInSuggestedWords; + // If neither of those, the order in the suggestion strip is right of the center first + // then left of the center, to both edges of the suggestion strip. + // For example, Center+1, center-2, center+2, center-3, and so on. + final int n = indexInSuggestedWords + 1; + final int offsetFromCenter = (n % 2) == 0 ? -(n / 2) : (n / 2); + final int positionInSuggestionStrip = centerPositionInStrip + offsetFromCenter; + return positionInSuggestionStrip; } private int getSuggestionTextColor(final SuggestedWords suggestedWords, final int indexInSuggestedWords) { - final int positionInStrip = - getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords); // Use identity for strings, not #equals : it's the typed word if it's the same object final boolean isTypedWord = suggestedWords.getInfo(indexInSuggestedWords).isKindOf( SuggestedWordInfo.KIND_TYPED); final int color; - if (positionInStrip == mCenterPositionInStrip && suggestedWords.mWillAutoCorrect) { + if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION + && suggestedWords.mWillAutoCorrect) { color = mColorAutoCorrect; } else if (isTypedWord && suggestedWords.mTypedWordValid) { color = mColorValidTypedWord; @@ -267,7 +322,8 @@ final class SuggestionStripLayoutHelper { if (DebugFlags.DEBUG_ENABLED && suggestedWords.size() > 1) { // If we auto-correct, then the autocorrection is in slot 0 and the typed word // is in slot 1. - if (positionInStrip == mCenterPositionInStrip + if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION + && suggestedWords.mWillAutoCorrect && AutoCorrectionUtils.shouldBlockAutoCorrectionBySafetyNet( suggestedWords.getLabel(SuggestedWords.INDEX_OF_AUTO_CORRECTION), suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD))) { @@ -294,31 +350,31 @@ final class SuggestionStripLayoutHelper { } /** - * Layout suggestions to the suggestions strip. And returns the number of suggestions displayed - * in the suggestions strip. + * Layout suggestions to the suggestions strip. And returns the start index of more + * suggestions. * * @param suggestedWords suggestions to be shown in the suggestions strip. * @param stripView the suggestions strip view. * @param placerView the view where the debug info will be placed. - * @return the number of suggestions displayed in the suggestions strip + * @return the start index of more suggestions. */ - public int layoutAndReturnSuggestionCountInStrip(final SuggestedWords suggestedWords, + public int layoutAndReturnStartIndexOfMoreSuggestions(final SuggestedWords suggestedWords, final ViewGroup stripView, final ViewGroup placerView) { if (suggestedWords.isPunctuationSuggestions()) { - return layoutPunctuationSuggestionsAndReturnSuggestionCountInStrip( + return layoutPunctuationsAndReturnStartIndexOfMoreSuggestions( (PunctuationSuggestions)suggestedWords, stripView); } - setupWordViewsTextAndColor(suggestedWords, mSuggestionsCountInStrip); + final int startIndexOfMoreSuggestions = setupWordViewsAndReturnStartIndexOfMoreSuggestions( + suggestedWords, mSuggestionsCountInStrip); final TextView centerWordView = mWordViews.get(mCenterPositionInStrip); final int stripWidth = stripView.getWidth(); final int centerWidth = getSuggestionWidth(mCenterPositionInStrip, stripWidth); - final int countInStrip; if (suggestedWords.size() == 1 || getTextScaleX(centerWordView.getText(), centerWidth, centerWordView.getPaint()) < MIN_TEXT_XSCALE) { // Layout only the most relevant suggested word at the center of the suggestion strip // by consolidating all slots in the strip. - countInStrip = 1; + final int countInStrip = 1; mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); layoutWord(mCenterPositionInStrip, stripWidth - mPadding); stripView.addView(centerWordView); @@ -326,31 +382,33 @@ final class SuggestionStripLayoutHelper { if (SuggestionStripView.DBG) { layoutDebugInfo(mCenterPositionInStrip, placerView, stripWidth); } - } else { - countInStrip = mSuggestionsCountInStrip; - mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); - int x = 0; - for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { - if (positionInStrip != 0) { - final View divider = mDividerViews.get(positionInStrip); - // Add divider if this isn't the left most suggestion in suggestions strip. - addDivider(stripView, divider); - x += divider.getMeasuredWidth(); - } - - final int width = getSuggestionWidth(positionInStrip, stripWidth); - final TextView wordView = layoutWord(positionInStrip, width); - stripView.addView(wordView); - setLayoutWeight(wordView, getSuggestionWeight(positionInStrip), - ViewGroup.LayoutParams.MATCH_PARENT); - x += wordView.getMeasuredWidth(); - - if (SuggestionStripView.DBG) { - layoutDebugInfo(positionInStrip, placerView, x); - } + final Integer lastIndex = (Integer)centerWordView.getTag(); + return (lastIndex == null ? 0 : lastIndex) + 1; + } + + final int countInStrip = mSuggestionsCountInStrip; + mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); + int x = 0; + for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { + if (positionInStrip != 0) { + final View divider = mDividerViews.get(positionInStrip); + // Add divider if this isn't the left most suggestion in suggestions strip. + addDivider(stripView, divider); + x += divider.getMeasuredWidth(); + } + + final int width = getSuggestionWidth(positionInStrip, stripWidth); + final TextView wordView = layoutWord(positionInStrip, width); + stripView.addView(wordView); + setLayoutWeight(wordView, getSuggestionWeight(positionInStrip), + ViewGroup.LayoutParams.MATCH_PARENT); + x += wordView.getMeasuredWidth(); + + if (SuggestionStripView.DBG) { + layoutDebugInfo(positionInStrip, placerView, x); } } - return countInStrip; + return startIndexOfMoreSuggestions; } /** @@ -428,10 +486,10 @@ final class SuggestionStripLayoutHelper { return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1); } - private void setupWordViewsTextAndColor(final SuggestedWords suggestedWords, - final int countInStrip) { + private int setupWordViewsAndReturnStartIndexOfMoreSuggestions( + final SuggestedWords suggestedWords, final int maxSuggestionInStrip) { // Clear all suggestions first - for (int positionInStrip = 0; positionInStrip < countInStrip; ++positionInStrip) { + for (int positionInStrip = 0; positionInStrip < maxSuggestionInStrip; ++positionInStrip) { final TextView wordView = mWordViews.get(positionInStrip); wordView.setText(null); wordView.setTag(null); @@ -440,11 +498,15 @@ final class SuggestionStripLayoutHelper { mDebugInfoViews.get(positionInStrip).setText(null); } } - final int count = Math.min(suggestedWords.size(), countInStrip); - for (int indexInSuggestedWords = 0; indexInSuggestedWords < count; - indexInSuggestedWords++) { + int count = 0; + int indexInSuggestedWords; + for (indexInSuggestedWords = 0; indexInSuggestedWords < suggestedWords.size() + && count < maxSuggestionInStrip; indexInSuggestedWords++) { final int positionInStrip = getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords); + if (positionInStrip < 0) { + continue; + } final TextView wordView = mWordViews.get(positionInStrip); // {@link TextView#getTag()} is used to get the index in suggestedWords at // {@link SuggestionStripView#onClick(View)}. @@ -455,10 +517,12 @@ final class SuggestionStripLayoutHelper { mDebugInfoViews.get(positionInStrip).setText( suggestedWords.getDebugString(indexInSuggestedWords)); } + count++; } + return indexInSuggestedWords; } - private int layoutPunctuationSuggestionsAndReturnSuggestionCountInStrip( + private int layoutPunctuationsAndReturnStartIndexOfMoreSuggestions( final PunctuationSuggestions punctuationSuggestions, final ViewGroup stripView) { final int countInStrip = Math.min(punctuationSuggestions.size(), PUNCTUATIONS_IN_STRIP); for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { @@ -485,8 +549,11 @@ final class SuggestionStripLayoutHelper { } public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) { + final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent() + .mShouldShowUiToAcceptTypedWord; final int stripWidth = addToDictionaryStrip.getWidth(); - final int width = stripWidth - mDividerWidth - mPadding * 2; + final int width = shouldShowUiToAcceptTypedWord ? stripWidth + : stripWidth - mDividerWidth - mPadding * 2; final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save); wordView.setTextColor(mColorTypedWord); @@ -496,25 +563,38 @@ final class SuggestionStripLayoutHelper { wordView.setText(wordToSave); wordView.setTextScaleX(wordScaleX); setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + final int wordVisibility = shouldShowUiToAcceptTypedWord ? View.GONE : View.VISIBLE; + wordView.setVisibility(wordVisibility); + addToDictionaryStrip.findViewById(R.id.word_to_save_divider).setVisibility(wordVisibility); + final Resources res = addToDictionaryStrip.getResources(); + final CharSequence hintText; + final int hintWidth; + final float hintWeight; final TextView hintView = (TextView)addToDictionaryStrip.findViewById( R.id.hint_add_to_dictionary); + if (shouldShowUiToAcceptTypedWord) { + hintText = res.getText(R.string.hint_add_to_dictionary_without_word); + hintWidth = width; + hintWeight = 1.0f; + hintView.setGravity(Gravity.CENTER); + } else { + final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip) + == ViewCompat.LAYOUT_DIRECTION_RTL); + final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW; + final boolean isRtlSystem = SubtypeLocaleUtils.isRtlLanguage( + res.getConfiguration().locale); + final CharSequence hint = res.getText(R.string.hint_add_to_dictionary); + hintText = (isRtlLanguage == isRtlSystem) ? (arrow + hint) : (hint + arrow); + hintWidth = width - wordWidth; + hintWeight = 1.0f - mCenterSuggestionWeight; + hintView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START); + } hintView.setTextColor(mColorAutoCorrect); - final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip) - == ViewCompat.LAYOUT_DIRECTION_RTL); - final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW; - final Resources res = addToDictionaryStrip.getResources(); - final boolean isRtlSystem = SubtypeLocaleUtils.isRtlLanguage(res.getConfiguration().locale); - final CharSequence hintText = res.getText(R.string.hint_add_to_dictionary); - final String hintWithArrow = (isRtlLanguage == isRtlSystem) - ? (arrow + hintText) : (hintText + arrow); - final int hintWidth = width - wordWidth; - hintView.setTextScaleX(1.0f); // Reset textScaleX. - final float hintScaleX = getTextScaleX(hintWithArrow, hintWidth, hintView.getPaint()); - hintView.setText(hintWithArrow); + final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); + hintView.setText(hintText); hintView.setTextScaleX(hintScaleX); - setLayoutWeight( - hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + setLayoutWeight(hintView, hintWeight, ViewGroup.LayoutParams.MATCH_PARENT); } public void layoutImportantNotice(final View importantNoticeStrip, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index d151e4037..0fd5e139e 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -38,6 +38,7 @@ import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.TextView; +import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; @@ -82,7 +83,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick Listener mListener; private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; - private int mSuggestionsCountInStrip; + private int mStartIndexOfMoreSuggestions; private final SuggestionStripLayoutHelper mLayoutHelper; private final StripVisibilityGroup mStripVisibilityGroup; @@ -213,13 +214,13 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick clear(); mStripVisibilityGroup.setLayoutDirection(isRtlLanguage); mSuggestedWords = suggestedWords; - mSuggestionsCountInStrip = mLayoutHelper.layoutAndReturnSuggestionCountInStrip( + mStartIndexOfMoreSuggestions = mLayoutHelper.layoutAndReturnStartIndexOfMoreSuggestions( mSuggestedWords, mSuggestionsStrip, this); mStripVisibilityGroup.showSuggestionsStrip(); } - public int setMoreSuggestionsHeight(final int remainingHeight) { - return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); + public void setMoreSuggestionsHeight(final int remainingHeight) { + mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); } public boolean isShowingAddToDictionaryHint() { @@ -336,7 +337,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return false; } final SuggestionStripLayoutHelper layoutHelper = mLayoutHelper; - if (!layoutHelper.mMoreSuggestionsAvailable) { + if (mSuggestedWords.size() <= mStartIndexOfMoreSuggestions) { return false; } // Dismiss another {@link MoreKeysPanel} that may be being showed, for example @@ -349,7 +350,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; - builder.layout(mSuggestedWords, mSuggestionsCountInStrip, maxWidth, + builder.layout(mSuggestedWords, mStartIndexOfMoreSuggestions, maxWidth, (int)(maxWidth * layoutHelper.mMinMoreSuggestionsWidth), layoutHelper.getMaxMoreSuggestionsRow(), parentKeyboard); mMoreSuggestionsView.setKeyboard(builder.build()); @@ -362,19 +363,21 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mMoreSuggestionsListener); mOriginX = mLastX; mOriginY = mLastY; - for (int i = 0; i < mSuggestionsCountInStrip; i++) { + for (int i = 0; i < mStartIndexOfMoreSuggestions; i++) { mWordViews.get(i).setPressed(false); } return true; } - // Working variables for {@link #onLongClick(View)} and - // {@link onInterceptTouchEvent(MotionEvent)}. + // Working variables for {@link onInterceptTouchEvent(MotionEvent)} and + // {@link onTouchEvent(MotionEvent)}. private int mLastX; private int mLastY; private int mOriginX; private int mOriginY; private final int mMoreSuggestionsModalTolerance; + private boolean mNeedsToTransformTouchEventToHoverEvent; + private boolean mIsDispatchingHoverEventToMoreSuggestions; private final GestureDetector mMoreSuggestionsSlidingDetector; private final GestureDetector.OnGestureListener mMoreSuggestionsSlidingListener = new GestureDetector.SimpleOnGestureListener() { @@ -402,9 +405,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick final int y = (int)me.getY(index); if (Math.abs(x - mOriginX) >= mMoreSuggestionsModalTolerance || mOriginY - y >= mMoreSuggestionsModalTolerance) { - // Decided to be in the sliding input mode only when the touch point has been moved + // Decided to be in the sliding suggestion mode only when the touch point has been moved // upward. Further {@link MotionEvent}s will be delivered to // {@link #onTouchEvent(MotionEvent)}. + mNeedsToTransformTouchEventToHoverEvent = + AccessibilityUtils.getInstance().isTouchExplorationEnabled(); + mIsDispatchingHoverEventToMoreSuggestions = false; return true; } @@ -426,10 +432,41 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // In the sliding input mode. {@link MotionEvent} should be forwarded to // {@link MoreSuggestionsView}. final int index = me.getActionIndex(); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - me.setLocation(mMoreSuggestionsView.translateX(x), mMoreSuggestionsView.translateY(y)); - mMoreSuggestionsView.onTouchEvent(me); + final int x = mMoreSuggestionsView.translateX((int)me.getX(index)); + final int y = mMoreSuggestionsView.translateY((int)me.getY(index)); + me.setLocation(x, y); + if (!mNeedsToTransformTouchEventToHoverEvent) { + mMoreSuggestionsView.onTouchEvent(me); + return true; + } + // In sliding suggestion mode with accessibility mode on, a touch event should be + // transformed to a hover event. + final int width = mMoreSuggestionsView.getWidth(); + final int height = mMoreSuggestionsView.getHeight(); + final boolean onMoreSuggestions = (x >= 0 && x < width && y >= 0 && y < height); + if (!onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) { + // Just drop this touch event because dispatching hover event isn't started yet and + // the touch event isn't on {@link MoreSuggestionsView}. + return true; + } + final int hoverAction; + if (onMoreSuggestions && !mIsDispatchingHoverEventToMoreSuggestions) { + // Transform this touch event to a hover enter event and start dispatching a hover + // event to {@link MoreSuggestionsView}. + mIsDispatchingHoverEventToMoreSuggestions = true; + hoverAction = MotionEvent.ACTION_HOVER_ENTER; + } else if (me.getActionMasked() == MotionEvent.ACTION_UP) { + // Transform this touch event to a hover exit event and stop dispatching a hover event + // after this. + mIsDispatchingHoverEventToMoreSuggestions = false; + mNeedsToTransformTouchEventToHoverEvent = false; + hoverAction = MotionEvent.ACTION_HOVER_EXIT; + } else { + // Transform this touch event to a hover move event. + hoverAction = MotionEvent.ACTION_HOVER_MOVE; + } + me.setAction(hoverAction); + mMoreSuggestionsView.onHoverEvent(me); return true; } diff --git a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java new file mode 100644 index 000000000..9dc0524a2 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.inputmethodservice.ExtractEditText; +import android.inputmethodservice.InputMethodService; +import android.text.Layout; +import android.text.Spannable; +import android.view.View; +import android.view.ViewParent; +import android.view.inputmethod.CursorAnchorInfo; +import android.widget.TextView; + +/** + * This class allows input methods to extract {@link CursorAnchorInfo} directly from the given + * {@link TextView}. This is useful and even necessary to support full-screen mode where the default + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} event callback must be + * ignored because it reports the character locations of the target application rather than + * characters on {@link ExtractEditText}. + */ +public final class CursorAnchorInfoUtils { + private CursorAnchorInfoUtils() { + // This helper class is not instantiable. + } + + private static boolean isPositionVisible(final View view, final float positionX, + final float positionY) { + final float[] position = new float[] { positionX, positionY }; + View currentView = view; + + while (currentView != null) { + if (currentView != view) { + // Local scroll is already taken into account in positionX/Y + position[0] -= currentView.getScrollX(); + position[1] -= currentView.getScrollY(); + } + + if (position[0] < 0 || position[1] < 0 || + position[0] > currentView.getWidth() || position[1] > currentView.getHeight()) { + return false; + } + + if (!currentView.getMatrix().isIdentity()) { + currentView.getMatrix().mapPoints(position); + } + + position[0] += currentView.getLeft(); + position[1] += currentView.getTop(); + + final ViewParent parent = currentView.getParent(); + if (parent instanceof View) { + currentView = (View) parent; + } else { + // We've reached the ViewRoot, stop iterating + currentView = null; + } + } + + // We've been able to walk up the view hierarchy and the position was never clipped + return true; + } + + /** + * Returns {@link CursorAnchorInfo} from the given {@link TextView}. + * @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted. + * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it + * is not feasible. + */ + public static CursorAnchorInfo getCursorAnchorInfo(final TextView textView) { + Layout layout = textView.getLayout(); + if (layout == null) { + return null; + } + + final CursorAnchorInfo.Builder builder = new CursorAnchorInfo.Builder(); + + final int selectionStart = textView.getSelectionStart(); + builder.setSelectionRange(selectionStart, textView.getSelectionEnd()); + + // Construct transformation matrix from view local coordinates to screen coordinates. + final Matrix viewToScreenMatrix = new Matrix(textView.getMatrix()); + final int[] viewOriginInScreen = new int[2]; + textView.getLocationOnScreen(viewOriginInScreen); + viewToScreenMatrix.postTranslate(viewOriginInScreen[0], viewOriginInScreen[1]); + builder.setMatrix(viewToScreenMatrix); + + if (layout.getLineCount() == 0) { + return null; + } + final Rect lineBoundsWithoutOffset = new Rect(); + final Rect lineBoundsWithOffset = new Rect(); + layout.getLineBounds(0, lineBoundsWithoutOffset); + textView.getLineBounds(0, lineBoundsWithOffset); + final float viewportToContentHorizontalOffset = lineBoundsWithOffset.left + - lineBoundsWithoutOffset.left - textView.getScrollX(); + final float viewportToContentVerticalOffset = lineBoundsWithOffset.top + - lineBoundsWithoutOffset.top - textView.getScrollY(); + + final CharSequence text = textView.getText(); + if (text instanceof Spannable) { + // Here we assume that the composing text is marked as SPAN_COMPOSING flag. This is not + // necessarily true, but basically works. + int composingTextStart = text.length(); + int composingTextEnd = 0; + final Spannable spannable = (Spannable) text; + final Object[] spans = spannable.getSpans(0, text.length(), Object.class); + for (Object span : spans) { + final int spanFlag = spannable.getSpanFlags(span); + if ((spanFlag & Spannable.SPAN_COMPOSING) != 0) { + composingTextStart = Math.min(composingTextStart, + spannable.getSpanStart(span)); + composingTextEnd = Math.max(composingTextEnd, spannable.getSpanEnd(span)); + } + } + + final boolean hasComposingText = + (0 <= composingTextStart) && (composingTextStart < composingTextEnd); + if (hasComposingText) { + final CharSequence composingText = text.subSequence(composingTextStart, + composingTextEnd); + builder.setComposingText(composingTextStart, composingText); + + final int minLine = layout.getLineForOffset(composingTextStart); + final int maxLine = layout.getLineForOffset(composingTextEnd - 1); + for (int line = minLine; line <= maxLine; ++line) { + final int lineStart = layout.getLineStart(line); + final int lineEnd = layout.getLineEnd(line); + final int offsetStart = Math.max(lineStart, composingTextStart); + final int offsetEnd = Math.min(lineEnd, composingTextEnd); + final boolean ltrLine = + layout.getParagraphDirection(line) == Layout.DIR_LEFT_TO_RIGHT; + final float[] widths = new float[offsetEnd - offsetStart]; + layout.getPaint().getTextWidths(text, offsetStart, offsetEnd, widths); + final float top = layout.getLineTop(line); + final float bottom = layout.getLineBottom(line); + for (int offset = offsetStart; offset < offsetEnd; ++offset) { + final float charWidth = widths[offset - offsetStart]; + final boolean isRtl = layout.isRtlCharAt(offset); + final float primary = layout.getPrimaryHorizontal(offset); + final float secondary = layout.getSecondaryHorizontal(offset); + // TODO: This doesn't work perfectly for text with custom styles and TAB + // chars. + final float left; + final float right; + if (ltrLine) { + if (isRtl) { + left = secondary - charWidth; + right = secondary; + } else { + left = primary; + right = primary + charWidth; + } + } else { + if (!isRtl) { + left = secondary; + right = secondary + charWidth; + } else { + left = primary - charWidth; + right = primary; + } + } + // TODO: Check top-right and bottom-left as well. + final float localLeft = left + viewportToContentHorizontalOffset; + final float localRight = right + viewportToContentHorizontalOffset; + final float localTop = top + viewportToContentVerticalOffset; + final float localBottom = bottom + viewportToContentVerticalOffset; + final boolean isTopLeftVisible = isPositionVisible(textView, + localLeft, localTop); + final boolean isBottomRightVisible = + isPositionVisible(textView, localRight, localBottom); + int characterBoundsFlags = 0; + if (isTopLeftVisible || isBottomRightVisible) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isTopLeftVisible || !isTopLeftVisible) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + } + if (isRtl) { + characterBoundsFlags |= CursorAnchorInfo.FLAG_IS_RTL; + } + // Here offset is the index in Java chars. + builder.addCharacterBounds(offset, localLeft, localTop, localRight, + localBottom, characterBoundsFlags); + } + } + } + } + + // Treat selectionStart as the insertion point. + if (0 <= selectionStart) { + final int offset = selectionStart; + final int line = layout.getLineForOffset(offset); + final float insertionMarkerX = layout.getPrimaryHorizontal(offset) + + viewportToContentHorizontalOffset; + final float insertionMarkerTop = layout.getLineTop(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBaseline = layout.getLineBaseline(line) + + viewportToContentVerticalOffset; + final float insertionMarkerBottom = layout.getLineBottom(line) + + viewportToContentVerticalOffset; + final boolean isTopVisible = + isPositionVisible(textView, insertionMarkerX, insertionMarkerTop); + final boolean isBottomVisible = + isPositionVisible(textView, insertionMarkerX, insertionMarkerBottom); + int insertionMarkerFlags = 0; + if (isTopVisible || isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION; + } + if (!isTopVisible || !isBottomVisible) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION; + } + if (layout.isRtlCharAt(offset)) { + insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL; + } + builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop, + insertionMarkerBaseline, insertionMarkerBottom, insertionMarkerFlags); + } + return builder.build(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index d76ea10c0..197908032 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -386,8 +386,7 @@ public class DictionaryInfoUtils { final SpacingAndPunctuations spacingAndPunctuations) { if (TextUtils.isEmpty(text)) return false; final int length = text.length(); - // TODO: Make this test "length > Constants.DICTIONARY_MAX_WORD_LENGTH". - if (length >= Constants.DICTIONARY_MAX_WORD_LENGTH) { + if (length > Constants.DICTIONARY_MAX_WORD_LENGTH) { return false; } int i = 0; diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java deleted file mode 100644 index 0ee6236b1..000000000 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import java.util.List; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -import android.content.Context; -import android.util.Log; -import android.util.LruCache; -import android.view.inputmethod.InputMethodSubtype; - -import com.android.inputmethod.latin.DictionaryFacilitator; -import com.android.inputmethod.latin.PrevWordsInfo; - -/** - * This class is used to prevent distracters being added to personalization - * or user history dictionaries - */ -public class DistracterFilterCheckingExactMatches implements DistracterFilter { - private static final String TAG = DistracterFilterCheckingExactMatches.class.getSimpleName(); - private static final boolean DEBUG = false; - - private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; - private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; - - private final Context mContext; - private final DictionaryFacilitator mDictionaryFacilitator; - private final LruCache<String, Boolean> mDistractersCache; - private final Object mLock = new Object(); - - /** - * Create a DistracterFilter instance. - * - * @param context the context. - */ - public DistracterFilterCheckingExactMatches(final Context context) { - mContext = context; - mDictionaryFacilitator = new DictionaryFacilitator(); - mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); - } - - @Override - public void close() { - mDictionaryFacilitator.closeDictionaries(); - } - - @Override - public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { - } - - private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { - mDictionaryFacilitator.resetDictionaries(mContext, newlocale, - false /* useContactsDict */, false /* usePersonalizedDicts */, - false /* forceReloadMainDictionary */, null /* listener */); - mDictionaryFacilitator.waitForLoadingMainDictionary( - TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); - } - - /** - * Determine whether a word is a distracter to words in dictionaries. - * - * @param prevWordsInfo the information of previous words. Not used for now. - * @param testedWord the word that will be tested to see whether it is a distracter to words - * in dictionaries. - * @param locale the locale of word. - * @return true if testedWord is a distracter, otherwise false. - */ - @Override - public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, - final String testedWord, final Locale locale) { - if (locale == null) { - return false; - } - if (!locale.equals(mDictionaryFacilitator.getLocale())) { - synchronized (mLock) { - // Reset dictionaries for the locale. - try { - mDistractersCache.evictAll(); - loadDictionariesForLocale(locale); - } catch (final InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", - e); - return false; - } - } - } - - final Boolean isCachedDistracter = mDistractersCache.get(testedWord); - if (isCachedDistracter != null && isCachedDistracter) { - if (DEBUG) { - Log.d(TAG, "testedWord: " + testedWord); - Log.d(TAG, "isDistracter: true (cache hit)"); - } - return true; - } - // The tested word is a distracter when there is a word that is exact matched to the tested - // word and its probability is higher than the tested word's probability. - final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); - final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); - final boolean isDistracter = perfectMatchFreq < exactMatchFreq; - if (DEBUG) { - Log.d(TAG, "testedWord: " + testedWord); - Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); - Log.d(TAG, "exactMatchFreq: " + exactMatchFreq); - Log.d(TAG, "isDistracter: " + isDistracter); - } - if (isDistracter) { - // Add the word to the cache. - mDistractersCache.put(testedWord, Boolean.TRUE); - } - return isDistracter; - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java new file mode 100644 index 000000000..27973287d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import android.content.Context; +import android.content.res.Resources; +import android.text.InputType; +import android.util.Log; +import android.util.LruCache; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.PrevWordsInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.WordComposer; +import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; + +/** + * This class is used to prevent distracters being added to personalization + * or user history dictionaries + */ +public class DistracterFilterCheckingExactMatchesAndSuggestions implements DistracterFilter { + private static final String TAG = + DistracterFilterCheckingExactMatchesAndSuggestions.class.getSimpleName(); + private static final boolean DEBUG = false; + + private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; + private static final int MAX_DISTRACTERS_CACHE_SIZE = 512; + + private final Context mContext; + private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; + private final Map<Locale, Keyboard> mLocaleToKeyboardMap; + private final DictionaryFacilitator mDictionaryFacilitator; + private final LruCache<String, Boolean> mDistractersCache; + private Keyboard mKeyboard; + private final Object mLock = new Object(); + + // If the score of the top suggestion exceeds this value, the tested word (e.g., + // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distractor to + // words in dictionary. The greater the threshold is, the less likely the tested word would + // become a distractor, which means the tested word will be more likely to be added to + // the dictionary. + private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 0.4f; + + /** + * Create a DistracterFilter instance. + * + * @param context the context. + */ + public DistracterFilterCheckingExactMatchesAndSuggestions(final Context context) { + mContext = context; + mLocaleToSubtypeMap = new HashMap<>(); + mLocaleToKeyboardMap = new HashMap<>(); + mDictionaryFacilitator = new DictionaryFacilitator(); + mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE); + mKeyboard = null; + } + + @Override + public void close() { + mDictionaryFacilitator.closeDictionaries(); + } + + @Override + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { + final Map<Locale, InputMethodSubtype> newLocaleToSubtypeMap = new HashMap<>(); + if (enabledSubtypes != null) { + for (final InputMethodSubtype subtype : enabledSubtypes) { + final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); + if (newLocaleToSubtypeMap.containsKey(locale)) { + // Multiple subtypes are enabled for one locale. + // TODO: Investigate what we should do for this case. + continue; + } + newLocaleToSubtypeMap.put(locale, subtype); + } + } + if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { + // Enabled subtypes have not been changed. + return; + } + synchronized (mLock) { + mLocaleToSubtypeMap.clear(); + mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); + mLocaleToKeyboardMap.clear(); + } + } + + private void loadKeyboardForLocale(final Locale newLocale) { + final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); + if (cachedKeyboard != null) { + mKeyboard = cachedKeyboard; + return; + } + final InputMethodSubtype subtype; + synchronized (mLock) { + subtype = mLocaleToSubtypeMap.get(newLocale); + } + if (subtype == null) { + return; + } + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.inputType = InputType.TYPE_CLASS_TEXT; + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( + mContext, editorInfo); + final Resources res = mContext.getResources(); + final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); + final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); + builder.setSubtype(subtype); + builder.setIsSpellChecker(false /* isSpellChecker */); + final KeyboardLayoutSet layoutSet = builder.build(); + mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); + } + + private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { + mDictionaryFacilitator.resetDictionaries(mContext, newlocale, + false /* useContactsDict */, false /* usePersonalizedDicts */, + false /* forceReloadMainDictionary */, null /* listener */); + mDictionaryFacilitator.waitForLoadingMainDictionary( + TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); + } + + /** + * Determine whether a word is a distracter to words in dictionaries. + * + * @param prevWordsInfo the information of previous words. Not used for now. + * @param testedWord the word that will be tested to see whether it is a distracter to words + * in dictionaries. + * @param locale the locale of word. + * @return true if testedWord is a distracter, otherwise false. + */ + @Override + public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, + final String testedWord, final Locale locale) { + if (locale == null) { + return false; + } + if (!locale.equals(mDictionaryFacilitator.getLocale())) { + synchronized (mLock) { + if (!mLocaleToSubtypeMap.containsKey(locale)) { + Log.e(TAG, "Locale " + locale + " is not enabled."); + // TODO: Investigate what we should do for disabled locales. + return false; + } + loadKeyboardForLocale(locale); + // Reset dictionaries for the locale. + try { + mDistractersCache.evictAll(); + loadDictionariesForLocale(locale); + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", + e); + return false; + } + } + } + + if (DEBUG) { + Log.d(TAG, "testedWord: " + testedWord); + } + final Boolean isCachedDistracter = mDistractersCache.get(testedWord); + if (isCachedDistracter != null && isCachedDistracter) { + if (DEBUG) { + Log.d(TAG, "isDistracter: true (cache hit)"); + } + return true; + } + + final boolean isDistracterCheckedByGetMaxFreqencyOfExactMatches = + checkDistracterUsingMaxFreqencyOfExactMatches(testedWord); + if (isDistracterCheckedByGetMaxFreqencyOfExactMatches) { + // Add the word to the cache. + mDistractersCache.put(testedWord, Boolean.TRUE); + return true; + } + final boolean isValidWord = mDictionaryFacilitator.isValidWord(testedWord, + false /* ignoreCase */); + if (isValidWord) { + // Valid word is not a distractor. + if (DEBUG) { + Log.d(TAG, "isDistracter: false (valid word)"); + } + return false; + } + + final boolean isDistracterCheckedByGetSuggestion = + checkDistracterUsingGetSuggestions(testedWord); + if (isDistracterCheckedByGetSuggestion) { + // Add the word to the cache. + mDistractersCache.put(testedWord, Boolean.TRUE); + return true; + } + return false; + } + + private boolean checkDistracterUsingMaxFreqencyOfExactMatches(final String testedWord) { + // The tested word is a distracter when there is a word that is exact matched to the tested + // word and its probability is higher than the tested word's probability. + final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final boolean isDistracter = perfectMatchFreq < exactMatchFreq; + if (DEBUG) { + Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); + Log.d(TAG, "exactMatchFreq: " + exactMatchFreq); + Log.d(TAG, "isDistracter: " + isDistracter); + } + return isDistracter; + } + + private boolean checkDistracterUsingGetSuggestions(final String testedWord) { + if (mKeyboard == null) { + return false; + } + final SettingsValuesForSuggestion settingsValuesForSuggestion = + new SettingsValuesForSuggestion(false /* blockPotentiallyOffensive */, + false /* spaceAwareGestureEnabled */, + null /* additionalFeaturesSettingValues */); + final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); + final String consideredWord = trailingSingleQuotesCount > 0 ? + testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) : + testedWord; + final WordComposer composer = new WordComposer(); + final int[] codePoints = StringUtils.toCodePointArray(testedWord); + + synchronized (mLock) { + final int[] coordinates = mKeyboard.getCoordinates(codePoints); + composer.setComposingWord(codePoints, coordinates); + final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( + composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, mKeyboard.getProximityInfo(), + settingsValuesForSuggestion, 0 /* sessionId */); + if (suggestionResults.isEmpty()) { + return false; + } + final SuggestedWordInfo firstSuggestion = suggestionResults.first(); + final boolean isDistractor = suggestionExceedsDistracterThreshold( + firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); + if (DEBUG) { + Log.d(TAG, "isDistracter: " + isDistractor); + } + return isDistractor; + } + } + + private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion, + final String consideredWord, final float distracterThreshold) { + if (suggestion == null) { + return false; + } + final int suggestionScore = suggestion.mScore; + final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( + consideredWord, suggestion.mWord, suggestionScore); + if (DEBUG) { + Log.d(TAG, "normalizedScore: " + normalizedScore); + Log.d(TAG, "distracterThreshold: " + distracterThreshold); + } + if (normalizedScore > distracterThreshold) { + return true; + } + return false; + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java index a44c5764a..c2167a76b 100644 --- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java @@ -18,11 +18,16 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.dictionarypack.DictionarySettingsFragment; import com.android.inputmethod.latin.about.AboutPreferences; +import com.android.inputmethod.latin.settings.AdvancedSettingsFragment; +import com.android.inputmethod.latin.settings.AppearanceSettingsFragment; +import com.android.inputmethod.latin.settings.CorrectionSettingsFragment; import com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment; -import com.android.inputmethod.latin.settings.DebugSettings; -import com.android.inputmethod.latin.settings.InputSettingsFragment; +import com.android.inputmethod.latin.settings.DebugSettingsFragment; +import com.android.inputmethod.latin.settings.GestureSettingsFragment; import com.android.inputmethod.latin.settings.MultiLingualSettingsFragment; +import com.android.inputmethod.latin.settings.PreferencesSettingsFragment; import com.android.inputmethod.latin.settings.SettingsFragment; +import com.android.inputmethod.latin.settings.ThemeSettingsFragment; import com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment; import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragment; import com.android.inputmethod.latin.userdictionary.UserDictionaryList; @@ -36,10 +41,15 @@ public class FragmentUtils { static { sLatinImeFragments.add(DictionarySettingsFragment.class.getName()); sLatinImeFragments.add(AboutPreferences.class.getName()); - sLatinImeFragments.add(InputSettingsFragment.class.getName()); + sLatinImeFragments.add(PreferencesSettingsFragment.class.getName()); + sLatinImeFragments.add(AppearanceSettingsFragment.class.getName()); + sLatinImeFragments.add(ThemeSettingsFragment.class.getName()); sLatinImeFragments.add(MultiLingualSettingsFragment.class.getName()); sLatinImeFragments.add(CustomInputStyleSettingsFragment.class.getName()); - sLatinImeFragments.add(DebugSettings.class.getName()); + sLatinImeFragments.add(GestureSettingsFragment.class.getName()); + sLatinImeFragments.add(CorrectionSettingsFragment.class.getName()); + sLatinImeFragments.add(AdvancedSettingsFragment.class.getName()); + sLatinImeFragments.add(DebugSettingsFragment.class.getName()); sLatinImeFragments.add(SettingsFragment.class.getName()); sLatinImeFragments.add(SpellCheckerSettingsFragment.class.getName()); sLatinImeFragments.add(UserDictionaryAddWordFragment.class.getName()); diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java index 8b7077879..ea406fa75 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -23,15 +23,24 @@ import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; +import java.util.concurrent.TimeUnit; + public final class ImportantNoticeUtils { private static final String TAG = ImportantNoticeUtils.class.getSimpleName(); // {@link SharedPreferences} name to save the last important notice version that has been // displayed to users. private static final String PREFERENCE_NAME = "important_notice_pref"; - private static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + @UsedForTesting + static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + @UsedForTesting + static final String KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE = + "timestamp_of_first_important_notice"; + @UsedForTesting + static final long TIMEOUT_OF_IMPORTANT_NOTICE = TimeUnit.HOURS.toMillis(23); public static final int VERSION_TO_ENABLE_PERSONALIZED_SUGGESTIONS = 1; // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key. @@ -56,15 +65,18 @@ public final class ImportantNoticeUtils { } } - private static SharedPreferences getImportantNoticePreferences(final Context context) { + @UsedForTesting + static SharedPreferences getImportantNoticePreferences(final Context context) { return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); } - private static int getCurrentImportantNoticeVersion(final Context context) { + @UsedForTesting + static int getCurrentImportantNoticeVersion(final Context context) { return context.getResources().getInteger(R.integer.config_important_notice_version); } - private static int getLastImportantNoticeVersion(final Context context) { + @UsedForTesting + static int getLastImportantNoticeVersion(final Context context) { return getImportantNoticePreferences(context).getInt(KEY_IMPORTANT_NOTICE_VERSION, 0); } @@ -77,6 +89,20 @@ public final class ImportantNoticeUtils { return getCurrentImportantNoticeVersion(context) > lastVersion; } + @UsedForTesting + static boolean hasTimeoutPassed(final Context context, final long currentTimeInMillis) { + final SharedPreferences prefs = getImportantNoticePreferences(context); + if (!prefs.contains(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)) { + prefs.edit() + .putLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis) + .apply(); + } + final long firstDisplayTimeInMillis = prefs.getLong( + KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, currentTimeInMillis); + final long elapsedTime = currentTimeInMillis - firstDisplayTimeInMillis; + return elapsedTime >= TIMEOUT_OF_IMPORTANT_NOTICE; + } + public static boolean shouldShowImportantNotice(final Context context) { if (!hasNewImportantNotice(context)) { return false; @@ -88,6 +114,10 @@ public final class ImportantNoticeUtils { if (isInSystemSetupWizard(context)) { return false; } + if (hasTimeoutPassed(context, System.currentTimeMillis())) { + updateLastImportantNoticeVersion(context); + return false; + } return true; } @@ -95,11 +125,12 @@ public final class ImportantNoticeUtils { getImportantNoticePreferences(context) .edit() .putInt(KEY_IMPORTANT_NOTICE_VERSION, getNextImportantNoticeVersion(context)) + .remove(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE) .apply(); } public static String getNextImportantNoticeTitle(final Context context) { - final int nextVersion = getCurrentImportantNoticeVersion(context); + final int nextVersion = getNextImportantNoticeVersion(context); final String[] importantNoticeTitleArray = context.getResources().getStringArray( R.array.important_notice_title_array); if (nextVersion > 0 && nextVersion < importantNoticeTitleArray.length) { diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java index a76a6dfd7..0e244666d 100644 --- a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java @@ -26,13 +26,24 @@ public class ScriptUtils { // Used for hardware keyboards public static final int SCRIPT_UNKNOWN = -1; // TODO: should we use ISO 15924 identifiers instead? - public static final int SCRIPT_LATIN = 0; - public static final int SCRIPT_CYRILLIC = 1; - public static final int SCRIPT_GREEK = 2; - public static final int SCRIPT_ARABIC = 3; - public static final int SCRIPT_HEBREW = 4; - public static final int SCRIPT_ARMENIAN = 5; - public static final int SCRIPT_GEORGIAN = 6; + public static final int SCRIPT_ARABIC = 0; + public static final int SCRIPT_ARMENIAN = 1; + public static final int SCRIPT_BENGALI = 2; + public static final int SCRIPT_CYRILLIC = 3; + public static final int SCRIPT_DEVANAGARI = 4; + public static final int SCRIPT_GEORGIAN = 5; + public static final int SCRIPT_GREEK = 6; + public static final int SCRIPT_HEBREW = 7; + public static final int SCRIPT_KANNADA = 8; + public static final int SCRIPT_KHMER = 9; + public static final int SCRIPT_LAO = 10; + public static final int SCRIPT_LATIN = 11; + public static final int SCRIPT_MALAYALAM = 12; + public static final int SCRIPT_MYANMAR = 13; + public static final int SCRIPT_SINHALA = 14; + public static final int SCRIPT_TAMIL = 15; + public static final int SCRIPT_TELUGU = 16; + public static final int SCRIPT_THAI = 17; public static final TreeMap<String, Integer> mSpellCheckerLanguageToScript; static { // List of the supported languages and their associated script. We won't check @@ -72,56 +83,96 @@ public class ScriptUtils { */ public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) { switch (scriptId) { - case SCRIPT_LATIN: - // Our supported latin script dictionaries (EFIGS) at the moment only include - // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode - // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, - // so the below is a very efficient way to test for it. As for the 0-0x3F, it's - // excluded from isLetter anyway. - return codePoint <= 0x2AF && Character.isLetter(codePoint); - case SCRIPT_CYRILLIC: - // All Cyrillic characters are in the 400~52F block. There are some in the upper - // Unicode range, but they are archaic characters that are not used in modern - // Russian and are not used by our dictionary. - return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); - case SCRIPT_GREEK: - // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the - // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters. - // Our dictionary also contains a few words with 0xF2; it would be best to check - // if that's correct, but a web search does return results for these words so - // they are probably okay. - return (codePoint >= 0x370 && codePoint <= 0x3FF) - || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) - || codePoint == 0xF2; case SCRIPT_ARABIC: // Arabic letters can be in any of the following blocks: // Arabic U+0600..U+06FF - // Arabic Supplement U+0750..U+077F + // Arabic Supplement, Thaana U+0750..U+077F, U+0780..U+07BF // Arabic Extended-A U+08A0..U+08FF // Arabic Presentation Forms-A U+FB50..U+FDFF // Arabic Presentation Forms-B U+FE70..U+FEFF return (codePoint >= 0x600 && codePoint <= 0x6FF) - || (codePoint >= 0x750 && codePoint <= 0x77F) + || (codePoint >= 0x750 && codePoint <= 0x7BF) || (codePoint >= 0x8A0 && codePoint <= 0x8FF) || (codePoint >= 0xFB50 && codePoint <= 0xFDFF) || (codePoint >= 0xFE70 && codePoint <= 0xFEFF); - case SCRIPT_HEBREW: - // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF, - // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the - // Hebrew part of that block, which is U+FB1D..U+FB4F. - return (codePoint >= 0x590 && codePoint <= 0x5FF - || codePoint >= 0xFB1D && codePoint <= 0xFB4F); case SCRIPT_ARMENIAN: // Armenian letters are in the Armenian unicode block, U+0530..U+058F and // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part // of that block, which is U+FB13..U+FB17. return (codePoint >= 0x530 && codePoint <= 0x58F || codePoint >= 0xFB13 && codePoint <= 0xFB17); + case SCRIPT_BENGALI: + // Bengali unicode block is U+0980..U+09FF + return (codePoint >= 0x980 && codePoint <= 0x9FF); + case SCRIPT_CYRILLIC: + // All Cyrillic characters are in the 400~52F block. There are some in the upper + // Unicode range, but they are archaic characters that are not used in modern + // Russian and are not used by our dictionary. + return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); + case SCRIPT_DEVANAGARI: + // Devanagari unicode block is +0900..U+097F + return (codePoint >= 0x900 && codePoint <= 0x97F); case SCRIPT_GEORGIAN: // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF, // or Georgian supplement block, U+2D00..U+2D2F return (codePoint >= 0x10A0 && codePoint <= 0x10FF || codePoint >= 0x2D00 && codePoint <= 0x2D2F); + case SCRIPT_GREEK: + // Greek letters are either in the 370~3FF range (Greek & Coptic), or in the + // 1F00~1FFF range (Greek extended). Our dictionary contains both sort of characters. + // Our dictionary also contains a few words with 0xF2; it would be best to check + // if that's correct, but a web search does return results for these words so + // they are probably okay. + return (codePoint >= 0x370 && codePoint <= 0x3FF) + || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) + || codePoint == 0xF2; + case SCRIPT_HEBREW: + // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF, + // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the + // Hebrew part of that block, which is U+FB1D..U+FB4F. + return (codePoint >= 0x590 && codePoint <= 0x5FF + || codePoint >= 0xFB1D && codePoint <= 0xFB4F); + case SCRIPT_KANNADA: + // Kannada unicode block is U+0C80..U+0CFF + return (codePoint >= 0xC80 && codePoint <= 0xCFF); + case SCRIPT_KHMER: + // Khmer letters are in unicode block U+1780..U+17FF, and the Khmer symbols block + // is U+19E0..U+19FF + return (codePoint >= 0x1780 && codePoint <= 0x17FF + || codePoint >= 0x19E0 && codePoint <= 0x19FF); + case SCRIPT_LAO: + // The Lao block is U+0E80..U+0EFF + return (codePoint >= 0xE80 && codePoint <= 0xEFF); + case SCRIPT_LATIN: + // Our supported latin script dictionaries (EFIGS) at the moment only include + // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode + // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, + // so the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + return codePoint <= 0x2AF && Character.isLetter(codePoint); + case SCRIPT_MALAYALAM: + // Malayalam unicode block is U+0D00..U+0D7F + return (codePoint >= 0xD00 && codePoint <= 0xD7F); + case SCRIPT_MYANMAR: + // Myanmar has three unicode blocks : + // Myanmar U+1000..U+109F + // Myanmar extended-A U+AA60..U+AA7F + // Myanmar extended-B U+A9E0..U+A9FF + return (codePoint >= 0x1000 && codePoint <= 0x109F + || codePoint >= 0xAA60 && codePoint <= 0xAA7F + || codePoint >= 0xA9E0 && codePoint <= 0xA9FF); + case SCRIPT_SINHALA: + // Sinhala unicode block is U+0D80..U+0DFF + return (codePoint >= 0xD80 && codePoint <= 0xDFF); + case SCRIPT_TAMIL: + // Tamil unicode block is U+0B80..U+0BFF + return (codePoint >= 0xB80 && codePoint <= 0xBFF); + case SCRIPT_TELUGU: + // Telugu unicode block is U+0C00..U+0C7F + return (codePoint >= 0xC00 && codePoint <= 0xC7F); + case SCRIPT_THAI: + // Thai unicode block is U+0E00..U+0E7F + return (codePoint >= 0xE00 && codePoint <= 0xE7F); case SCRIPT_UNKNOWN: return true; default: diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index 38f0b3fee..79128dbd2 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -37,6 +37,14 @@ public final class StringUtils { private static final String EMPTY_STRING = ""; + private static final char CHAR_LINE_FEED = 0X000A; + private static final char CHAR_VERTICAL_TAB = 0X000B; + private static final char CHAR_FORM_FEED = 0X000C; + private static final char CHAR_CARRIAGE_RETURN = 0X000D; + private static final char CHAR_NEXT_LINE = 0X0085; + private static final char CHAR_LINE_SEPARATOR = 0X2028; + private static final char CHAR_PARAGRAPH_SEPARATOR = 0X2029; + private StringUtils() { // This utility class is not publicly instantiable. } @@ -123,20 +131,20 @@ public final class StringUtils { public static String capitalizeFirstCodePoint(final String s, final Locale locale) { if (s.length() <= 1) { - return s.toUpperCase(locale); + return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale); } // Please refer to the comment below in // {@link #capitalizeFirstAndDowncaseRest(String,Locale)} as this has the same shortcomings final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff); + return toUpperCaseOfStringForLocale( + s.substring(0, cutoff), true /* needsToUpperCase */, locale) + s.substring(cutoff); } public static String capitalizeFirstAndDowncaseRest(final String s, final Locale locale) { if (s.length() <= 1) { - return s.toUpperCase(locale); + return toUpperCaseOfStringForLocale(s, true /* needsToUpperCase */, locale); } // TODO: fix the bugs below - // - This does not work for Greek, because it returns upper case instead of title case. // - It does not work for Serbian, because it fails to account for the "lj" character, // which should be "Lj" in title case and "LJ" in upper case. // - It does not work for Dutch, because it fails to account for the "ij" digraph when it's @@ -144,7 +152,9 @@ public final class StringUtils { // be capitalized as "IJ" as if they were a single letter in most words (not all). If the // unicode char for the ligature is used however, it works. final int cutoff = s.offsetByCodePoints(0, 1); - return s.substring(0, cutoff).toUpperCase(locale) + s.substring(cutoff).toLowerCase(locale); + final String titleCaseFirstLetter = toUpperCaseOfStringForLocale( + s.substring(0, cutoff), true /* needsToUpperCase */, locale); + return titleCaseFirstLetter + s.substring(cutoff).toLowerCase(locale); } private static final int[] EMPTY_CODEPOINTS = {}; @@ -481,10 +491,23 @@ public final class StringUtils { return bytes; } + private static final String LANGUAGE_GREEK = "el"; + + private static Locale getLocaleUsedForToTitleCase(final Locale locale) { + // In Greek locale {@link String#toUpperCase(Locale)} eliminates accents from its result. + // In order to get accented upper case letter, {@link Locale#ROOT} should be used. + if (LANGUAGE_GREEK.equals(locale.getLanguage())) { + return Locale.ROOT; + } + return locale; + } + public static String toUpperCaseOfStringForLocale(final String text, final boolean needsToUpperCase, final Locale locale) { - if (text == null || !needsToUpperCase) return text; - return text.toUpperCase(locale); + if (text == null || !needsToUpperCase) { + return text; + } + return text.toUpperCase(getLocaleUsedForToTitleCase(locale)); } public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, @@ -594,4 +617,30 @@ public final class StringUtils { return sb + "]"; } } + + /** + * Returns whether the last composed word contains line-breaking character (e.g. CR or LF). + * @param text the text to be examined. + * @return {@code true} if the last composed word contains line-breaking separator. + */ + @UsedForTesting + public static boolean hasLineBreakCharacter(final String text) { + if (TextUtils.isEmpty(text)) { + return false; + } + for (int i = text.length() - 1; i >= 0; --i) { + final char c = text.charAt(i); + switch (c) { + case CHAR_LINE_FEED: + case CHAR_VERTICAL_TAB: + case CHAR_FORM_FEED: + case CHAR_CARRIAGE_RETURN: + case CHAR_NEXT_LINE: + case CHAR_LINE_SEPARATOR: + case CHAR_PARAGRAPH_SEPARATOR: + return true; + } + } + return false; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 7170bd789..8cd49509f 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -32,14 +32,18 @@ import java.util.TreeSet; public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { public final Locale mLocale; public final ArrayList<SuggestedWordInfo> mRawSuggestions; + // TODO: Instead of a boolean , we may want to include the context of this suggestion results, + // such as {@link PrevWordsInfo}. + public final boolean mIsBeginningOfSentence; private final int mCapacity; - public SuggestionResults(final Locale locale, final int capacity) { - this(locale, sSuggestedWordInfoComparator, capacity); + public SuggestionResults(final Locale locale, final int capacity, + final boolean isBeginningOfSentence) { + this(locale, sSuggestedWordInfoComparator, capacity, isBeginningOfSentence); } - public SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, - final int capacity) { + private SuggestionResults(final Locale locale, final Comparator<SuggestedWordInfo> comparator, + final int capacity, final boolean isBeginningOfSentence) { super(comparator); mLocale = locale; mCapacity = capacity; @@ -48,6 +52,7 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { } else { mRawSuggestions = null; } + mIsBeginningOfSentence = isBeginningOfSentence; } @Override diff --git a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java index f9d853493..dd122b634 100644 --- a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java @@ -19,7 +19,10 @@ package com.android.inputmethod.latin.utils; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; +import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; +import android.widget.LinearLayout; import android.widget.RelativeLayout; public final class ViewLayoutUtils { @@ -51,4 +54,40 @@ public final class ViewLayoutUtils { marginLayoutParams.setMargins(x, y, 0, 0); } } + + public static void updateLayoutHeightOf(final Window window, final int layoutHeight) { + final WindowManager.LayoutParams params = window.getAttributes(); + if (params.height != layoutHeight) { + params.height = layoutHeight; + window.setAttributes(params); + } + } + + public static void updateLayoutHeightOf(final View view, final int layoutHeight) { + final ViewGroup.LayoutParams params = view.getLayoutParams(); + if (params.height != layoutHeight) { + params.height = layoutHeight; + view.setLayoutParams(params); + } + } + + public static void updateLayoutGravityOf(final View view, final int layoutGravity) { + final ViewGroup.LayoutParams lp = view.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)lp; + if (params.gravity != layoutGravity) { + params.gravity = layoutGravity; + view.setLayoutParams(params); + } + } else if (lp instanceof FrameLayout.LayoutParams) { + final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)lp; + if (params.gravity != layoutGravity) { + params.gravity = layoutGravity; + view.setLayoutParams(params); + } + } else { + throw new IllegalArgumentException("Layout parameter doesn't have gravity: " + + lp.getClass().getName()); + } + } } diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk index 1cb61c45f..7a732a588 100644 --- a/native/jni/NativeFileList.mk +++ b/native/jni/NativeFileList.mk @@ -72,7 +72,7 @@ LATIN_IME_CORE_SRC_FILES := \ ver4_pt_node_array_reader.cpp) \ $(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \ bigram_dict_content.cpp \ - probability_dict_content.cpp \ + language_model_dict_content.cpp \ shortcut_dict_content.cpp \ sparse_table_dict_content.cpp \ terminal_position_lookup_table.cpp) \ @@ -84,7 +84,8 @@ LATIN_IME_CORE_SRC_FILES := \ forgetting_curve_utils.cpp \ format_utils.cpp \ mmapped_buffer.cpp \ - sparse_table.cpp) \ + sparse_table.cpp \ + trie_map.cpp ) \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ $(addprefix suggest/policyimpl/typing/, \ scoring_params.cpp \ @@ -124,5 +125,9 @@ LATIN_IME_CORE_TEST_FILES := \ defines_test.cpp \ suggest/core/layout/normal_distribution_2d_test.cpp \ suggest/core/dictionary/bloom_filter_test.cpp \ + suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp \ + suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp \ suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer_test.cpp \ - utils/autocorrection_threshold_utils_test.cpp + suggest/policyimpl/dictionary/utils/trie_map_test.cpp \ + utils/autocorrection_threshold_utils_test.cpp \ + utils/int_array_view_test.cpp diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 92f39ea25..d1b2c87be 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -117,7 +117,7 @@ class DicNode { int newPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; newPrevWordsPtNodePos[0] = dicNode->mDicNodeProperties.getPtNodePos(); for (size_t i = 1; i < NELEMS(newPrevWordsPtNodePos); ++i) { - newPrevWordsPtNodePos[i] = dicNode->getNthPrevWordTerminalPtNodePos(i); + newPrevWordsPtNodePos[i] = dicNode->getPrevWordsTerminalPtNodePos()[i - 1]; } mDicNodeProperties.init(rootPtNodeArrayPos, newPrevWordsPtNodePos); mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState, @@ -208,12 +208,9 @@ class DicNode { return mDicNodeProperties.getPtNodePos(); } - // Used to get n-gram probability in DicNodeUtils. n is 1-indexed. - int getNthPrevWordTerminalPtNodePos(const int n) const { - if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { - return NOT_A_DICT_POS; - } - return mDicNodeProperties.getPrevWordsTerminalPtNodePos()[n - 1]; + // TODO: Use view class to return PtNodePos array. + const int *getPrevWordsTerminalPtNodePos() const { + return mDicNodeProperties.getPrevWordsTerminalPtNodePos(); } // Used in DicNodeUtils diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 4445f4aaf..69ea67418 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -85,17 +85,10 @@ namespace latinime { const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy, const DicNode *const dicNode, MultiBigramMap *const multiBigramMap) { const int unigramProbability = dicNode->getProbability(); - const int ptNodePos = dicNode->getPtNodePos(); - const int prevWordTerminalPtNodePos = dicNode->getNthPrevWordTerminalPtNodePos(1 /* n */); - if (NOT_A_DICT_POS == ptNodePos || NOT_A_DICT_POS == prevWordTerminalPtNodePos) { - // Note: Normally wordPos comes from the dictionary and should never equal - // NOT_A_VALID_WORD_POS. - return dictionaryStructurePolicy->getProbability(unigramProbability, - NOT_A_PROBABILITY); - } if (multiBigramMap) { + const int *const prevWordsPtNodePos = dicNode->getPrevWordsTerminalPtNodePos(); return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, - prevWordTerminalPtNodePos, ptNodePos, unigramProbability); + prevWordsPtNodePos, dicNode->getPtNodePos(), unigramProbability); } return dictionaryStructurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY); diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index fb25f757c..d62573970 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -59,42 +59,48 @@ void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession } } +Dictionary::NgramListenerForPrediction::NgramListenerForPrediction( + const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const suggestionResults, + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy) + : mPrevWordsInfo(prevWordsInfo), mSuggestionResults(suggestionResults), + mDictStructurePolicy(dictStructurePolicy) {} + +void Dictionary::NgramListenerForPrediction::onVisitEntry(const int ngramProbability, + const int targetPtNodePos) { + if (targetPtNodePos == NOT_A_DICT_POS) { + return; + } + if (mPrevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */) + && ngramProbability == NOT_A_PROBABILITY) { + return; + } + int targetWordCodePoints[MAX_WORD_LENGTH]; + int unigramProbability = 0; + const int codePointCount = mDictStructurePolicy-> + getCodePointsAndProbabilityAndReturnCodePointCount(targetPtNodePos, + MAX_WORD_LENGTH, targetWordCodePoints, &unigramProbability); + if (codePointCount <= 0) { + return; + } + const int probability = mDictStructurePolicy->getProbability( + unigramProbability, ngramProbability); + mSuggestionResults->addPrediction(targetWordCodePoints, codePointCount, probability); +} + void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo, SuggestionResults *const outSuggestionResults) const { TimeKeeper::setCurrentTime(); - int unigramProbability = 0; - int bigramCodePoints[MAX_WORD_LENGTH]; - BinaryDictionaryBigramsIterator bigramsIt = prevWordsInfo->getBigramsIteratorForPrediction( + NgramListenerForPrediction listener(prevWordsInfo, outSuggestionResults, mDictionaryStructureWithBufferPolicy.get()); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) { - continue; - } - if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */) - && bigramsIt.getProbability() == NOT_A_PROBABILITY) { - continue; - } - const int codePointCount = mDictionaryStructureWithBufferPolicy-> - getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(), - MAX_WORD_LENGTH, bigramCodePoints, &unigramProbability); - if (codePointCount <= 0) { - continue; - } - const int probability = mDictionaryStructureWithBufferPolicy->getProbability( - unigramProbability, bigramsIt.getProbability()); - outSuggestionResults->addPrediction(bigramCodePoints, codePointCount, probability); - } + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos( + mDictionaryStructureWithBufferPolicy.get(), prevWordsPtNodePos, + true /* tryLowerCaseSearch */); + mDictionaryStructureWithBufferPolicy->iterateNgramEntries(prevWordsPtNodePos, &listener); } int Dictionary::getProbability(const int *word, int length) const { - TimeKeeper::setCurrentTime(); - int pos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(word, length, - false /* forceLowerCaseSearch */); - if (NOT_A_DICT_POS == pos) { - return NOT_A_PROBABILITY; - } - return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos); + return getNgramProbability(nullptr /* prevWordsInfo */, word, length); } int Dictionary::getMaxProbabilityOfExactMatches(const int *word, int length) const { @@ -109,18 +115,15 @@ int Dictionary::getNgramProbability(const PrevWordsInfo *const prevWordsInfo, co int nextWordPos = mDictionaryStructureWithBufferPolicy->getTerminalPtNodePositionOfWord(word, length, false /* forceLowerCaseSearch */); if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY; - BinaryDictionaryBigramsIterator bigramsIt = prevWordsInfo->getBigramsIteratorForPrediction( - mDictionaryStructureWithBufferPolicy.get()); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == nextWordPos - && bigramsIt.getProbability() != NOT_A_PROBABILITY) { - return mDictionaryStructureWithBufferPolicy->getProbability( - mDictionaryStructureWithBufferPolicy->getUnigramProbabilityOfPtNode( - nextWordPos), bigramsIt.getProbability()); - } + if (!prevWordsInfo) { + return getDictionaryStructurePolicy()->getProbabilityOfPtNode( + nullptr /* prevWordsPtNodePos */, nextWordPos); } - return NOT_A_PROBABILITY; + int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + prevWordsInfo->getPrevWordsTerminalPtNodePos( + mDictionaryStructureWithBufferPolicy.get(), prevWordsPtNodePos, + true /* tryLowerCaseSearch */); + return getDictionaryStructurePolicy()->getProbabilityOfPtNode(prevWordsPtNodePos, nextWordPos); } bool Dictionary::addUnigramEntry(const int *const word, const int length, diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index 3b41088fe..732d3b199 100644 --- a/native/jni/src/suggest/core/dictionary/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -21,6 +21,7 @@ #include "defines.h" #include "jni.h" +#include "suggest/core/dictionary/ngram_listener.h" #include "suggest/core/dictionary/property/word_property.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" @@ -114,6 +115,21 @@ class Dictionary { typedef std::unique_ptr<SuggestInterface> SuggestInterfacePtr; + class NgramListenerForPrediction : public NgramListener { + public: + NgramListenerForPrediction(const PrevWordsInfo *const prevWordsInfo, + SuggestionResults *const suggestionResults, + const DictionaryStructureWithBufferPolicy *const dictStructurePolicy); + virtual void onVisitEntry(const int ngramProbability, const int targetPtNodePos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(NgramListenerForPrediction); + + const PrevWordsInfo *const mPrevWordsInfo; + SuggestionResults *const mSuggestionResults; + const DictionaryStructureWithBufferPolicy *const mDictStructurePolicy; + }; + static const int HEADER_ATTRIBUTE_BUFFER_SIZE; const DictionaryStructureWithBufferPolicy::StructurePolicyPtr diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp index 012e4dc9c..91f33a8dd 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.cpp @@ -35,34 +35,30 @@ const int MultiBigramMap::BigramMap::DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP = // Also caches the bigrams if there is space remaining and they have not been cached already. int MultiBigramMap::getBigramProbability( const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int wordPosition, const int nextWordPosition, const int unigramProbability) { + const int *const prevWordsPtNodePos, const int nextWordPosition, + const int unigramProbability) { + if (!prevWordsPtNodePos || prevWordsPtNodePos[0] == NOT_A_DICT_POS) { + return structurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY); + } std::unordered_map<int, BigramMap>::const_iterator mapPosition = - mBigramMaps.find(wordPosition); + mBigramMaps.find(prevWordsPtNodePos[0]); if (mapPosition != mBigramMaps.end()) { return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition, unigramProbability); } if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) { - addBigramsForWordPosition(structurePolicy, wordPosition); - return mBigramMaps[wordPosition].getBigramProbability(structurePolicy, + addBigramsForWordPosition(structurePolicy, prevWordsPtNodePos); + return mBigramMaps[prevWordsPtNodePos[0]].getBigramProbability(structurePolicy, nextWordPosition, unigramProbability); } - return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition, + return readBigramProbabilityFromBinaryDictionary(structurePolicy, prevWordsPtNodePos, nextWordPosition, unigramProbability); } void MultiBigramMap::BigramMap::init( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos) { - BinaryDictionaryBigramsIterator bigramsIt = - structurePolicy->getBigramsIteratorOfPtNode(nodePos); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) { - continue; - } - mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability(); - mBloomFilter.setInFilter(bigramsIt.getBigramPos()); - } + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int *const prevWordsPtNodePos) { + structurePolicy->iterateNgramEntries(prevWordsPtNodePos, this /* listener */); } int MultiBigramMap::BigramMap::getBigramProbability( @@ -79,25 +75,33 @@ int MultiBigramMap::BigramMap::getBigramProbability( return structurePolicy->getProbability(unigramProbability, bigramProbability); } +void MultiBigramMap::BigramMap::onVisitEntry(const int ngramProbability, + const int targetPtNodePos) { + if (targetPtNodePos == NOT_A_DICT_POS) { + return; + } + mBigramMap[targetPtNodePos] = ngramProbability; + mBloomFilter.setInFilter(targetPtNodePos); +} + void MultiBigramMap::addBigramsForWordPosition( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) { - mBigramMaps[position].init(structurePolicy, position); + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int *const prevWordsPtNodePos) { + if (prevWordsPtNodePos) { + mBigramMaps[prevWordsPtNodePos[0]].init(structurePolicy, prevWordsPtNodePos); + } } int MultiBigramMap::readBigramProbabilityFromBinaryDictionary( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, - const int nextWordPosition, const int unigramProbability) { - int bigramProbability = NOT_A_PROBABILITY; - BinaryDictionaryBigramsIterator bigramsIt = - structurePolicy->getBigramsIteratorOfPtNode(nodePos); - while (bigramsIt.hasNext()) { - bigramsIt.next(); - if (bigramsIt.getBigramPos() == nextWordPosition) { - bigramProbability = bigramsIt.getProbability(); - break; - } + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int *const prevWordsPtNodePos, const int nextWordPosition, + const int unigramProbability) { + const int bigramProbability = structurePolicy->getProbabilityOfPtNode(prevWordsPtNodePos, + nextWordPosition); + if (bigramProbability != NOT_A_PROBABILITY) { + return bigramProbability; } - return structurePolicy->getProbability(unigramProbability, bigramProbability); + return structurePolicy->getProbability(unigramProbability, NOT_A_PROBABILITY); } } // namespace latinime diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h index 195b5e22f..ad36dde83 100644 --- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h @@ -23,6 +23,7 @@ #include "defines.h" #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/bloom_filter.h" +#include "suggest/core/dictionary/ngram_listener.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" namespace latinime { @@ -38,7 +39,8 @@ class MultiBigramMap { // Look up the bigram probability for the given word pair from the cached bigram maps. // Also caches the bigrams if there is space remaining and they have not been cached already. int getBigramProbability(const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int wordPosition, const int nextWordPosition, const int unigramProbability); + const int *const prevWordsPtNodePos, const int nextWordPosition, + const int unigramProbability); void clear() { mBigramMaps.clear(); @@ -47,32 +49,35 @@ class MultiBigramMap { private: DISALLOW_COPY_AND_ASSIGN(MultiBigramMap); - class BigramMap { + class BigramMap : public NgramListener { public: BigramMap() : mBigramMap(DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP), mBloomFilter() {} - ~BigramMap() {} + // Copy constructor needed for std::unordered_map. + BigramMap(const BigramMap &bigramMap) + : mBigramMap(bigramMap.mBigramMap), mBloomFilter(bigramMap.mBloomFilter) {} + virtual ~BigramMap() {} void init(const DictionaryStructureWithBufferPolicy *const structurePolicy, - const int nodePos); - + const int *const prevWordsPtNodePos); int getBigramProbability( const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nextWordPosition, const int unigramProbability) const; + virtual void onVisitEntry(const int ngramProbability, const int targetPtNodePos); private: - // NOTE: The BigramMap class doesn't use DISALLOW_COPY_AND_ASSIGN() because its default - // copy constructor is needed for use in hash_map. static const int DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP; std::unordered_map<int, int> mBigramMap; BloomFilter mBloomFilter; }; void addBigramsForWordPosition( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position); + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int *const prevWordsPtNodePos); int readBigramProbabilityFromBinaryDictionary( - const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos, - const int nextWordPosition, const int unigramProbability); + const DictionaryStructureWithBufferPolicy *const structurePolicy, + const int *const prevWordsPtNodePos, const int nextWordPosition, + const int unigramProbability); static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP; std::unordered_map<int, BigramMap> mBigramMaps; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dict_content.h b/native/jni/src/suggest/core/dictionary/ngram_listener.h index c264aeac4..88b88bafb 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/dict_content.h +++ b/native/jni/src/suggest/core/dictionary/ngram_listener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013, The Android Open Source Project + * Copyright (C) 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,22 +14,27 @@ * limitations under the License. */ -#ifndef LATINIME_DICT_CONTENT_H -#define LATINIME_DICT_CONTENT_H +#ifndef LATINIME_NGRAM_LISTENER_H +#define LATINIME_NGRAM_LISTENER_H #include "defines.h" namespace latinime { -class DictContent { +/** + * Interface to iterate ngram entries. + */ +class NgramListener { public: - virtual ~DictContent() {} + virtual void onVisitEntry(const int ngramProbability, const int targetPtNodePos) = 0; + virtual ~NgramListener() {}; protected: - DictContent() {} + NgramListener() {} private: - DISALLOW_COPY_AND_ASSIGN(DictContent); + DISALLOW_COPY_AND_ASSIGN(NgramListener); + }; } // namespace latinime -#endif /* LATINIME_DICT_CONTENT_H */ +#endif /* LATINIME_NGRAM_LISTENER_H */ 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 6b1a319aa..e6180fe17 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state.h @@ -215,13 +215,13 @@ class ProximityInfoState { std::vector<float> mSpeedRates; std::vector<float> mDirections; // probabilities of skipping or mapping to a key for each point. - std::vector<std::unordered_map<int, float> > mCharProbabilities; + std::vector<std::unordered_map<int, float>> mCharProbabilities; // The vector for the key code set which holds nearby keys of some trailing sampled input points // for each sampled input point. These nearby keys contain the next characters which can be in // the dictionary. Specifically, currently we are looking for keys nearby trailing sampled // inputs including the current input point. std::vector<ProximityInfoStateUtils::NearKeycodesSet> mSampledSearchKeySets; - std::vector<std::vector<int> > mSampledSearchKeyVectors; + std::vector<std::vector<int>> mSampledSearchKeyVectors; bool mTouchPositionCorrectionEnabled; int mInputProximities[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH]; int mSampledInputSize; 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 ea3b02216..0aeb36aad 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 @@ -621,7 +621,7 @@ namespace latinime { const std::vector<int> *const sampledLengthCache, const std::vector<float> *const sampledNormalizedSquaredLengthCache, const ProximityInfo *const proximityInfo, - std::vector<std::unordered_map<int, float> > *charProbabilities) { + std::vector<std::unordered_map<int, float>> *charProbabilities) { charProbabilities->resize(sampledInputSize); // Calculates probabilities of using a point as a correlated point with the character // for each point. @@ -822,9 +822,9 @@ namespace latinime { /* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets( const ProximityInfo *const proximityInfo, const int sampledInputSize, const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache, - const std::vector<std::unordered_map<int, float> > *const charProbabilities, + const std::vector<std::unordered_map<int, float>> *const charProbabilities, std::vector<NearKeycodesSet> *sampledSearchKeySets, - std::vector<std::vector<int> > *sampledSearchKeyVectors) { + std::vector<std::vector<int>> *sampledSearchKeyVectors) { sampledSearchKeySets->resize(sampledInputSize); sampledSearchKeyVectors->resize(sampledInputSize); const int readForwordLength = static_cast<int>( @@ -868,7 +868,7 @@ namespace latinime { /* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth, const int sampledInputSize, const std::vector<int> *const lengthCache, const int index0, const int index1, - std::vector<std::unordered_map<int, float> > *charProbabilities) { + std::vector<std::unordered_map<int, float>> *charProbabilities) { ASSERT(0 <= index0 && index0 < sampledInputSize); ASSERT(0 <= index1 && index1 < sampledInputSize); const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth); @@ -933,7 +933,7 @@ namespace latinime { // returns probability of generating the word. /* static */ float ProximityInfoStateUtils::getMostProbableString( const ProximityInfo *const proximityInfo, const int sampledInputSize, - const std::vector<std::unordered_map<int, float> > *const charProbabilities, + const std::vector<std::unordered_map<int, float>> *const charProbabilities, int *const codePointBuf) { ASSERT(sampledInputSize >= 0); memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH); diff --git a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h index 211a79737..4043334e6 100644 --- a/native/jni/src/suggest/core/layout/proximity_info_state_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h @@ -72,13 +72,13 @@ class ProximityInfoStateUtils { const std::vector<int> *const sampledLengthCache, const std::vector<float> *const sampledNormalizedSquaredLengthCache, const ProximityInfo *const proximityInfo, - std::vector<std::unordered_map<int, float> > *charProbabilities); + std::vector<std::unordered_map<int, float>> *charProbabilities); static void updateSampledSearchKeySets(const ProximityInfo *const proximityInfo, const int sampledInputSize, const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache, - const std::vector<std::unordered_map<int, float> > *const charProbabilities, + const std::vector<std::unordered_map<int, float>> *const charProbabilities, std::vector<NearKeycodesSet> *sampledSearchKeySets, - std::vector<std::vector<int> > *sampledSearchKeyVectors); + std::vector<std::vector<int>> *sampledSearchKeyVectors); static float getPointToKeyByIdLength(const float maxPointToKeyLength, const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount, const int inputIndex, const int keyId); @@ -105,7 +105,7 @@ class ProximityInfoStateUtils { // TODO: Move to most_probable_string_utils.h static float getMostProbableString(const ProximityInfo *const proximityInfo, const int sampledInputSize, - const std::vector<std::unordered_map<int, float> > *const charProbabilities, + const std::vector<std::unordered_map<int, float>> *const charProbabilities, int *const codePointBuf); private: @@ -147,7 +147,7 @@ class ProximityInfoStateUtils { const int index2); static bool suppressCharProbabilities(const int mostCommonKeyWidth, const int sampledInputSize, const std::vector<int> *const lengthCache, const int index0, - const int index1, std::vector<std::unordered_map<int, float> > *charProbabilities); + const int index1, std::vector<std::unordered_map<int, float>> *charProbabilities); static float calculateSquaredDistanceFromSweetSpotCenter( const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs, const int keyIndex, diff --git a/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h index 661ef1b1a..aa0d068aa 100644 --- a/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h @@ -30,7 +30,7 @@ class DictionaryBigramsStructurePolicy { virtual void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const pos) const = 0; - virtual void skipAllBigrams(int *const pos) const = 0; + virtual bool skipAllBigrams(int *const pos) const = 0; protected: DictionaryBigramsStructurePolicy() {} diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h index a61227626..6da390e55 100644 --- a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h +++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h @@ -30,7 +30,7 @@ namespace latinime { */ class DictionaryHeaderStructurePolicy { public: - typedef std::map<std::vector<int>, std::vector<int> > AttributeMap; + typedef std::map<std::vector<int>, std::vector<int>> AttributeMap; virtual ~DictionaryHeaderStructurePolicy() {} diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h index a48d64473..e91f07682 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 @@ -20,16 +20,15 @@ #include <memory> #include "defines.h" -#include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" #include "suggest/core/dictionary/property/word_property.h" namespace latinime { class DicNode; class DicNodeVector; -class DictionaryBigramsStructurePolicy; class DictionaryHeaderStructurePolicy; class DictionaryShortcutsStructurePolicy; +class NgramListener; class PrevWordsInfo; class UnigramProperty; @@ -58,11 +57,13 @@ class DictionaryStructureWithBufferPolicy { virtual int getProbability(const int unigramProbability, const int bigramProbability) const = 0; - virtual int getUnigramProbabilityOfPtNode(const int nodePos) const = 0; + virtual int getProbabilityOfPtNode(const int *const prevWordsPtNodePos, + const int nodePos) const = 0; - virtual int getShortcutPositionOfPtNode(const int nodePos) const = 0; + virtual void iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const = 0; - virtual BinaryDictionaryBigramsIterator getBigramsIteratorOfPtNode(const int nodePos) const = 0; + virtual int getShortcutPositionOfPtNode(const int nodePos) const = 0; virtual const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const = 0; diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h index 292194bf2..9e75cace4 100644 --- a/native/jni/src/suggest/core/policy/scoring.h +++ b/native/jni/src/suggest/core/policy/scoring.h @@ -37,7 +37,6 @@ class Scoring { DicNode *const terminals, const int size) const = 0; virtual float getDoubleLetterDemotionDistanceCost( const DicNode *const terminalDicNode) const = 0; - virtual bool doesAutoCorrectValidWord() const = 0; virtual bool autoCorrectsToMultiWordSuggestionIfTop() const = 0; virtual bool sameAsTyped(const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const = 0; diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp index 7b0e7e1b4..0b99b75ec 100644 --- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp +++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp @@ -117,8 +117,7 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; const int finalScore = scoringPolicy->calculateFinalScore( compoundDistance, traverseSession->getInputSize(), terminalDicNode->getContainedErrorTypes(), - (forceCommitMultiWords && terminalDicNode->hasMultipleWords()) - || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()), + (forceCommitMultiWords && terminalDicNode->hasMultipleWords()), boostExactMatches); // Don't output invalid or blocked offensive words. However, we still need to submit their @@ -145,12 +144,7 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; traverseSession->getDictionaryStructurePolicy() ->getShortcutPositionOfPtNode(terminalDicNode->getPtNodePos())); const bool sameAsTyped = scoringPolicy->sameAsTyped(traverseSession, terminalDicNode); - const int shortcutBaseScore = scoringPolicy->doesAutoCorrectValidWord() ? - scoringPolicy->calculateFinalScore(compoundDistance, - traverseSession->getInputSize(), - terminalDicNode->getContainedErrorTypes(), - true /* forceCommit */, boostExactMatches) : finalScore; - outputShortcuts(&shortcutIt, shortcutBaseScore, sameAsTyped, outSuggestionResults); + outputShortcuts(&shortcutIt, finalScore, sameAsTyped, outSuggestionResults); } } diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/prev_words_info.h index 76276f528..e44e876e9 100644 --- a/native/jni/src/suggest/core/session/prev_words_info.h +++ b/native/jni/src/suggest/core/session/prev_words_info.h @@ -90,13 +90,6 @@ class PrevWordsInfo { } } - BinaryDictionaryBigramsIterator getBigramsIteratorForPrediction( - const DictionaryStructureWithBufferPolicy *const dictStructurePolicy) const { - return getBigramsIteratorForWordWithTryingLowerCaseSearch( - dictStructurePolicy, mPrevWordCodePoints[0], mPrevWordCodePointCount[0], - mIsBeginningOfSentence[0]); - } - // n is 1-indexed. const int *getNthPrevWordCodePoints(const int n) const { if (n <= 0 || n > MAX_PREV_WORD_COUNT_FOR_N_GRAM) { @@ -154,46 +147,6 @@ class PrevWordsInfo { codePoints, codePointCount, true /* forceLowerCaseSearch */); } - static BinaryDictionaryBigramsIterator getBigramsIteratorForWordWithTryingLowerCaseSearch( - const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, - const int *const wordCodePoints, const int wordCodePointCount, - const bool isBeginningOfSentence) { - if (!dictStructurePolicy || !wordCodePoints || wordCodePointCount > MAX_WORD_LENGTH) { - return BinaryDictionaryBigramsIterator(); - } - int codePoints[MAX_WORD_LENGTH]; - int codePointCount = wordCodePointCount; - memmove(codePoints, wordCodePoints, sizeof(int) * codePointCount); - if (isBeginningOfSentence) { - codePointCount = CharUtils::attachBeginningOfSentenceMarker(codePoints, - codePointCount, MAX_WORD_LENGTH); - if (codePointCount <= 0) { - return BinaryDictionaryBigramsIterator(); - } - } - BinaryDictionaryBigramsIterator bigramsIt = getBigramsIteratorForWord(dictStructurePolicy, - codePoints, codePointCount, false /* forceLowerCaseSearch */); - // getBigramsIteratorForWord returns an empty iterator if this word isn't in the dictionary - // or has no bigrams. - if (bigramsIt.hasNext()) { - return bigramsIt; - } - // If no bigrams for this exact word, search again in lower case. - return getBigramsIteratorForWord(dictStructurePolicy, codePoints, - codePointCount, true /* forceLowerCaseSearch */); - } - - static BinaryDictionaryBigramsIterator getBigramsIteratorForWord( - const DictionaryStructureWithBufferPolicy *const dictStructurePolicy, - const int *wordCodePoints, const int wordCodePointCount, - const bool forceLowerCaseSearch) { - if (!wordCodePoints || wordCodePointCount <= 0) return BinaryDictionaryBigramsIterator(); - const int terminalPtNodePos = dictStructurePolicy->getTerminalPtNodePositionOfWord( - wordCodePoints, wordCodePointCount, forceLowerCaseSearch); - if (NOT_A_DICT_POS == terminalPtNodePos) return BinaryDictionaryBigramsIterator(); - return dictStructurePolicy->getBigramsIteratorOfPtNode(terminalPtNodePos); - } - void clear() { for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) { mPrevWordCodePointCount[i] = 0; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h index 61623468e..50a4c9743 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/bigram/ver4_bigram_list_policy.h @@ -58,8 +58,9 @@ class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const bigramEntryPos) const; - void skipAllBigrams(int *const pos) const { + bool skipAllBigrams(int *const pos) const { // Do nothing because we don't need to skip bigram lists in ver4 dictionaries. + return true; } bool addNewEntry(const int terminalId, const int newTargetTerminalId, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h index 6433650b0..49f446814 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/single_dict_content.h @@ -30,6 +30,7 @@ #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" +#include "utils/byte_array_view.h" namespace latinime { namespace backward { @@ -40,8 +41,9 @@ class SingleDictContent : public DictContent { SingleDictContent(const char *const dictPath, const char *const contentFileName, const bool isUpdatable) : mMmappedBuffer(MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), - mExpandableContentBuffer(mMmappedBuffer ? mMmappedBuffer->getBuffer() : nullptr, - mMmappedBuffer ? mMmappedBuffer->getBufferSize() : 0, + mExpandableContentBuffer( + mMmappedBuffer ? mMmappedBuffer->getReadWriteByteArrayView() : + ReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mIsValid(mMmappedBuffer) {} diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h index c7233edd3..3c626df11 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/sparse_table_dict_content.h @@ -31,6 +31,7 @@ #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" #include "suggest/policyimpl/dictionary/utils/sparse_table.h" +#include "utils/byte_array_view.h" namespace latinime { namespace backward { @@ -50,15 +51,16 @@ class SparseTableDictContent : public DictContent { mContentBuffer( MmappedBuffer::openBuffer(dictPath, contentFileName, isUpdatable)), mExpandableLookupTableBuffer( - mLookupTableBuffer ? mLookupTableBuffer->getBuffer() : nullptr, - mLookupTableBuffer ? mLookupTableBuffer->getBufferSize() : 0, + mLookupTableBuffer ? mLookupTableBuffer->getReadWriteByteArrayView() : + ReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mExpandableAddressTableBuffer( - mAddressTableBuffer ? mAddressTableBuffer->getBuffer() : nullptr, - mAddressTableBuffer ? mAddressTableBuffer->getBufferSize() : 0, + mAddressTableBuffer ? mAddressTableBuffer->getReadWriteByteArrayView() : + ReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableContentBuffer(mContentBuffer ? mContentBuffer->getBuffer() : nullptr, - mContentBuffer ? mContentBuffer->getBufferSize() : 0, + mExpandableContentBuffer( + mContentBuffer ? mContentBuffer->getReadWriteByteArrayView() : + ReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, sparseTableBlockSize, sparseTableDataSize), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp index 93f192976..3dfbd1c94 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_dict_buffers.cpp @@ -30,6 +30,7 @@ #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include "suggest/policyimpl/dictionary/utils/file_utils.h" +#include "utils/byte_array_view.h" namespace latinime { namespace backward { @@ -130,12 +131,12 @@ Ver4DictBuffers::Ver4DictBuffers(const char *const dictPath, : mHeaderBuffer(std::move(headerBuffer)), mDictBuffer(MmappedBuffer::openBuffer(dictPath, Ver4DictConstants::TRIE_FILE_EXTENSION, isUpdatable)), - mHeaderPolicy(mHeaderBuffer->getBuffer(), formatVersion), - mExpandableHeaderBuffer(mHeaderBuffer ? mHeaderBuffer->getBuffer() : nullptr, - mHeaderPolicy.getSize(), + mHeaderPolicy(mHeaderBuffer->getReadOnlyByteArrayView().data(), formatVersion), + mExpandableHeaderBuffer(mHeaderBuffer->getReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableTrieBuffer(mDictBuffer ? mDictBuffer->getBuffer() : nullptr, - mDictBuffer ? mDictBuffer->getBufferSize() : 0, + mExpandableTrieBuffer( + mDictBuffer ? mDictBuffer->getReadWriteByteArrayView() : + ReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mTerminalPositionLookupTable(dictPath, isUpdatable), mProbabilityDictContent(dictPath, mHeaderPolicy.hasHistoricalInfoOfWords(), isUpdatable), diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp index 4220a9561..278f2b199 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp @@ -231,30 +231,31 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( &probabilityEntryToWrite); } -bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry( - const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam, - const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { - if (!mBigramPolicy->addNewEntry(sourcePtNodeParams->getTerminalId(), - targetPtNodeParam->getTerminalId(), bigramProperty, outAddedNewBigram)) { +bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) { + if (!mBigramPolicy->addNewEntry(prevWordIds[0], wordId, bigramProperty, outAddedNewEntry)) { AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); return false; } - if (!sourcePtNodeParams->hasBigrams()) { + const int ptNodePos = + mBuffers->getTerminalPositionLookupTable()->getTerminalPtNodePosition(prevWordIds[0]); + const PtNodeParams sourcePtNodeParams = + mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); + if (!sourcePtNodeParams.hasBigrams()) { // Update has bigrams flag. - return updatePtNodeFlags(sourcePtNodeParams->getHeadPos(), - sourcePtNodeParams->isBlacklisted(), sourcePtNodeParams->isNotAWord(), - sourcePtNodeParams->isTerminal(), sourcePtNodeParams->hasShortcutTargets(), + return updatePtNodeFlags(sourcePtNodeParams.getHeadPos(), + sourcePtNodeParams.isBlacklisted(), sourcePtNodeParams.isNotAWord(), + sourcePtNodeParams.isTerminal(), sourcePtNodeParams.hasShortcutTargets(), true /* hasBigrams */, - sourcePtNodeParams->getCodePointCount() > 1 /* hasMultipleChars */); + sourcePtNodeParams.getCodePointCount() > 1 /* hasMultipleChars */); } return true; } -bool Ver4PatriciaTrieNodeWriter::removeBigramEntry( - const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam) { - return mBigramPolicy->removeEntry(sourcePtNodeParams->getTerminalId(), - targetPtNodeParam->getTerminalId()); +bool Ver4PatriciaTrieNodeWriter::removeNgramEntry(const WordIdArrayView prevWordIds, + const int wordId) { + return mBigramPolicy->removeEntry(prevWordIds[0], wordId); } bool Ver4PatriciaTrieNodeWriter::updateAllBigramEntriesAndDeleteUselessEntries( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h index 08226ea26..d49d9a666 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h @@ -29,6 +29,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h" #include "suggest/policyimpl/dictionary/structure/backward/v402/content/probability_entry.h" +#include "utils/int_array_view.h" namespace latinime { namespace backward { @@ -61,8 +62,8 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { const PtNodeArrayReader *const ptNodeArrayReader, Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy) : mTrieBuffer(trieBuffer), mBuffers(buffers), mHeaderPolicy(headerPolicy), - mReadingHelper(ptNodeReader, ptNodeArrayReader), mBigramPolicy(bigramPolicy), - mShortcutPolicy(shortcutPolicy) {} + mPtNodeReader(ptNodeReader), mReadingHelper(ptNodeReader, ptNodeArrayReader), + mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy) {} virtual ~Ver4PatriciaTrieNodeWriter() {} @@ -92,12 +93,10 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos); - virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, - bool *const outAddedNewBigram); + virtual bool addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); - virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam); + virtual bool removeNgramEntry(const WordIdArrayView prevWordIds, const int wordId); virtual bool updateAllBigramEntriesAndDeleteUselessEntries( const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount); @@ -135,6 +134,7 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { BufferWithExtendableBuffer *const mTrieBuffer; Ver4DictBuffers *const mBuffers; const HeaderPolicy *const mHeaderPolicy; + const PtNodeReader *const mPtNodeReader; DynamicPtReadingHelper mReadingHelper; Ver4BigramListPolicy *const mBigramPolicy; Ver4ShortcutListPolicy *const mShortcutPolicy; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp index f478d9b91..1296b8acd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -28,6 +28,7 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/ngram_listener.h" #include "suggest/core/dictionary/property/bigram_property.h" #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" @@ -131,7 +132,8 @@ int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability, } } -int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { +int Ver4PatriciaTriePolicy::getProbabilityOfPtNode(const int *const prevWordsPtNodePos, + const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_PROBABILITY; } @@ -139,9 +141,34 @@ int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) c if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { return NOT_A_PROBABILITY; } + if (prevWordsPtNodePos) { + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + if (bigramsIt.getBigramPos() == ptNodePos + && bigramsIt.getProbability() != NOT_A_PROBABILITY) { + return getProbability(ptNodeParams.getProbability(), bigramsIt.getProbability()); + } + } + return NOT_A_PROBABILITY; + } return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY); } +void Ver4PatriciaTriePolicy::iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const { + if (!prevWordsPtNodePos) { + return; + } + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + listener->onVisitEntry(bigramsIt.getProbability(), bigramsIt.getBigramPos()); + } +} + int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; @@ -154,12 +181,6 @@ int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) con ptNodeParams.getTerminalId()); } -BinaryDictionaryBigramsIterator Ver4PatriciaTriePolicy::getBigramsIteratorOfPtNode( - const int ptNodePos) const { - const int bigramsPosition = getBigramsPositionOfPtNode(ptNodePos); - return BinaryDictionaryBigramsIterator(&mBigramPolicy, bigramsPosition); -} - int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; @@ -288,8 +309,8 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI return false; } bool addedNewBigram = false; - if (mUpdatingHelper.addBigramWords(prevWordsPtNodePos[0], word1Pos, bigramProperty, - &addedNewBigram)) { + if (mUpdatingHelper.addNgramEntry(PtNodePosArrayView::fromObject(prevWordsPtNodePos), + word1Pos, bigramProperty, &addedNewBigram)) { if (addedNewBigram) { mBigramCount++; } @@ -329,7 +350,8 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor if (wordPos == NOT_A_DICT_POS) { return false; } - if (mUpdatingHelper.removeBigramWords(prevWordsPtNodePos[0], wordPos)) { + if (mUpdatingHelper.removeNgramEntry( + PtNodePosArrayView::fromObject(prevWordsPtNodePos), wordPos)) { mBigramCount--; return true; } else { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h index 6d97c7cc8..9e989b268 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h @@ -90,11 +90,12 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getProbability(const int unigramProbability, const int bigramProbability) const; - int getUnigramProbabilityOfPtNode(const int ptNodePos) const; + int getProbabilityOfPtNode(const int *const prevWordsPtNodePos, const int ptNodePos) const; - int getShortcutPositionOfPtNode(const int ptNodePos) const; + void iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const; - BinaryDictionaryBigramsIterator getBigramsIteratorOfPtNode(const int ptNodePos) const; + int getShortcutPositionOfPtNode(const int ptNodePos) const; const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { return mHeaderPolicy; 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 e4b5fa267..e4ea3da16 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 @@ -31,6 +31,7 @@ #include "suggest/policyimpl/dictionary/utils/file_utils.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -110,7 +111,8 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str return nullptr; } const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion( - mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize()); + mmappedBuffer->getReadOnlyByteArrayView().data(), + mmappedBuffer->getReadOnlyByteArrayView().size()); switch (formatVersion) { case FormatUtils::VERSION_2: AKLOGE("Given path is a directory but the format is version 2. path: %s", path); @@ -172,8 +174,8 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str if (!mmappedBuffer) { return nullptr; } - switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), - mmappedBuffer->getBufferSize())) { + switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView().data(), + mmappedBuffer->getReadOnlyByteArrayView().size())) { case FormatUtils::VERSION_2: return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( new PatriciaTriePolicy(std::move(mmappedBuffer))); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp index 08b4e0b5e..f7fd5c071 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.cpp @@ -38,9 +38,14 @@ const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::FLAG_ATTRI const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F; -/* static */ void BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( - const uint8_t *const bigramsBuf, BigramFlags *const outBigramFlags, +/* static */ bool BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition( + const uint8_t *const bigramsBuf, const int bufSize, BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, int *const bigramEntryPos) { + if (bufSize <= *bigramEntryPos) { + AKLOGE("Read invalid pos in getBigramEntryPropertiesAndAdvancePosition(). bufSize: %d, " + "bigramEntryPos: %d.", bufSize, *bigramEntryPos); + return false; + } const BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, bigramEntryPos); if (outBigramFlags) { @@ -51,15 +56,19 @@ const BigramListReadWriteUtils::BigramFlags if (outTargetPtNodePos) { *outTargetPtNodePos = targetPos; } + return true; } -/* static */ void BigramListReadWriteUtils::skipExistingBigrams(const uint8_t *const bigramsBuf, - int *const bigramListPos) { +/* static */ bool BigramListReadWriteUtils::skipExistingBigrams(const uint8_t *const bigramsBuf, + const int bufSize, int *const bigramListPos) { BigramFlags flags; do { - getBigramEntryPropertiesAndAdvancePosition(bigramsBuf, &flags, 0 /* outTargetPtNodePos */, - bigramListPos); + if (!getBigramEntryPropertiesAndAdvancePosition(bigramsBuf, bufSize, &flags, + 0 /* outTargetPtNodePos */, bigramListPos)) { + return false; + } } while(hasNext(flags)); + return true; } /* static */ int BigramListReadWriteUtils::getBigramAddressAndAdvancePosition( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h index 15f924a6a..10f93fb7a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h @@ -30,8 +30,8 @@ class BigramListReadWriteUtils { public: typedef uint8_t BigramFlags; - static void getBigramEntryPropertiesAndAdvancePosition(const uint8_t *const bigramsBuf, - BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, + static bool getBigramEntryPropertiesAndAdvancePosition(const uint8_t *const bigramsBuf, + const int bufSize, BigramFlags *const outBigramFlags, int *const outTargetPtNodePos, int *const bigramEntryPos); static AK_FORCE_INLINE int getProbabilityFromFlags(const BigramFlags flags) { @@ -43,7 +43,8 @@ public: } // Bigrams reading methods - static void skipExistingBigrams(const uint8_t *const bigramsBuf, int *const bigramListPos); + static bool skipExistingBigrams(const uint8_t *const bigramsBuf, const int bufSize, + int *const bigramListPos); private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListReadWriteUtils); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h index 2e05bf397..b7262581a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h @@ -26,7 +26,6 @@ namespace latinime { -class DictionaryBigramsStructurePolicy; class DictionaryShortcutsStructurePolicy; class PtNodeArrayReader; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp index f31c914d2..3c62e2e56 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.cpp @@ -84,23 +84,39 @@ bool DynamicPtUpdatingHelper::addUnigramWord( unigramProperty, &pos); } -bool DynamicPtUpdatingHelper::addBigramWords(const int word0Pos, const int word1Pos, - const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { - const PtNodeParams sourcePtNodeParams( - mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(word0Pos)); - const PtNodeParams targetPtNodeParams( - mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(word1Pos)); - return mPtNodeWriter->addNewBigramEntry(&sourcePtNodeParams, &targetPtNodeParams, - bigramProperty, outAddedNewBigram); +bool DynamicPtUpdatingHelper::addNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, + const int wordPos, const BigramProperty *const bigramProperty, + bool *const outAddedNewEntry) { + if (prevWordsPtNodePos.empty()) { + return false; + } + ASSERT(prevWordsPtNodePos.size() <= MAX_PREV_WORD_COUNT_FOR_N_GRAM); + int prevWordTerminalIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + for (size_t i = 0; i < prevWordsPtNodePos.size(); ++i) { + prevWordTerminalIds[i] = mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos( + prevWordsPtNodePos[i]).getTerminalId(); + } + const WordIdArrayView prevWordIds(prevWordTerminalIds, prevWordsPtNodePos.size()); + const int wordId = + mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(wordPos).getTerminalId(); + return mPtNodeWriter->addNgramEntry(prevWordIds, wordId, bigramProperty, outAddedNewEntry); } -// Remove a bigram relation from word0Pos to word1Pos. -bool DynamicPtUpdatingHelper::removeBigramWords(const int word0Pos, const int word1Pos) { - const PtNodeParams sourcePtNodeParams( - mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(word0Pos)); - const PtNodeParams targetPtNodeParams( - mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(word1Pos)); - return mPtNodeWriter->removeBigramEntry(&sourcePtNodeParams, &targetPtNodeParams); +bool DynamicPtUpdatingHelper::removeNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, + const int wordPos) { + if (prevWordsPtNodePos.empty()) { + return false; + } + ASSERT(prevWordsPtNodePos.size() <= MAX_PREV_WORD_COUNT_FOR_N_GRAM); + int prevWordTerminalIds[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; + for (size_t i = 0; i < prevWordsPtNodePos.size(); ++i) { + prevWordTerminalIds[i] = mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos( + prevWordsPtNodePos[i]).getTerminalId(); + } + const WordIdArrayView prevWordIds(prevWordTerminalIds, prevWordsPtNodePos.size()); + const int wordId = + mPtNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(wordPos).getTerminalId(); + return mPtNodeWriter->removeNgramEntry(prevWordIds, wordId); } bool DynamicPtUpdatingHelper::addShortcutTarget(const int wordPos, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h index f10d15a9b..97c05c1ea 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h @@ -19,6 +19,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "utils/int_array_view.h" namespace latinime { @@ -42,12 +43,12 @@ class DynamicPtUpdatingHelper { const int *const wordCodePoints, const int codePointCount, const UnigramProperty *const unigramProperty, bool *const outAddedNewUnigram); - // Add a bigram relation from word0Pos to word1Pos. - bool addBigramWords(const int word0Pos, const int word1Pos, - const BigramProperty *const bigramProperty, bool *const outAddedNewBigram); + // Add an n-gram entry. + bool addNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, const int wordPos, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); - // Remove a bigram relation from word0Pos to word1Pos. - bool removeBigramWords(const int word0Pos, const int word1Pos); + // Remove an n-gram entry. + bool removeNgramEntry(const PtNodePosArrayView prevWordsPtNodePos, const int wordPos); // Add a shortcut target. bool addShortcutTarget(const int wordPos, const int *const targetCodePoints, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h index a8029f73f..955d779ac 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_writer.h @@ -21,6 +21,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h" +#include "utils/int_array_view.h" namespace latinime { @@ -70,12 +71,10 @@ class PtNodeWriter { virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos) = 0; - virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, - bool *const outAddedNewBigram) = 0; + virtual bool addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) = 0; - virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam) = 0; + virtual bool removeNgramEntry(const WordIdArrayView prevWordIds, const int wordId) = 0; virtual bool updateAllBigramEntriesAndDeleteUselessEntries( const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount) = 0; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h index 00bb502dc..73e291ec2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/bigram/bigram_list_policy.h @@ -27,27 +27,34 @@ namespace latinime { class BigramListPolicy : public DictionaryBigramsStructurePolicy { public: - explicit BigramListPolicy(const uint8_t *const bigramsBuf) : mBigramsBuf(bigramsBuf) {} + BigramListPolicy(const uint8_t *const bigramsBuf, const int bufSize) + : mBigramsBuf(bigramsBuf), mBufSize(bufSize) {} ~BigramListPolicy() {} void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const pos) const { BigramListReadWriteUtils::BigramFlags flags; - BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBigramsBuf, &flags, - outBigramPos, pos); + if (!BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBigramsBuf, + mBufSize, &flags, outBigramPos, pos)) { + AKLOGE("Cannot read bigram entry. mBufSize: %d, pos: %d. ", mBufSize, *pos); + *outProbability = NOT_A_PROBABILITY; + *outHasNext = false; + return; + } *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(flags); *outHasNext = BigramListReadWriteUtils::hasNext(flags); } - void skipAllBigrams(int *const pos) const { - BigramListReadWriteUtils::skipExistingBigrams(mBigramsBuf, pos); + bool skipAllBigrams(int *const pos) const { + return BigramListReadWriteUtils::skipExistingBigrams(mBigramsBuf, mBufSize, pos); } private: DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListPolicy); const uint8_t *const mBigramsBuf; + const int mBufSize; }; } // namespace latinime #endif // LATINIME_BIGRAM_LIST_POLICY_H diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp index 91d76040f..ea32eb2a9 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp @@ -21,6 +21,8 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h" +#include "suggest/core/dictionary/ngram_listener.h" +#include "suggest/core/session/prev_words_info.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/probability_utils.h" @@ -223,7 +225,14 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( mShortcutListPolicy.skipAllShortcuts(&pos); } if (PatriciaTrieReadingUtils::hasBigrams(flags)) { - mBigramListPolicy.skipAllBigrams(&pos); + if (!mBigramListPolicy.skipAllBigrams(&pos)) { + AKLOGE("Cannot skip bigrams. BufSize: %d, pos: %d.", mDictBufferSize, + pos); + mIsCorrupted = true; + ASSERT(false); + *outUnigramProbability = NOT_A_PROBABILITY; + return 0; + } } } } else { @@ -240,7 +249,13 @@ int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount( mShortcutListPolicy.skipAllShortcuts(&pos); } if (PatriciaTrieReadingUtils::hasBigrams(flags)) { - mBigramListPolicy.skipAllBigrams(&pos); + if (!mBigramListPolicy.skipAllBigrams(&pos)) { + AKLOGE("Cannot skip bigrams. BufSize: %d, pos: %d.", mDictBufferSize, pos); + mIsCorrupted = true; + ASSERT(false); + *outUnigramProbability = NOT_A_PROBABILITY; + return 0; + } } } @@ -282,7 +297,8 @@ int PatriciaTriePolicy::getProbability(const int unigramProbability, } } -int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { +int PatriciaTriePolicy::getProbabilityOfPtNode(const int *const prevWordsPtNodePos, + const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_PROBABILITY; } @@ -294,9 +310,34 @@ int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const // for shortcuts). return NOT_A_PROBABILITY; } + if (prevWordsPtNodePos) { + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramListPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + if (bigramsIt.getBigramPos() == ptNodePos + && bigramsIt.getProbability() != NOT_A_PROBABILITY) { + return getProbability(ptNodeParams.getProbability(), bigramsIt.getProbability()); + } + } + return NOT_A_PROBABILITY; + } return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY); } +void PatriciaTriePolicy::iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const { + if (!prevWordsPtNodePos) { + return; + } + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramListPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + listener->onVisitEntry(bigramsIt.getProbability(), bigramsIt.getBigramPos()); + } +} + int PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; @@ -304,12 +345,6 @@ int PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { return mPtNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos).getShortcutPos(); } -BinaryDictionaryBigramsIterator PatriciaTriePolicy::getBigramsIteratorOfPtNode( - const int ptNodePos) const { - const int bigramsPosition = getBigramsPositionOfPtNode(ptNodePos); - return BinaryDictionaryBigramsIterator(&mBigramListPolicy, bigramsPosition); -} - int PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; 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 7c0b9d3c5..70351d147 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 @@ -29,6 +29,7 @@ #include "suggest/policyimpl/dictionary/structure/v2/ver2_pt_node_array_reader.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -39,10 +40,13 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { public: 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), + mHeaderPolicy(mMmappedBuffer->getReadOnlyByteArrayView().data(), + FormatUtils::VERSION_2), + mDictRoot(mMmappedBuffer->getReadOnlyByteArrayView().data() + + mHeaderPolicy.getSize()), + mDictBufferSize(mMmappedBuffer->getReadOnlyByteArrayView().size() + - mHeaderPolicy.getSize()), + mBigramListPolicy(mDictRoot, mDictBufferSize), mShortcutListPolicy(mDictRoot), mPtNodeReader(mDictRoot, mDictBufferSize, &mBigramListPolicy, &mShortcutListPolicy), mPtNodeArrayReader(mDictRoot, mDictBufferSize), mTerminalPtNodePositionsForIteratingWords(), mIsCorrupted(false) {} @@ -63,11 +67,12 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getProbability(const int unigramProbability, const int bigramProbability) const; - int getUnigramProbabilityOfPtNode(const int ptNodePos) const; + int getProbabilityOfPtNode(const int *const prevWordsPtNodePos, const int ptNodePos) const; - int getShortcutPositionOfPtNode(const int ptNodePos) const; + void iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const; - BinaryDictionaryBigramsIterator getBigramsIteratorOfPtNode(const int ptNodePos) const; + int getShortcutPositionOfPtNode(const int ptNodePos) const; const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { return &mHeaderPolicy; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h index 55ba613a5..4b3bb3725 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h @@ -40,8 +40,9 @@ class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy { void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext, int *const bigramEntryPos) const; - void skipAllBigrams(int *const pos) const { + bool skipAllBigrams(int *const pos) const { // Do nothing because we don't need to skip bigram lists in ver4 dictionaries. + return true; } bool addNewEntry(const int terminalId, const int newTargetTerminalId, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp new file mode 100644 index 000000000..5dc91ba10 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp @@ -0,0 +1,95 @@ +/* + * 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/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h" + +namespace latinime { + +bool LanguageModelDictContent::save(FILE *const file) const { + return mTrieMap.save(file); +} + +bool LanguageModelDictContent::runGC( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const LanguageModelDictContent *const originalContent, + int *const outNgramCount) { + return runGCInner(terminalIdMap, originalContent->mTrieMap.getEntriesInRootLevel(), + 0 /* nextLevelBitmapEntryIndex */, outNgramCount); +} + +ProbabilityEntry LanguageModelDictContent::getNgramProbabilityEntry( + const WordIdArrayView prevWordIds, const int wordId) const { + const int bitmapEntryIndex = getBitmapEntryIndex(prevWordIds); + if (bitmapEntryIndex == TrieMap::INVALID_INDEX) { + return ProbabilityEntry(); + } + const TrieMap::Result result = mTrieMap.get(wordId, bitmapEntryIndex); + if (!result.mIsValid) { + // Not found. + return ProbabilityEntry(); + } + return ProbabilityEntry::decode(result.mValue, mHasHistoricalInfo); +} + +bool LanguageModelDictContent::setNgramProbabilityEntry(const WordIdArrayView prevWordIds, + const int terminalId, const ProbabilityEntry *const probabilityEntry) { + const int bitmapEntryIndex = getBitmapEntryIndex(prevWordIds); + if (bitmapEntryIndex == TrieMap::INVALID_INDEX) { + return false; + } + return mTrieMap.put(terminalId, probabilityEntry->encode(mHasHistoricalInfo), bitmapEntryIndex); +} + +bool LanguageModelDictContent::runGCInner( + const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const TrieMap::TrieMapRange trieMapRange, + const int nextLevelBitmapEntryIndex, int *const outNgramCount) { + for (auto &entry : trieMapRange) { + const auto it = terminalIdMap->find(entry.key()); + if (it == terminalIdMap->end() || it->second == Ver4DictConstants::NOT_A_TERMINAL_ID) { + // The word has been removed. + continue; + } + if (!mTrieMap.put(it->second, entry.value(), nextLevelBitmapEntryIndex)) { + return false; + } + if (outNgramCount) { + *outNgramCount += 1; + } + if (entry.hasNextLevelMap()) { + if (!runGCInner(terminalIdMap, entry.getEntriesInNextLevel(), + mTrieMap.getNextLevelBitmapEntryIndex(it->second, nextLevelBitmapEntryIndex), + outNgramCount)) { + return false; + } + } + } + return true; +} + +int LanguageModelDictContent::getBitmapEntryIndex(const WordIdArrayView prevWordIds) const { + int bitmapEntryIndex = mTrieMap.getRootBitmapEntryIndex(); + for (const int wordId : prevWordIds) { + const TrieMap::Result result = mTrieMap.get(wordId, bitmapEntryIndex); + if (!result.mIsValid) { + return TrieMap::INVALID_INDEX; + } + bitmapEntryIndex = result.mNextLevelBitmapEntryIndex; + } + return bitmapEntryIndex; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h new file mode 100644 index 000000000..18f2e0170 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.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_LANGUAGE_MODEL_DICT_CONTENT_H +#define LATINIME_LANGUAGE_MODEL_DICT_CONTENT_H + +#include <cstdio> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/trie_map.h" +#include "utils/byte_array_view.h" +#include "utils/int_array_view.h" + +namespace latinime { + +/** + * Class representing language model. + * + * This class provides methods to get and store unigram/n-gram probability information and flags. + */ +class LanguageModelDictContent { + public: + LanguageModelDictContent(const ReadWriteByteArrayView trieMapBuffer, + const bool hasHistoricalInfo) + : mTrieMap(trieMapBuffer), mHasHistoricalInfo(hasHistoricalInfo) {} + + explicit LanguageModelDictContent(const bool hasHistoricalInfo) + : mTrieMap(), mHasHistoricalInfo(hasHistoricalInfo) {} + + bool isNearSizeLimit() const { + return mTrieMap.isNearSizeLimit(); + } + + bool save(FILE *const file) const; + + bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const LanguageModelDictContent *const originalContent, + int *const outNgramCount); + + ProbabilityEntry getProbabilityEntry(const int wordId) const { + return getNgramProbabilityEntry(WordIdArrayView(), wordId); + } + + bool setProbabilityEntry(const int wordId, const ProbabilityEntry *const probabilityEntry) { + return setNgramProbabilityEntry(WordIdArrayView(), wordId, probabilityEntry); + } + + ProbabilityEntry getNgramProbabilityEntry(const WordIdArrayView prevWordIds, + const int wordId) const; + + bool setNgramProbabilityEntry(const WordIdArrayView prevWordIds, const int wordId, + const ProbabilityEntry *const probabilityEntry); + + private: + DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContent); + + TrieMap mTrieMap; + const bool mHasHistoricalInfo; + + bool runGCInner(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, + const TrieMap::TrieMapRange trieMapRange, const int nextLevelBitmapEntryIndex, + int *const outNgramCount); + + int getBitmapEntryIndex(const WordIdArrayView prevWordIds) const; +}; +} // namespace latinime +#endif /* LATINIME_LANGUAGE_MODEL_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp deleted file mode 100644 index 2425b3b2f..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h" - -#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" -#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" - -namespace latinime { - -const ProbabilityEntry ProbabilityDictContent::getProbabilityEntry(const int terminalId) const { - if (terminalId < 0 || terminalId >= mSize) { - // This method can be called with invalid terminal id during GC. - return ProbabilityEntry(0 /* flags */, NOT_A_PROBABILITY); - } - const BufferWithExtendableBuffer *const buffer = getBuffer(); - int entryPos = getEntryPos(terminalId); - const int flags = buffer->readUintAndAdvancePosition( - Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &entryPos); - const int probability = buffer->readUintAndAdvancePosition( - Ver4DictConstants::PROBABILITY_SIZE, &entryPos); - if (mHasHistoricalInfo) { - const int timestamp = buffer->readUintAndAdvancePosition( - Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &entryPos); - const int level = buffer->readUintAndAdvancePosition( - Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos); - const int count = buffer->readUintAndAdvancePosition( - Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos); - const HistoricalInfo historicalInfo(timestamp, level, count); - return ProbabilityEntry(flags, probability, &historicalInfo); - } else { - return ProbabilityEntry(flags, probability); - } -} - -bool ProbabilityDictContent::setProbabilityEntry(const int terminalId, - const ProbabilityEntry *const probabilityEntry) { - if (terminalId < 0) { - return false; - } - const int entryPos = getEntryPos(terminalId); - if (terminalId >= mSize) { - ProbabilityEntry dummyEntry; - // Write new entry. - int writingPos = getBuffer()->getTailPosition(); - while (writingPos <= entryPos) { - // Fulfilling with dummy entries until writingPos. - if (!writeEntry(&dummyEntry, writingPos)) { - AKLOGE("Cannot write dummy entry. pos: %d, mSize: %d", writingPos, mSize); - return false; - } - writingPos += getEntrySize(); - mSize++; - } - } - return writeEntry(probabilityEntry, entryPos); -} - -bool ProbabilityDictContent::flushToFile(FILE *const file) const { - if (getEntryPos(mSize) < getBuffer()->getTailPosition()) { - ProbabilityDictContent probabilityDictContentToWrite(mHasHistoricalInfo); - for (int i = 0; i < mSize; ++i) { - const ProbabilityEntry probabilityEntry = getProbabilityEntry(i); - if (!probabilityDictContentToWrite.setProbabilityEntry(i, &probabilityEntry)) { - AKLOGE("Cannot set probability entry in flushToFile. terminalId: %d", i); - return false; - } - } - return probabilityDictContentToWrite.flush(file); - } else { - return flush(file); - } -} - -bool ProbabilityDictContent::runGC( - const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const ProbabilityDictContent *const originalProbabilityDictContent) { - mSize = 0; - for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin(); - it != terminalIdMap->end(); ++it) { - const ProbabilityEntry probabilityEntry = - originalProbabilityDictContent->getProbabilityEntry(it->first); - if (!setProbabilityEntry(it->second, &probabilityEntry)) { - AKLOGE("Cannot set probability entry in runGC. terminalId: %d", it->second); - return false; - } - mSize++; - } - return true; -} - -int ProbabilityDictContent::getEntrySize() const { - if (mHasHistoricalInfo) { - return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE - + Ver4DictConstants::PROBABILITY_SIZE - + Ver4DictConstants::TIME_STAMP_FIELD_SIZE - + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE - + Ver4DictConstants::WORD_COUNT_FIELD_SIZE; - } else { - return Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE - + Ver4DictConstants::PROBABILITY_SIZE; - } -} - -int ProbabilityDictContent::getEntryPos(const int terminalId) const { - return terminalId * getEntrySize(); -} - -bool ProbabilityDictContent::writeEntry(const ProbabilityEntry *const probabilityEntry, - const int entryPos) { - BufferWithExtendableBuffer *const bufferToWrite = getWritableBuffer(); - int writingPos = entryPos; - if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getFlags(), - Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, &writingPos)) { - AKLOGE("Cannot write flags in probability dict content. pos: %d", writingPos); - return false; - } - if (!bufferToWrite->writeUintAndAdvancePosition(probabilityEntry->getProbability(), - Ver4DictConstants::PROBABILITY_SIZE, &writingPos)) { - AKLOGE("Cannot write probability in probability dict content. pos: %d", writingPos); - return false; - } - if (mHasHistoricalInfo) { - const HistoricalInfo *const historicalInfo = probabilityEntry->getHistoricalInfo(); - if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(), - Ver4DictConstants::TIME_STAMP_FIELD_SIZE, &writingPos)) { - AKLOGE("Cannot write timestamp in probability dict content. pos: %d", writingPos); - return false; - } - if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getLevel(), - Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &writingPos)) { - AKLOGE("Cannot write level in probability dict content. pos: %d", writingPos); - return false; - } - if (!bufferToWrite->writeUintAndAdvancePosition(historicalInfo->getCount(), - Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &writingPos)) { - AKLOGE("Cannot write count in probability dict content. pos: %d", writingPos); - return false; - } - } - return true; -} - -} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h deleted file mode 100644 index 80e992c1c..000000000 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2013, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LATINIME_PROBABILITY_DICT_CONTENT_H -#define LATINIME_PROBABILITY_DICT_CONTENT_H - -#include <cstdint> -#include <cstdio> - -#include "defines.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" -#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" -#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" - -namespace latinime { - -class ProbabilityEntry; - -class ProbabilityDictContent : public SingleDictContent { - public: - ProbabilityDictContent(uint8_t *const buffer, const int bufferSize, - const bool hasHistoricalInfo) - : SingleDictContent(buffer, bufferSize), - mHasHistoricalInfo(hasHistoricalInfo), - mSize(getBuffer()->getTailPosition() / getEntrySize()) {} - - ProbabilityDictContent(const bool hasHistoricalInfo) - : mHasHistoricalInfo(hasHistoricalInfo), mSize(0) {} - - const ProbabilityEntry getProbabilityEntry(const int terminalId) const; - - bool setProbabilityEntry(const int terminalId, const ProbabilityEntry *const probabilityEntry); - - bool flushToFile(FILE *const file) const; - - bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, - const ProbabilityDictContent *const originalProbabilityDictContent); - - private: - DISALLOW_COPY_AND_ASSIGN(ProbabilityDictContent); - - int getEntrySize() const; - - int getEntryPos(const int terminalId) const; - - bool writeEntry(const ProbabilityEntry *const probabilityEntry, const int entryPos); - - bool mHasHistoricalInfo; - int mSize; -}; -} // namespace latinime -#endif /* LATINIME_PROBABILITY_DICT_CONTENT_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h index 36ba82be1..feff6b57f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -17,6 +17,9 @@ #ifndef LATINIME_PROBABILITY_ENTRY_H #define LATINIME_PROBABILITY_ENTRY_H +#include <climits> +#include <cstdint> + #include "defines.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" #include "suggest/policyimpl/dictionary/utils/historical_info.h" @@ -67,6 +70,50 @@ class ProbabilityEntry { return &mHistoricalInfo; } + uint64_t encode(const bool hasHistoricalInfo) const { + uint64_t encodedEntry = static_cast<uint64_t>(mFlags); + if (hasHistoricalInfo) { + encodedEntry = (encodedEntry << (Ver4DictConstants::TIME_STAMP_FIELD_SIZE * CHAR_BIT)) + ^ static_cast<uint64_t>(mHistoricalInfo.getTimeStamp()); + encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_LEVEL_FIELD_SIZE * CHAR_BIT)) + ^ static_cast<uint64_t>(mHistoricalInfo.getLevel()); + encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT)) + ^ static_cast<uint64_t>(mHistoricalInfo.getCount()); + } else { + encodedEntry = (encodedEntry << (Ver4DictConstants::PROBABILITY_SIZE * CHAR_BIT)) + ^ static_cast<uint64_t>(mProbability); + } + return encodedEntry; + } + + static ProbabilityEntry decode(const uint64_t encodedEntry, const bool hasHistoricalInfo) { + if (hasHistoricalInfo) { + const int flags = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, + Ver4DictConstants::TIME_STAMP_FIELD_SIZE + + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE); + const int timestamp = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::TIME_STAMP_FIELD_SIZE, + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + + Ver4DictConstants::WORD_COUNT_FIELD_SIZE); + const int level = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, + Ver4DictConstants::WORD_COUNT_FIELD_SIZE); + const int count = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::WORD_COUNT_FIELD_SIZE, 0 /* pos */); + const HistoricalInfo historicalInfo(timestamp, level, count); + return ProbabilityEntry(flags, NOT_A_PROBABILITY, &historicalInfo); + } else { + const int flags = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, + Ver4DictConstants::PROBABILITY_SIZE); + const int probability = readFromEncodedEntry(encodedEntry, + Ver4DictConstants::PROBABILITY_SIZE, 0 /* pos */); + return ProbabilityEntry(flags, probability); + } + } + private: // Copy constructor is public to use this class as a type of return value. DISALLOW_ASSIGNMENT_OPERATOR(ProbabilityEntry); @@ -74,6 +121,11 @@ class ProbabilityEntry { const int mFlags; const int mProbability; const HistoricalInfo mHistoricalInfo; + + static int readFromEncodedEntry(const uint64_t encodedEntry, const int size, const int pos) { + return static_cast<int>( + (encodedEntry >> (pos * CHAR_BIT)) & ((1ull << (size * CHAR_BIT)) - 1)); + } }; } // namespace latinime #endif /* LATINIME_PROBABILITY_ENTRY_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/single_dict_content.h index 69a11425f..921774181 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 @@ -21,17 +21,17 @@ #include <cstdio> #include "defines.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" +#include "utils/byte_array_view.h" namespace latinime { -class SingleDictContent : public DictContent { +class SingleDictContent { public: SingleDictContent(uint8_t *const buffer, const int bufferSize) - : mExpandableContentBuffer(buffer, bufferSize, + : mExpandableContentBuffer(ReadWriteByteArrayView(buffer, bufferSize), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE) {} 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 cdf870bd2..c98dd11fd 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 @@ -21,26 +21,29 @@ #include <cstdio> #include "defines.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" #include "suggest/policyimpl/dictionary/utils/sparse_table.h" +#include "utils/byte_array_view.h" namespace latinime { // TODO: Support multiple contents. -class SparseTableDictContent : public DictContent { +class SparseTableDictContent { public: AK_FORCE_INLINE SparseTableDictContent(uint8_t *const *buffers, const int *bufferSizes, const int sparseTableBlockSize, const int sparseTableDataSize) - : mExpandableLookupTableBuffer(buffers[LOOKUP_TABLE_BUFFER_INDEX], - bufferSizes[LOOKUP_TABLE_BUFFER_INDEX], + : mExpandableLookupTableBuffer( + ReadWriteByteArrayView(buffers[LOOKUP_TABLE_BUFFER_INDEX], + bufferSizes[LOOKUP_TABLE_BUFFER_INDEX]), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableAddressTableBuffer(buffers[ADDRESS_TABLE_BUFFER_INDEX], - bufferSizes[ADDRESS_TABLE_BUFFER_INDEX], + mExpandableAddressTableBuffer( + ReadWriteByteArrayView(buffers[ADDRESS_TABLE_BUFFER_INDEX], + bufferSizes[ADDRESS_TABLE_BUFFER_INDEX]), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableContentBuffer(buffers[CONTENT_BUFFER_INDEX], - bufferSizes[CONTENT_BUFFER_INDEX], + mExpandableContentBuffer( + ReadWriteByteArrayView(buffers[CONTENT_BUFFER_INDEX], + bufferSizes[CONTENT_BUFFER_INDEX]), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mAddressLookupTable(&mExpandableLookupTableBuffer, &mExpandableAddressTableBuffer, sparseTableBlockSize, sparseTableDataSize) {} 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 36ab9963a..3c8008dc4 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 @@ -26,6 +26,7 @@ #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" #include "suggest/policyimpl/dictionary/utils/file_utils.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -46,14 +47,16 @@ namespace latinime { } std::vector<uint8_t *> buffers; std::vector<int> bufferSizes; - uint8_t *const buffer = bodyBuffer->getBuffer(); + const ReadWriteByteArrayView buffer = bodyBuffer->getReadWriteByteArrayView(); int position = 0; - while (position < bodyBuffer->getBufferSize()) { - const int bufferSize = ByteArrayUtils::readUint32AndAdvancePosition(buffer, &position); - buffers.push_back(buffer + position); - bufferSizes.push_back(bufferSize); + while (position < static_cast<int>(buffer.size())) { + const int bufferSize = ByteArrayUtils::readUint32AndAdvancePosition( + buffer.data(), &position); + const ReadWriteByteArrayView subBuffer = buffer.subView(position, bufferSize); + buffers.push_back(subBuffer.data()); + bufferSizes.push_back(subBuffer.size()); position += bufferSize; - if (bufferSize < 0 || position < 0 || position > bodyBuffer->getBufferSize()) { + if (bufferSize < 0 || position < 0 || position > static_cast<int>(buffer.size())) { AKLOGE("The dict body file is corrupted."); return Ver4DictBuffersPtr(nullptr); } @@ -118,7 +121,7 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, } FILE *const file = fdopen(fd, "wb"); if (!file) { - AKLOGE("fdopen failed for the file %s. errno: %d", filePath, errno); + AKLOGE("fdopen failed for the file %s. errno: %d", bodyFilePath, errno); ASSERT(false); return false; } @@ -146,27 +149,27 @@ bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, bool Ver4DictBuffers::flushDictBuffers(FILE *const file) const { // Write trie. if (!DictFileWritingUtils::writeBufferToFileTail(file, &mExpandableTrieBuffer)) { - AKLOGE("Trie cannot be written. %s", tmpDirPath); + AKLOGE("Trie cannot be written."); return false; } // Write terminal position lookup table. if (!mTerminalPositionLookupTable.flushToFile(file)) { - AKLOGE("Terminal position lookup table cannot be written. %s", tmpDirPath); + AKLOGE("Terminal position lookup table cannot be written."); return false; } - // Write probability dict content. - if (!mProbabilityDictContent.flushToFile(file)) { - AKLOGE("Probability dict content cannot be written. %s", tmpDirPath); + // Write language model content. + if (!mLanguageModelDictContent.save(file)) { + AKLOGE("Language model dict content cannot be written."); return false; } // Write bigram dict content. if (!mBigramDictContent.flushToFile(file)) { - AKLOGE("Bigram dict content cannot be written. %s", tmpDirPath); + AKLOGE("Bigram dict content cannot be written."); return false; } // Write shortcut dict content. if (!mShortcutDictContent.flushToFile(file)) { - AKLOGE("Shortcut dict content cannot be written. %s", tmpDirPath); + AKLOGE("Shortcut dict content cannot be written."); return false; } return true; @@ -177,20 +180,21 @@ Ver4DictBuffers::Ver4DictBuffers(MmappedBuffer::MmappedBufferPtr &&headerBuffer, const FormatUtils::FORMAT_VERSION formatVersion, const std::vector<uint8_t *> &contentBuffers, const std::vector<int> &contentBufferSizes) : mHeaderBuffer(std::move(headerBuffer)), mDictBuffer(std::move(bodyBuffer)), - mHeaderPolicy(mHeaderBuffer->getBuffer(), formatVersion), - mExpandableHeaderBuffer(mHeaderBuffer ? mHeaderBuffer->getBuffer() : nullptr, - mHeaderPolicy.getSize(), + mHeaderPolicy(mHeaderBuffer->getReadOnlyByteArrayView().data(), formatVersion), + mExpandableHeaderBuffer(mHeaderBuffer->getReadWriteByteArrayView(), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), - mExpandableTrieBuffer(contentBuffers[Ver4DictConstants::TRIE_BUFFER_INDEX], - contentBufferSizes[Ver4DictConstants::TRIE_BUFFER_INDEX], + mExpandableTrieBuffer( + ReadWriteByteArrayView(contentBuffers[Ver4DictConstants::TRIE_BUFFER_INDEX], + contentBufferSizes[Ver4DictConstants::TRIE_BUFFER_INDEX]), BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE), mTerminalPositionLookupTable( contentBuffers[Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX], contentBufferSizes[ Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX]), - mProbabilityDictContent( - contentBuffers[Ver4DictConstants::PROBABILITY_BUFFER_INDEX], - contentBufferSizes[Ver4DictConstants::PROBABILITY_BUFFER_INDEX], + mLanguageModelDictContent( + ReadWriteByteArrayView( + contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX], + contentBufferSizes[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX]), mHeaderPolicy.hasHistoricalInfoOfWords()), mBigramDictContent(&contentBuffers[Ver4DictConstants::BIGRAM_BUFFERS_INDEX], &contentBufferSizes[Ver4DictConstants::BIGRAM_BUFFERS_INDEX], @@ -203,7 +207,7 @@ Ver4DictBuffers::Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const i : mHeaderBuffer(nullptr), mDictBuffer(nullptr), mHeaderPolicy(headerPolicy), mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE), mExpandableTrieBuffer(maxTrieSize), mTerminalPositionLookupTable(), - mProbabilityDictContent(headerPolicy->hasHistoricalInfoOfWords()), + mLanguageModelDictContent(headerPolicy->hasHistoricalInfoOfWords()), mBigramDictContent(headerPolicy->hasHistoricalInfoOfWords()), mShortcutDictContent(), mIsUpdatable(true) {} 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 433411cb8..68027dcb8 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 @@ -23,7 +23,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/header/header_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" @@ -52,7 +52,7 @@ class Ver4DictBuffers { AK_FORCE_INLINE bool isNearSizeLimit() const { return mExpandableTrieBuffer.isNearSizeLimit() || mTerminalPositionLookupTable.isNearSizeLimit() - || mProbabilityDictContent.isNearSizeLimit() + || mLanguageModelDictContent.isNearSizeLimit() || mBigramDictContent.isNearSizeLimit() || mShortcutDictContent.isNearSizeLimit(); } @@ -81,12 +81,12 @@ class Ver4DictBuffers { return &mTerminalPositionLookupTable; } - AK_FORCE_INLINE ProbabilityDictContent *getMutableProbabilityDictContent() { - return &mProbabilityDictContent; + AK_FORCE_INLINE LanguageModelDictContent *getMutableLanguageModelDictContent() { + return &mLanguageModelDictContent; } - AK_FORCE_INLINE const ProbabilityDictContent *getProbabilityDictContent() const { - return &mProbabilityDictContent; + AK_FORCE_INLINE const LanguageModelDictContent *getLanguageModelDictContent() const { + return &mLanguageModelDictContent; } AK_FORCE_INLINE BigramDictContent *getMutableBigramDictContent() { @@ -135,7 +135,7 @@ class Ver4DictBuffers { BufferWithExtendableBuffer mExpandableHeaderBuffer; BufferWithExtendableBuffer mExpandableTrieBuffer; TerminalPositionLookupTable mTerminalPositionLookupTable; - ProbabilityDictContent mProbabilityDictContent; + LanguageModelDictContent mLanguageModelDictContent; BigramDictContent mBigramDictContent; ShortcutDictContent mShortcutDictContent; const int mIsUpdatable; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp index d45dfe377..93d4e562d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp @@ -27,18 +27,20 @@ const int Ver4DictConstants::MAX_DICTIONARY_SIZE = 8 * 1024 * 1024; // limited to 1MB to prevent from inefficient traversing. const int Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE = 1 * 1024 * 1024; -// NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT for Trie, TerminalAddressLookupTable and Probability. +// NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT for Trie and TerminalAddressLookupTable. +// NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT for language model. // NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT for bigram and shortcut. const size_t Ver4DictConstants::NUM_OF_CONTENT_BUFFERS_IN_BODY_FILE = - NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT * 3 + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT * 2 + + NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT + NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT * 2; const int Ver4DictConstants::TRIE_BUFFER_INDEX = 0; const int Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX = TRIE_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT; -const int Ver4DictConstants::PROBABILITY_BUFFER_INDEX = +const int Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX = TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT; const int Ver4DictConstants::BIGRAM_BUFFERS_INDEX = - PROBABILITY_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT; + LANGUAGE_MODEL_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT; const int Ver4DictConstants::SHORTCUT_BUFFERS_INDEX = BIGRAM_BUFFERS_INDEX + NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT; @@ -73,5 +75,6 @@ const int Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK = 0x80; const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT = 1; const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT = 3; +const size_t Ver4DictConstants::NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT = 1; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h index e8f6739ba..6950ca70f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h @@ -35,7 +35,7 @@ class Ver4DictConstants { static const size_t NUM_OF_CONTENT_BUFFERS_IN_BODY_FILE; static const int TRIE_BUFFER_INDEX; static const int TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX; - static const int PROBABILITY_BUFFER_INDEX; + static const int LANGUAGE_MODEL_BUFFER_INDEX; static const int BIGRAM_BUFFERS_INDEX; static const int SHORTCUT_BUFFERS_INDEX; @@ -71,6 +71,7 @@ class Ver4DictConstants { static const size_t NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT; static const size_t NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT; + static const size_t NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT; }; } // namespace latinime #endif /* LATINIME_VER4_DICT_CONSTANTS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp index 0a435e91c..731092efd 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.cpp @@ -18,7 +18,7 @@ #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h" #include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h" -#include "suggest/policyimpl/dictionary/structure/v4/content/probability_dict_content.h" +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h" #include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_reading_utils.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" @@ -61,8 +61,9 @@ const PtNodeParams Ver4PatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProce terminalIdFieldPos += mBuffer->getOriginalBufferSize(); } terminalId = Ver4PatriciaTrieReadingUtils::getTerminalIdAndAdvancePosition(dictBuf, &pos); + // TODO: Quit reading probability here. const ProbabilityEntry probabilityEntry = - mProbabilityDictContent->getProbabilityEntry(terminalId); + mLanguageModelDictContent->getProbabilityEntry(terminalId); if (probabilityEntry.hasHistoricalInfo()) { probability = ForgettingCurveUtils::decodeProbability( probabilityEntry.getHistoricalInfo(), mHeaderPolicy); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h index 22ed4a6c0..a91ad5728 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h @@ -25,18 +25,18 @@ namespace latinime { class BufferWithExtendableBuffer; class HeaderPolicy; -class ProbabilityDictContent; +class LanguageModelDictContent; /* * This class is used for helping to read nodes of ver4 patricia trie. This class handles moved - * node and reads node attributes including probability form probabilityBuffer. + * node and reads node attributes including probability form language model. */ class Ver4PatriciaTrieNodeReader : public PtNodeReader { public: Ver4PatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer, - const ProbabilityDictContent *const probabilityDictContent, + const LanguageModelDictContent *const languageModelDictContent, const HeaderPolicy *const headerPolicy) - : mBuffer(buffer), mProbabilityDictContent(probabilityDictContent), + : mBuffer(buffer), mLanguageModelDictContent(languageModelDictContent), mHeaderPolicy(headerPolicy) {} ~Ver4PatriciaTrieNodeReader() {} @@ -50,7 +50,7 @@ class Ver4PatriciaTrieNodeReader : public PtNodeReader { DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeReader); const BufferWithExtendableBuffer *const mBuffer; - const ProbabilityDictContent *const mProbabilityDictContent; + const LanguageModelDictContent *const mLanguageModelDictContent; const HeaderPolicy *const mHeaderPolicy; const PtNodeParams fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos, diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index 3d8da9173..857222f5d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -143,11 +143,11 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeUnigramProperty( return false; } const ProbabilityEntry originalProbabilityEntry = - mBuffers->getProbabilityDictContent()->getProbabilityEntry( + mBuffers->getLanguageModelDictContent()->getProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId()); const ProbabilityEntry probabilityEntry = createUpdatedEntryFrom(&originalProbabilityEntry, unigramProperty); - return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry); } @@ -158,14 +158,14 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbabilityAndGetNeedsToKeepPtNodeA return false; } const ProbabilityEntry originalProbabilityEntry = - mBuffers->getProbabilityDictContent()->getProbabilityEntry( + mBuffers->getLanguageModelDictContent()->getProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId()); if (originalProbabilityEntry.hasHistoricalInfo()) { const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave( originalProbabilityEntry.getHistoricalInfo(), mHeaderPolicy); const ProbabilityEntry probabilityEntry = originalProbabilityEntry.createEntryWithUpdatedHistoricalInfo(&historicalInfo); - if (!mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + if (!mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry)) { AKLOGE("Cannot write updated probability entry. terminalId: %d", toBeUpdatedPtNodeParams->getTerminalId()); @@ -218,26 +218,23 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( ProbabilityEntry newProbabilityEntry; const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( &newProbabilityEntry, unigramProperty); - return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry(terminalId, - &probabilityEntryToWrite); + return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( + terminalId, &probabilityEntryToWrite); } -bool Ver4PatriciaTrieNodeWriter::addNewBigramEntry( - const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam, +bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { - if (!mBigramPolicy->addNewEntry(sourcePtNodeParams->getTerminalId(), - targetPtNodeParam->getTerminalId(), bigramProperty, outAddedNewBigram)) { + if (!mBigramPolicy->addNewEntry(prevWordIds[0], wordId, bigramProperty, outAddedNewBigram)) { AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", - sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); + prevWordIds[0], wordId); return false; } return true; } -bool Ver4PatriciaTrieNodeWriter::removeBigramEntry( - const PtNodeParams *const sourcePtNodeParams, const PtNodeParams *const targetPtNodeParam) { - return mBigramPolicy->removeEntry(sourcePtNodeParams->getTerminalId(), - targetPtNodeParam->getTerminalId()); +bool Ver4PatriciaTrieNodeWriter::removeNgramEntry(const WordIdArrayView prevWordIds, + const int wordId) { + return mBigramPolicy->removeEntry(prevWordIds[0], wordId); } bool Ver4PatriciaTrieNodeWriter::updateAllBigramEntriesAndDeleteUselessEntries( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h index 162dc9b1d..6703dba04 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h @@ -75,12 +75,10 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { virtual bool writeNewTerminalPtNodeAndAdvancePosition(const PtNodeParams *const ptNodeParams, const UnigramProperty *const unigramProperty, int *const ptNodeWritingPos); - virtual bool addNewBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam, const BigramProperty *const bigramProperty, - bool *const outAddedNewBigram); + virtual bool addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, + const BigramProperty *const bigramProperty, bool *const outAddedNewEntry); - virtual bool removeBigramEntry(const PtNodeParams *const sourcePtNodeParams, - const PtNodeParams *const targetPtNodeParam); + virtual bool removeNgramEntry(const WordIdArrayView prevWordIds, const int wordId); virtual bool updateAllBigramEntriesAndDeleteUselessEntries( const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount); 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 0b5764aba..723808399 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 @@ -20,6 +20,7 @@ #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/ngram_listener.h" #include "suggest/core/dictionary/property/bigram_property.h" #include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/core/dictionary/property/word_property.h" @@ -121,7 +122,8 @@ int Ver4PatriciaTriePolicy::getProbability(const int unigramProbability, } } -int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const { +int Ver4PatriciaTriePolicy::getProbabilityOfPtNode(const int *const prevWordsPtNodePos, + const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_PROBABILITY; } @@ -129,9 +131,34 @@ int Ver4PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) c if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) { return NOT_A_PROBABILITY; } + if (prevWordsPtNodePos) { + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + if (bigramsIt.getBigramPos() == ptNodePos + && bigramsIt.getProbability() != NOT_A_PROBABILITY) { + return getProbability(ptNodeParams.getProbability(), bigramsIt.getProbability()); + } + } + return NOT_A_PROBABILITY; + } return getProbability(ptNodeParams.getProbability(), NOT_A_PROBABILITY); } +void Ver4PatriciaTriePolicy::iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const { + if (!prevWordsPtNodePos) { + return; + } + const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]); + BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition); + while (bigramsIt.hasNext()) { + bigramsIt.next(); + listener->onVisitEntry(bigramsIt.getProbability(), bigramsIt.getBigramPos()); + } +} + int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; @@ -144,12 +171,6 @@ int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) con ptNodeParams.getTerminalId()); } -BinaryDictionaryBigramsIterator Ver4PatriciaTriePolicy::getBigramsIteratorOfPtNode( - const int ptNodePos) const { - const int bigramsPosition = getBigramsPositionOfPtNode(ptNodePos); - return BinaryDictionaryBigramsIterator(&mBigramPolicy, bigramsPosition); -} - int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const { if (ptNodePos == NOT_A_DICT_POS) { return NOT_A_DICT_POS; @@ -271,6 +292,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, false /* tryLowerCaseSearch */); + const auto prevWordsPtNodePosView = PtNodePosArrayView::fromFixedSizeArray(prevWordsPtNodePos); // TODO: Support N-gram. if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { if (prevWordsInfo->isNthPrevWordBeginningOfSentence(1 /* n */)) { @@ -298,10 +320,10 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI if (word1Pos == NOT_A_DICT_POS) { return false; } - bool addedNewBigram = false; - if (mUpdatingHelper.addBigramWords(prevWordsPtNodePos[0], word1Pos, bigramProperty, - &addedNewBigram)) { - if (addedNewBigram) { + bool addedNewEntry = false; + if (mUpdatingHelper.addNgramEntry(prevWordsPtNodePosView, word1Pos, bigramProperty, + &addedNewEntry)) { + if (addedNewEntry) { mBigramCount++; } return true; @@ -331,6 +353,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; prevWordsInfo->getPrevWordsTerminalPtNodePos(this, prevWordsPtNodePos, false /* tryLowerCaseSerch */); + const auto prevWordsPtNodePosView = PtNodePosArrayView::fromFixedSizeArray(prevWordsPtNodePos); // TODO: Support N-gram. if (prevWordsPtNodePos[0] == NOT_A_DICT_POS) { return false; @@ -340,7 +363,7 @@ bool Ver4PatriciaTriePolicy::removeNgramEntry(const PrevWordsInfo *const prevWor if (wordPos == NOT_A_DICT_POS) { return false; } - if (mUpdatingHelper.removeBigramWords(prevWordsPtNodePos[0], wordPos)) { + if (mUpdatingHelper.removeNgramEntry(prevWordsPtNodePosView, wordPos)) { mBigramCount--; return true; } else { @@ -431,7 +454,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code std::vector<int> codePointVector(ptNodeParams.getCodePoints(), ptNodeParams.getCodePoints() + ptNodeParams.getCodePointCount()); const ProbabilityEntry probabilityEntry = - mBuffers->getProbabilityDictContent()->getProbabilityEntry( + mBuffers->getLanguageModelDictContent()->getProbabilityEntry( ptNodeParams.getTerminalId()); const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo(); // Fetch bigram information. 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 85929b785..faad4290d 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 @@ -46,7 +46,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { mBuffers->getTerminalPositionLookupTable(), mHeaderPolicy), mShortcutPolicy(mBuffers->getMutableShortcutDictContent(), mBuffers->getTerminalPositionLookupTable()), - mNodeReader(mDictBuffer, mBuffers->getProbabilityDictContent(), mHeaderPolicy), + mNodeReader(mDictBuffer, mBuffers->getLanguageModelDictContent(), mHeaderPolicy), mPtNodeArrayReader(mDictBuffer), mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader, &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy), @@ -72,11 +72,12 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { int getProbability(const int unigramProbability, const int bigramProbability) const; - int getUnigramProbabilityOfPtNode(const int ptNodePos) const; + int getProbabilityOfPtNode(const int *const prevWordsPtNodePos, const int ptNodePos) const; - int getShortcutPositionOfPtNode(const int ptNodePos) const; + void iterateNgramEntries(const int *const prevWordsPtNodePos, + NgramListener *const listener) const; - BinaryDictionaryBigramsIterator getBigramsIteratorOfPtNode(const int ptNodePos) const; + int getShortcutPositionOfPtNode(const int ptNodePos) const; const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const { return mHeaderPolicy; 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 0e658f8e3..4220312e0 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 @@ -75,7 +75,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy, Ver4DictBuffers *const buffersToWrite, int *const outUnigramCount, int *const outBigramCount) { Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(), - mBuffers->getProbabilityDictContent(), headerPolicy); + mBuffers->getLanguageModelDictContent(), headerPolicy); Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer()); Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(), mBuffers->getTerminalPositionLookupTable(), headerPolicy); @@ -138,7 +138,7 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, // Create policy instances for the GCed dictionary. Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(), - buffersToWrite->getProbabilityDictContent(), headerPolicy); + buffersToWrite->getLanguageModelDictContent(), headerPolicy); Ver4PtNodeArrayReader newPtNodeArrayreader(buffersToWrite->getTrieBuffer()); Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(), buffersToWrite->getTerminalPositionLookupTable(), headerPolicy); @@ -154,8 +154,8 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos, return false; } // Run GC for probability dict content. - if (!buffersToWrite->getMutableProbabilityDictContent()->runGC(&terminalIdMap, - mBuffers->getProbabilityDictContent())) { + if (!buffersToWrite->getMutableLanguageModelDictContent()->runGC(&terminalIdMap, + mBuffers->getLanguageModelDictContent(), nullptr /* outNgramCount */)) { return false; } // Run GC for bigram dict content. @@ -201,7 +201,7 @@ bool Ver4PatriciaTrieWritingHelper::truncateUnigrams( continue; } const ProbabilityEntry probabilityEntry = - mBuffers->getProbabilityDictContent()->getProbabilityEntry(i); + mBuffers->getLanguageModelDictContent()->getProbabilityEntry(i); const int probability = probabilityEntry.hasHistoricalInfo() ? ForgettingCurveUtils::decodeProbability( probabilityEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) : diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp index 825b72c6a..833063c17 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp @@ -25,7 +25,7 @@ const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 12 uint32_t BufferWithExtendableBuffer::readUint(const int size, const int pos) const { const bool readingPosIsInAdditionalBuffer = isInAdditionalBuffer(pos); - const int posInBuffer = readingPosIsInAdditionalBuffer ? pos - mOriginalBufferSize : pos; + const int posInBuffer = readingPosIsInAdditionalBuffer ? pos - mOriginalBuffer.size() : pos; return ByteArrayUtils::readUint(getBuffer(readingPosIsInAdditionalBuffer), size, posInBuffer); } @@ -40,12 +40,12 @@ void BufferWithExtendableBuffer::readCodePointsAndAdvancePosition(const int maxC int *const outCodePoints, int *outCodePointCount, int *const pos) const { const bool readingPosIsInAdditionalBuffer = isInAdditionalBuffer(*pos); if (readingPosIsInAdditionalBuffer) { - *pos -= mOriginalBufferSize; + *pos -= mOriginalBuffer.size(); } *outCodePointCount = ByteArrayUtils::readStringAndAdvancePosition( getBuffer(readingPosIsInAdditionalBuffer), maxCodePointCount, outCodePoints, pos); if (readingPosIsInAdditionalBuffer) { - *pos += mOriginalBufferSize; + *pos += mOriginalBuffer.size(); } } @@ -69,13 +69,14 @@ bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data return false; } const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos); - uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer; + uint8_t *const buffer = + usesAdditionalBuffer ? mAdditionalBuffer.data() : mOriginalBuffer.data(); if (usesAdditionalBuffer) { - *pos -= mOriginalBufferSize; + *pos -= mOriginalBuffer.size(); } ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos); if (usesAdditionalBuffer) { - *pos += mOriginalBufferSize; + *pos += mOriginalBuffer.size(); } return true; } @@ -88,14 +89,15 @@ bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *co return false; } const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos); - uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer; + uint8_t *const buffer = + usesAdditionalBuffer ? mAdditionalBuffer.data() : mOriginalBuffer.data(); if (usesAdditionalBuffer) { - *pos -= mOriginalBufferSize; + *pos -= mOriginalBuffer.size(); } ByteArrayUtils::writeCodePointsAndAdvancePosition(buffer, codePoints, codePointCount, writesTerminator, pos); if (usesAdditionalBuffer) { - *pos += mOriginalBufferSize; + *pos += mOriginalBuffer.size(); } return true; } @@ -119,7 +121,7 @@ bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int const size_t totalRequiredSize = static_cast<size_t>(pos + size); if (!isInAdditionalBuffer(pos)) { // Here don't need to care about the additional buffer. - if (static_cast<size_t>(mOriginalBufferSize) < totalRequiredSize) { + if (mOriginalBuffer.size() < totalRequiredSize) { // Violate the boundary. return false; } @@ -137,7 +139,7 @@ bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int return false; } const size_t extendSize = totalRequiredSize - - std::min(mAdditionalBuffer.size() + mOriginalBufferSize, totalRequiredSize); + std::min(mAdditionalBuffer.size() + mOriginalBuffer.size(), totalRequiredSize); if (extendSize > 0 && !extendBuffer(extendSize)) { // Failed to extend the buffer. return false; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h index 5e1362eee..fad83aa25 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h @@ -23,6 +23,7 @@ #include "defines.h" #include "suggest/policyimpl/dictionary/utils/byte_array_utils.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -34,20 +35,18 @@ class BufferWithExtendableBuffer { public: static const size_t DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE; - BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize, + BufferWithExtendableBuffer(const ReadWriteByteArrayView originalBuffer, const int maxAdditionalBufferSize) - : mOriginalBuffer(originalBuffer), mOriginalBufferSize(originalBufferSize), - mAdditionalBuffer(0), mUsedAdditionalBufferSize(0), + : mOriginalBuffer(originalBuffer), mAdditionalBuffer(), mUsedAdditionalBufferSize(0), mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} // Without original buffer. BufferWithExtendableBuffer(const int maxAdditionalBufferSize) - : mOriginalBuffer(0), mOriginalBufferSize(0), - mAdditionalBuffer(0), mUsedAdditionalBufferSize(0), + : mOriginalBuffer(), mAdditionalBuffer(), mUsedAdditionalBufferSize(0), mMaxAdditionalBufferSize(maxAdditionalBufferSize) {} AK_FORCE_INLINE int getTailPosition() const { - return mOriginalBufferSize + mUsedAdditionalBufferSize; + return mOriginalBuffer.size() + mUsedAdditionalBufferSize; } AK_FORCE_INLINE int getUsedAdditionalBufferSize() const { @@ -58,16 +57,16 @@ class BufferWithExtendableBuffer { * For reading. */ AK_FORCE_INLINE bool isInAdditionalBuffer(const int position) const { - return position >= mOriginalBufferSize; + return position >= static_cast<int>(mOriginalBuffer.size()); } // TODO: Resolve the issue that the address can be changed when the vector is resized. // CAVEAT!: Be careful about array out of bound access with buffers AK_FORCE_INLINE const uint8_t *getBuffer(const bool usesAdditionalBuffer) const { if (usesAdditionalBuffer) { - return &mAdditionalBuffer[0]; + return mAdditionalBuffer.data(); } else { - return mOriginalBuffer; + return mOriginalBuffer.data(); } } @@ -79,7 +78,7 @@ class BufferWithExtendableBuffer { int *const outCodePoints, int *outCodePointCount, int *const pos) const; AK_FORCE_INLINE int getOriginalBufferSize() const { - return mOriginalBufferSize; + return mOriginalBuffer.size(); } AK_FORCE_INLINE bool isNearSizeLimit() const { @@ -110,8 +109,7 @@ class BufferWithExtendableBuffer { static const int NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE; static const size_t EXTEND_ADDITIONAL_BUFFER_SIZE_STEP; - uint8_t *const mOriginalBuffer; - const int mOriginalBufferSize; + const ReadWriteByteArrayView mOriginalBuffer; std::vector<uint8_t> mAdditionalBuffer; int mUsedAdditionalBufferSize; const size_t mMaxAdditionalBufferSize; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h index 3ff80aeec..9910777b8 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h @@ -84,7 +84,7 @@ class ForgettingCurveUtils { static const int STRONG_BASE_PROBABILITY; static const int AGGRESSIVE_BASE_PROBABILITY; - std::vector<std::vector<std::vector<int> > > mTables; + std::vector<std::vector<std::vector<int>>> mTables; static int getBaseProbabilityForLevel(const int tableId, const int level); }; 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 8460087ab..e25310373 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h @@ -21,6 +21,7 @@ #include <memory> #include "defines.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -39,12 +40,12 @@ class MmappedBuffer { ~MmappedBuffer(); - AK_FORCE_INLINE uint8_t *getBuffer() const { - return mBuffer; + ReadWriteByteArrayView getReadWriteByteArrayView() const { + return mByteArrayView; } - AK_FORCE_INLINE int getBufferSize() const { - return mBufferSize; + ReadOnlyByteArrayView getReadOnlyByteArrayView() const { + return mByteArrayView.getReadOnlyView(); } AK_FORCE_INLINE bool isUpdatable() const { @@ -55,18 +56,17 @@ class MmappedBuffer { AK_FORCE_INLINE MmappedBuffer(uint8_t *const buffer, const int bufferSize, void *const mmappedBuffer, const int alignedSize, const int mmapFd, const bool isUpdatable) - : mBuffer(buffer), mBufferSize(bufferSize), mMmappedBuffer(mmappedBuffer), + : mByteArrayView(buffer, bufferSize), mMmappedBuffer(mmappedBuffer), mAlignedSize(alignedSize), mMmapFd(mmapFd), mIsUpdatable(isUpdatable) {} // Empty file. We have to handle an empty file as a valid part of a dictionary. AK_FORCE_INLINE MmappedBuffer(const bool isUpdatable) - : mBuffer(nullptr), mBufferSize(0), mMmappedBuffer(nullptr), mAlignedSize(0), + : mByteArrayView(), mMmappedBuffer(nullptr), mAlignedSize(0), mMmapFd(0), mIsUpdatable(isUpdatable) {} DISALLOW_IMPLICIT_CONSTRUCTORS(MmappedBuffer); - uint8_t *const mBuffer; - const int mBufferSize; + const ReadWriteByteArrayView mByteArrayView; void *const mMmappedBuffer; const int mAlignedSize; const int mMmapFd; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp new file mode 100644 index 000000000..407b8efd0 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp @@ -0,0 +1,387 @@ +/* + * 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/policyimpl/dictionary/utils/trie_map.h" + +#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" + +namespace latinime { + +const int TrieMap::INVALID_INDEX = -1; +const int TrieMap::FIELD0_SIZE = 4; +const int TrieMap::FIELD1_SIZE = 3; +const int TrieMap::ENTRY_SIZE = FIELD0_SIZE + FIELD1_SIZE; +const uint32_t TrieMap::VALUE_FLAG = 0x400000; +const uint32_t TrieMap::VALUE_MASK = 0x3FFFFF; +const uint32_t TrieMap::TERMINAL_LINK_FLAG = 0x800000; +const uint32_t TrieMap::TERMINAL_LINK_MASK = 0x7FFFFF; +const int TrieMap::NUM_OF_BITS_USED_FOR_ONE_LEVEL = 5; +const uint32_t TrieMap::LABEL_MASK = 0x1F; +const int TrieMap::MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL = 1 << NUM_OF_BITS_USED_FOR_ONE_LEVEL; +const int TrieMap::ROOT_BITMAP_ENTRY_INDEX = 0; +const int TrieMap::ROOT_BITMAP_ENTRY_POS = MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL * FIELD0_SIZE; +const TrieMap::Entry TrieMap::EMPTY_BITMAP_ENTRY = TrieMap::Entry(0, 0); +const uint64_t TrieMap::MAX_VALUE = + (static_cast<uint64_t>(1) << ((FIELD0_SIZE + FIELD1_SIZE) * CHAR_BIT)) - 1; +const int TrieMap::MAX_BUFFER_SIZE = TERMINAL_LINK_MASK * ENTRY_SIZE; + +TrieMap::TrieMap() : mBuffer(MAX_BUFFER_SIZE) { + mBuffer.extend(ROOT_BITMAP_ENTRY_POS); + writeEntry(EMPTY_BITMAP_ENTRY, ROOT_BITMAP_ENTRY_INDEX); +} + +TrieMap::TrieMap(const ReadWriteByteArrayView buffer) + : mBuffer(buffer, BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE) {} + +void TrieMap::dump(const int from, const int to) const { + AKLOGI("BufSize: %d", mBuffer.getTailPosition()); + for (int i = from; i < to; ++i) { + AKLOGI("Entry[%d]: %x, %x", i, readField0(i), readField1(i)); + } + int unusedRegionSize = 0; + for (int i = 1; i <= MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL; ++i) { + int index = readEmptyTableLink(i); + while (index != ROOT_BITMAP_ENTRY_INDEX) { + index = readField0(index); + unusedRegionSize += i; + } + } + AKLOGI("Unused Size: %d", unusedRegionSize); +} + +int TrieMap::getNextLevelBitmapEntryIndex(const int key, const int bitmapEntryIndex) { + const Entry bitmapEntry = readEntry(bitmapEntryIndex); + const uint32_t unsignedKey = static_cast<uint32_t>(key); + const int terminalEntryIndex = getTerminalEntryIndex( + unsignedKey, getBitShuffledKey(unsignedKey), bitmapEntry, 0 /* level */); + if (terminalEntryIndex == INVALID_INDEX) { + // Not found. + return INVALID_INDEX; + } + const Entry terminalEntry = readEntry(terminalEntryIndex); + if (terminalEntry.hasTerminalLink()) { + return terminalEntry.getValueEntryIndex() + 1; + } + // Create a value entry and a bitmap entry. + const int valueEntryIndex = allocateTable(2 /* entryCount */); + if (!writeEntry(Entry(0, terminalEntry.getValue()), valueEntryIndex)) { + return INVALID_INDEX; + } + if (!writeEntry(EMPTY_BITMAP_ENTRY, valueEntryIndex + 1)) { + return INVALID_INDEX; + } + if (!writeField1(valueEntryIndex | TERMINAL_LINK_FLAG, valueEntryIndex)) { + return INVALID_INDEX; + } + return valueEntryIndex + 1; +} + +const TrieMap::Result TrieMap::get(const int key, const int bitmapEntryIndex) const { + const uint32_t unsignedKey = static_cast<uint32_t>(key); + return getInternal(unsignedKey, getBitShuffledKey(unsignedKey), bitmapEntryIndex, + 0 /* level */); +} + +bool TrieMap::put(const int key, const uint64_t value, const int bitmapEntryIndex) { + if (value > MAX_VALUE) { + return false; + } + const uint32_t unsignedKey = static_cast<uint32_t>(key); + return putInternal(unsignedKey, value, getBitShuffledKey(unsignedKey), bitmapEntryIndex, + readEntry(bitmapEntryIndex), 0 /* level */); +} + +bool TrieMap::save(FILE *const file) const { + return DictFileWritingUtils::writeBufferToFileTail(file, &mBuffer); +} + +/** + * Iterate next entry in a certain level. + * + * @param iterationState the iteration state that will be read and updated in this method. + * @param outKey the output key + * @return Result instance. mIsValid is false when all entries are iterated. + */ +const TrieMap::Result TrieMap::iterateNext(std::vector<TableIterationState> *const iterationState, + int *const outKey) const { + while (!iterationState->empty()) { + TableIterationState &state = iterationState->back(); + if (state.mTableSize <= state.mCurrentIndex) { + // Move to parent. + iterationState->pop_back(); + } else { + const int entryIndex = state.mTableIndex + state.mCurrentIndex; + state.mCurrentIndex += 1; + const Entry entry = readEntry(entryIndex); + if (entry.isBitmapEntry()) { + // Move to child. + iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex()); + } else { + if (outKey) { + *outKey = entry.getKey(); + } + if (!entry.hasTerminalLink()) { + return Result(entry.getValue(), true, INVALID_INDEX); + } + const int valueEntryIndex = entry.getValueEntryIndex(); + const Entry valueEntry = readEntry(valueEntryIndex); + return Result(valueEntry.getValueOfValueEntry(), true, valueEntryIndex + 1); + } + } + } + // Visited all entries. + return Result(0, false, INVALID_INDEX); +} + +/** + * Shuffle bits of the key in the fixed order. + * + * This method is used as a hash function. This returns different values for different inputs. + */ +uint32_t TrieMap::getBitShuffledKey(const uint32_t key) const { + uint32_t shuffledKey = 0; + for (int i = 0; i < 4; ++i) { + const uint32_t keyPiece = (key >> (i * 8)) & 0xFF; + shuffledKey ^= ((keyPiece ^ (keyPiece << 7) ^ (keyPiece << 14) ^ (keyPiece << 21)) + & 0x11111111) << i; + } + return shuffledKey; +} + +bool TrieMap::writeValue(const uint64_t value, const int terminalEntryIndex) { + if (value <= VALUE_MASK) { + // Write value into the terminal entry. + return writeField1(value | VALUE_FLAG, terminalEntryIndex); + } + // Create value entry and write value. + const int valueEntryIndex = allocateTable(2 /* entryCount */); + if (!writeEntry(Entry(value >> (FIELD1_SIZE * CHAR_BIT), value), valueEntryIndex)) { + return false; + } + if (!writeEntry(EMPTY_BITMAP_ENTRY, valueEntryIndex + 1)) { + return false; + } + return writeField1(valueEntryIndex | TERMINAL_LINK_FLAG, terminalEntryIndex); +} + +bool TrieMap::updateValue(const Entry &terminalEntry, const uint64_t value, + const int terminalEntryIndex) { + if (!terminalEntry.hasTerminalLink()) { + return writeValue(value, terminalEntryIndex); + } + const int valueEntryIndex = terminalEntry.getValueEntryIndex(); + return writeEntry(Entry(value >> (FIELD1_SIZE * CHAR_BIT), value), valueEntryIndex); +} + +bool TrieMap::freeTable(const int tableIndex, const int entryCount) { + if (!writeField0(readEmptyTableLink(entryCount), tableIndex)) { + return false; + } + return writeEmptyTableLink(tableIndex, entryCount); +} + +/** + * Allocate table with entryCount-entries. Reuse freed table if possible. + */ +int TrieMap::allocateTable(const int entryCount) { + if (entryCount > 0 && entryCount <= MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL) { + const int tableIndex = readEmptyTableLink(entryCount); + if (tableIndex > 0) { + if (!writeEmptyTableLink(readField0(tableIndex), entryCount)) { + return INVALID_INDEX; + } + // Reuse the table. + return tableIndex; + } + } + // Allocate memory space at tail position of the buffer. + const int mapIndex = getTailEntryIndex(); + if (!mBuffer.extend(entryCount * ENTRY_SIZE)) { + return INVALID_INDEX; + } + return mapIndex; +} + +int TrieMap::getTerminalEntryIndex(const uint32_t key, const uint32_t hashedKey, + const Entry &bitmapEntry, const int level) const { + const int label = getLabel(hashedKey, level); + if (!exists(bitmapEntry.getBitmap(), label)) { + return INVALID_INDEX; + } + const int entryIndex = bitmapEntry.getTableIndex() + popCount(bitmapEntry.getBitmap(), label); + const Entry entry = readEntry(entryIndex); + if (entry.isBitmapEntry()) { + // Move to the next level. + return getTerminalEntryIndex(key, hashedKey, entry, level + 1); + } + if (entry.getKey() == key) { + // Terminal entry is found. + return entryIndex; + } + return INVALID_INDEX; +} + +/** + * Get Result corresponding to the key. + * + * @param key the key. + * @param hashedKey the hashed key. + * @param bitmapEntryIndex the index of bitmap entry + * @param level current level + * @return Result instance corresponding to the key. mIsValid indicates whether the key is in the + * map. + */ +const TrieMap::Result TrieMap::getInternal(const uint32_t key, const uint32_t hashedKey, + const int bitmapEntryIndex, const int level) const { + const int terminalEntryIndex = getTerminalEntryIndex(key, hashedKey, + readEntry(bitmapEntryIndex), level); + if (terminalEntryIndex == INVALID_INDEX) { + // Not found. + return Result(0, false, INVALID_INDEX); + } + const Entry terminalEntry = readEntry(terminalEntryIndex); + if (!terminalEntry.hasTerminalLink()) { + return Result(terminalEntry.getValue(), true, INVALID_INDEX); + } + const int valueEntryIndex = terminalEntry.getValueEntryIndex(); + const Entry valueEntry = readEntry(valueEntryIndex); + return Result(valueEntry.getValueOfValueEntry(), true, valueEntryIndex + 1); +} + +/** + * Put key to value mapping to the map. + * + * @param key the key. + * @param value the value + * @param hashedKey the hashed key. + * @param bitmapEntryIndex the index of bitmap entry + * @param bitmapEntry the bitmap entry + * @param level current level + * @return whether the key-value has been correctly inserted to the map or not. + */ +bool TrieMap::putInternal(const uint32_t key, const uint64_t value, const uint32_t hashedKey, + const int bitmapEntryIndex, const Entry &bitmapEntry, const int level) { + const int label = getLabel(hashedKey, level); + const uint32_t bitmap = bitmapEntry.getBitmap(); + const int mapIndex = bitmapEntry.getTableIndex(); + if (!exists(bitmap, label)) { + // Current map doesn't contain the label. + return addNewEntryByExpandingTable(key, value, mapIndex, bitmap, bitmapEntryIndex, label); + } + const int entryIndex = mapIndex + popCount(bitmap, label); + const Entry entry = readEntry(entryIndex); + if (entry.isBitmapEntry()) { + // Bitmap entry is found. Go to the next level. + return putInternal(key, value, hashedKey, entryIndex, entry, level + 1); + } + if (entry.getKey() == key) { + // Terminal entry for the key is found. Update the value. + return updateValue(entry, value, entryIndex); + } + // Conflict with the existing key. + return addNewEntryByResolvingConflict(key, value, hashedKey, entry, entryIndex, level); +} + +/** + * Resolve a conflict in the current level and add new entry. + * + * @param key the key + * @param value the value + * @param hashedKey the hashed key + * @param conflictedEntry the existing conflicted entry + * @param conflictedEntryIndex the index of existing conflicted entry + * @param level current level + * @return whether the key-value has been correctly inserted to the map or not. + */ +bool TrieMap::addNewEntryByResolvingConflict(const uint32_t key, const uint64_t value, + const uint32_t hashedKey, const Entry &conflictedEntry, const int conflictedEntryIndex, + const int level) { + const int conflictedKeyNextLabel = + getLabel(getBitShuffledKey(conflictedEntry.getKey()), level + 1); + const int nextLabel = getLabel(hashedKey, level + 1); + if (conflictedKeyNextLabel == nextLabel) { + // Conflicted again in the next level. + const int newTableIndex = allocateTable(1 /* entryCount */); + if (newTableIndex == INVALID_INDEX) { + return false; + } + if (!writeEntry(conflictedEntry, newTableIndex)) { + return false; + } + const Entry newBitmapEntry(setExist(0 /* bitmap */, nextLabel), newTableIndex); + if (!writeEntry(newBitmapEntry, conflictedEntryIndex)) { + return false; + } + return putInternal(key, value, hashedKey, conflictedEntryIndex, newBitmapEntry, level + 1); + } + // The conflict has been resolved. Create a table that contains 2 entries. + const int newTableIndex = allocateTable(2 /* entryCount */); + if (newTableIndex == INVALID_INDEX) { + return false; + } + if (nextLabel < conflictedKeyNextLabel) { + if (!writeTerminalEntry(key, value, newTableIndex)) { + return false; + } + if (!writeEntry(conflictedEntry, newTableIndex + 1)) { + return false; + } + } else { // nextLabel > conflictedKeyNextLabel + if (!writeEntry(conflictedEntry, newTableIndex)) { + return false; + } + if (!writeTerminalEntry(key, value, newTableIndex + 1)) { + return false; + } + } + const uint32_t updatedBitmap = + setExist(setExist(0 /* bitmap */, nextLabel), conflictedKeyNextLabel); + return writeEntry(Entry(updatedBitmap, newTableIndex), conflictedEntryIndex); +} + +/** + * Add new entry to the existing table. + */ +bool TrieMap::addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, + const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, const int label) { + // Current map doesn't contain the label. + const int entryCount = popCount(bitmap); + const int newTableIndex = allocateTable(entryCount + 1); + if (newTableIndex == INVALID_INDEX) { + return false; + } + const int newEntryIndexInTable = popCount(bitmap, label); + // Copy from existing table to the new table. + for (int i = 0; i < entryCount; ++i) { + if (!copyEntry(tableIndex + i, newTableIndex + i + (i >= newEntryIndexInTable ? 1 : 0))) { + return false; + } + } + // Write new terminal entry. + if (!writeTerminalEntry(key, value, newTableIndex + newEntryIndexInTable)) { + return false; + } + // Update bitmap. + if (!writeEntry(Entry(setExist(bitmap, label), newTableIndex), bitmapEntryIndex)) { + return false; + } + if (entryCount > 0) { + return freeTable(tableIndex, entryCount); + } + return true; +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h new file mode 100644 index 000000000..3e5c4010c --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h @@ -0,0 +1,384 @@ +/* + * 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_TRIE_MAP_H +#define LATINIME_TRIE_MAP_H + +#include <climits> +#include <cstdint> +#include <cstdio> +#include <vector> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "utils/byte_array_view.h" + +namespace latinime { + +/** + * Trie map derived from Phil Bagwell's Hash Array Mapped Trie. + * key is int and value is uint64_t. + * This supports multiple level map. Terminal entries can have a bitmap for the next level map. + * This doesn't support root map resizing. + */ +class TrieMap { + public: + struct Result { + const uint64_t mValue; + const bool mIsValid; + const int mNextLevelBitmapEntryIndex; + + Result(const uint64_t value, const bool isValid, const int nextLevelBitmapEntryIndex) + : mValue(value), mIsValid(isValid), + mNextLevelBitmapEntryIndex(nextLevelBitmapEntryIndex) {} + }; + + /** + * Struct to record iteration state in a table. + */ + struct TableIterationState { + int mTableSize; + int mTableIndex; + int mCurrentIndex; + + TableIterationState(const int tableSize, const int tableIndex) + : mTableSize(tableSize), mTableIndex(tableIndex), mCurrentIndex(0) {} + }; + + class TrieMapRange; + class TrieMapIterator { + public: + class IterationResult { + public: + IterationResult(const TrieMap *const trieMap, const int key, const uint64_t value, + const int nextLeveBitmapEntryIndex) + : mTrieMap(trieMap), mKey(key), mValue(value), + mNextLevelBitmapEntryIndex(nextLeveBitmapEntryIndex) {} + + const TrieMapRange getEntriesInNextLevel() const { + return TrieMapRange(mTrieMap, mNextLevelBitmapEntryIndex); + } + + bool hasNextLevelMap() const { + return mNextLevelBitmapEntryIndex != INVALID_INDEX; + } + + AK_FORCE_INLINE int key() const { + return mKey; + } + + AK_FORCE_INLINE uint64_t value() const { + return mValue; + } + + private: + const TrieMap *const mTrieMap; + const int mKey; + const uint64_t mValue; + const int mNextLevelBitmapEntryIndex; + }; + + TrieMapIterator(const TrieMap *const trieMap, const int bitmapEntryIndex) + : mTrieMap(trieMap), mStateStack(), mBaseBitmapEntryIndex(bitmapEntryIndex), + mKey(0), mValue(0), mIsValid(false), mNextLevelBitmapEntryIndex(INVALID_INDEX) { + if (!trieMap) { + return; + } + const Entry bitmapEntry = mTrieMap->readEntry(mBaseBitmapEntryIndex); + mStateStack.emplace_back( + mTrieMap->popCount(bitmapEntry.getBitmap()), bitmapEntry.getTableIndex()); + this->operator++(); + } + + const IterationResult operator*() const { + return IterationResult(mTrieMap, mKey, mValue, mNextLevelBitmapEntryIndex); + } + + bool operator!=(const TrieMapIterator &other) const { + // Caveat: This works only for for loops. + return mIsValid || other.mIsValid; + } + + const TrieMapIterator &operator++() { + const Result result = mTrieMap->iterateNext(&mStateStack, &mKey); + mValue = result.mValue; + mIsValid = result.mIsValid; + mNextLevelBitmapEntryIndex = result.mNextLevelBitmapEntryIndex; + return *this; + } + + private: + DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapIterator); + DISALLOW_ASSIGNMENT_OPERATOR(TrieMapIterator); + + const TrieMap *const mTrieMap; + std::vector<TrieMap::TableIterationState> mStateStack; + const int mBaseBitmapEntryIndex; + int mKey; + uint64_t mValue; + bool mIsValid; + int mNextLevelBitmapEntryIndex; + }; + + /** + * Class to support iterating entries in TrieMap by range base for loops. + */ + class TrieMapRange { + public: + TrieMapRange(const TrieMap *const trieMap, const int bitmapEntryIndex) + : mTrieMap(trieMap), mBaseBitmapEntryIndex(bitmapEntryIndex) {}; + + TrieMapIterator begin() const { + return TrieMapIterator(mTrieMap, mBaseBitmapEntryIndex); + } + + const TrieMapIterator end() const { + return TrieMapIterator(nullptr, INVALID_INDEX); + } + + private: + DISALLOW_DEFAULT_CONSTRUCTOR(TrieMapRange); + DISALLOW_ASSIGNMENT_OPERATOR(TrieMapRange); + + const TrieMap *const mTrieMap; + const int mBaseBitmapEntryIndex; + }; + + static const int INVALID_INDEX; + static const uint64_t MAX_VALUE; + + TrieMap(); + // Construct TrieMap using existing data in the memory region written by save(). + TrieMap(const ReadWriteByteArrayView buffer); + void dump(const int from = 0, const int to = 0) const; + + bool isNearSizeLimit() const { + return mBuffer.isNearSizeLimit(); + } + + int getRootBitmapEntryIndex() const { + return ROOT_BITMAP_ENTRY_INDEX; + } + + // Returns bitmapEntryIndex. Create the next level map if it doesn't exist. + int getNextLevelBitmapEntryIndex(const int key) { + return getNextLevelBitmapEntryIndex(key, ROOT_BITMAP_ENTRY_INDEX); + } + + int getNextLevelBitmapEntryIndex(const int key, const int bitmapEntryIndex); + + const Result getRoot(const int key) const { + return get(key, ROOT_BITMAP_ENTRY_INDEX); + } + + const Result get(const int key, const int bitmapEntryIndex) const; + + bool putRoot(const int key, const uint64_t value) { + return put(key, value, ROOT_BITMAP_ENTRY_INDEX); + } + + bool put(const int key, const uint64_t value, const int bitmapEntryIndex); + + const TrieMapRange getEntriesInRootLevel() const { + return getEntriesInSpecifiedLevel(ROOT_BITMAP_ENTRY_INDEX); + } + + const TrieMapRange getEntriesInSpecifiedLevel(const int bitmapEntryIndex) const { + return TrieMapRange(this, bitmapEntryIndex); + } + + bool save(FILE *const file) const; + + private: + DISALLOW_COPY_AND_ASSIGN(TrieMap); + + /** + * Struct represents an entry. + * + * Entry is one of these entry types. All entries are fixed size and have 2 fields FIELD_0 and + * FIELD_1. + * 1. bitmap entry. bitmap entry contains bitmap and the link to hash table. + * FIELD_0(bitmap) FIELD_1(LINK_TO_HASH_TABLE) + * 2. terminal entry. terminal entry contains hashed key and value or terminal link. terminal + * entry have terminal link when the value is not fit to FIELD_1 or there is a next level map + * for the key. + * FIELD_0(hashed key) (FIELD_1(VALUE_FLAG VALUE) | FIELD_1(TERMINAL_LINK_FLAG TERMINAL_LINK)) + * 3. value entry. value entry represents a value. Upper order bytes are stored in FIELD_0 and + * lower order bytes are stored in FIELD_1. + * FIELD_0(value (upper order bytes)) FIELD_1(value (lower order bytes)) + */ + struct Entry { + const uint32_t mData0; + const uint32_t mData1; + + Entry(const uint32_t data0, const uint32_t data1) : mData0(data0), mData1(data1) {} + + AK_FORCE_INLINE bool isBitmapEntry() const { + return (mData1 & VALUE_FLAG) == 0 && (mData1 & TERMINAL_LINK_FLAG) == 0; + } + + AK_FORCE_INLINE bool hasTerminalLink() const { + return (mData1 & TERMINAL_LINK_FLAG) != 0; + } + + // For terminal entry. + AK_FORCE_INLINE uint32_t getKey() const { + return mData0; + } + + // For terminal entry. + AK_FORCE_INLINE uint32_t getValue() const { + return mData1 & VALUE_MASK; + } + + // For terminal entry. + AK_FORCE_INLINE uint32_t getValueEntryIndex() const { + return mData1 & TERMINAL_LINK_MASK; + } + + // For bitmap entry. + AK_FORCE_INLINE uint32_t getBitmap() const { + return mData0; + } + + // For bitmap entry. + AK_FORCE_INLINE int getTableIndex() const { + return static_cast<int>(mData1); + } + + // For value entry. + AK_FORCE_INLINE uint64_t getValueOfValueEntry() const { + return ((static_cast<uint64_t>(mData0) << (FIELD1_SIZE * CHAR_BIT)) ^ mData1); + } + }; + + BufferWithExtendableBuffer mBuffer; + + static const int FIELD0_SIZE; + static const int FIELD1_SIZE; + static const int ENTRY_SIZE; + static const uint32_t VALUE_FLAG; + static const uint32_t VALUE_MASK; + static const uint32_t TERMINAL_LINK_FLAG; + static const uint32_t TERMINAL_LINK_MASK; + static const int NUM_OF_BITS_USED_FOR_ONE_LEVEL; + static const uint32_t LABEL_MASK; + static const int MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL; + static const int ROOT_BITMAP_ENTRY_INDEX; + static const int ROOT_BITMAP_ENTRY_POS; + static const Entry EMPTY_BITMAP_ENTRY; + static const int MAX_BUFFER_SIZE; + + uint32_t getBitShuffledKey(const uint32_t key) const; + bool writeValue(const uint64_t value, const int terminalEntryIndex); + bool updateValue(const Entry &terminalEntry, const uint64_t value, + const int terminalEntryIndex); + bool freeTable(const int tableIndex, const int entryCount); + int allocateTable(const int entryCount); + int getTerminalEntryIndex(const uint32_t key, const uint32_t hashedKey, + const Entry &bitmapEntry, const int level) const; + const Result getInternal(const uint32_t key, const uint32_t hashedKey, + const int bitmapEntryIndex, const int level) const; + bool putInternal(const uint32_t key, const uint64_t value, const uint32_t hashedKey, + const int bitmapEntryIndex, const Entry &bitmapEntry, const int level); + bool addNewEntryByResolvingConflict(const uint32_t key, const uint64_t value, + const uint32_t hashedKey, const Entry &conflictedEntry, const int conflictedEntryIndex, + const int level); + bool addNewEntryByExpandingTable(const uint32_t key, const uint64_t value, + const int tableIndex, const uint32_t bitmap, const int bitmapEntryIndex, + const int label); + const Result iterateNext(std::vector<TableIterationState> *const iterationState, + int *const outKey) const; + + AK_FORCE_INLINE const Entry readEntry(const int entryIndex) const { + return Entry(readField0(entryIndex), readField1(entryIndex)); + } + + // Returns whether an entry for the index is existing by testing if the index-th bit in the + // bitmap is set or not. + AK_FORCE_INLINE bool exists(const uint32_t bitmap, const int index) const { + return (bitmap & (1 << index)) != 0; + } + + // Set index-th bit in the bitmap. + AK_FORCE_INLINE uint32_t setExist(const uint32_t bitmap, const int index) const { + return bitmap | (1 << index); + } + + // Count set bits before index in the bitmap. + AK_FORCE_INLINE int popCount(const uint32_t bitmap, const int index) const { + return popCount(bitmap & ((1 << index) - 1)); + } + + // Count set bits in the bitmap. + AK_FORCE_INLINE int popCount(const uint32_t bitmap) const { + return __builtin_popcount(bitmap); + // int v = bitmap - ((bitmap >> 1) & 0x55555555); + // v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + // return (((v + (v >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + } + + AK_FORCE_INLINE int getLabel(const uint32_t hashedKey, const int level) const { + return (hashedKey >> (level * NUM_OF_BITS_USED_FOR_ONE_LEVEL)) & LABEL_MASK; + } + + AK_FORCE_INLINE uint32_t readField0(const int entryIndex) const { + return mBuffer.readUint(FIELD0_SIZE, ROOT_BITMAP_ENTRY_POS + entryIndex * ENTRY_SIZE); + } + + AK_FORCE_INLINE uint32_t readField1(const int entryIndex) const { + return mBuffer.readUint(FIELD1_SIZE, + ROOT_BITMAP_ENTRY_POS + entryIndex * ENTRY_SIZE + FIELD0_SIZE); + } + + AK_FORCE_INLINE int readEmptyTableLink(const int entryCount) const { + return mBuffer.readUint(FIELD1_SIZE, (entryCount - 1) * FIELD1_SIZE); + } + + AK_FORCE_INLINE bool writeEmptyTableLink(const int tableIndex, const int entryCount) { + return mBuffer.writeUint(tableIndex, FIELD1_SIZE, (entryCount - 1) * FIELD1_SIZE); + } + + AK_FORCE_INLINE bool writeField0(const uint32_t data, const int entryIndex) { + return mBuffer.writeUint(data, FIELD0_SIZE, + ROOT_BITMAP_ENTRY_POS + entryIndex * ENTRY_SIZE); + } + + AK_FORCE_INLINE bool writeField1(const uint32_t data, const int entryIndex) { + return mBuffer.writeUint(data, FIELD1_SIZE, + ROOT_BITMAP_ENTRY_POS + entryIndex * ENTRY_SIZE + FIELD0_SIZE); + } + + AK_FORCE_INLINE bool writeEntry(const Entry &entry, const int entryIndex) { + return writeField0(entry.mData0, entryIndex) && writeField1(entry.mData1, entryIndex); + } + + AK_FORCE_INLINE bool writeTerminalEntry(const uint32_t key, const uint64_t value, + const int entryIndex) { + return writeField0(key, entryIndex) && writeValue(value, entryIndex); + } + + AK_FORCE_INLINE bool copyEntry(const int originalEntryIndex, const int newEntryIndex) { + return writeEntry(readEntry(originalEntryIndex), newEntryIndex); + } + + AK_FORCE_INLINE int getTailEntryIndex() const { + return (mBuffer.getTailPosition() - ROOT_BITMAP_ENTRY_POS) / ENTRY_SIZE; + } +}; + +} // namespace latinime +#endif /* LATINIME_TRIE_MAP_H */ diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h index 66ea62406..04cb6603a 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h @@ -69,10 +69,6 @@ class TypingScoring : public Scoring { return 0.0f; } - AK_FORCE_INLINE bool doesAutoCorrectValidWord() const { - return false; - } - AK_FORCE_INLINE bool autoCorrectsToMultiWordSuggestionIfTop() const { return true; } diff --git a/native/jni/src/utils/byte_array_view.h b/native/jni/src/utils/byte_array_view.h new file mode 100644 index 000000000..2c97c6d58 --- /dev/null +++ b/native/jni/src/utils/byte_array_view.h @@ -0,0 +1,87 @@ +/* + * 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_BYTE_ARRAY_VIEW_H +#define LATINIME_BYTE_ARRAY_VIEW_H + +#include <cstdint> +#include <cstdlib> + +#include "defines.h" + +namespace latinime { + +/** + * Helper class used to keep track of read accesses for a given memory region. + */ +class ReadOnlyByteArrayView { + public: + ReadOnlyByteArrayView() : mPtr(nullptr), mSize(0) {} + + ReadOnlyByteArrayView(const uint8_t *const ptr, const size_t size) + : mPtr(ptr), mSize(size) {} + + AK_FORCE_INLINE size_t size() const { + return mSize; + } + + AK_FORCE_INLINE const uint8_t *data() const { + return mPtr; + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(ReadOnlyByteArrayView); + + const uint8_t *const mPtr; + const size_t mSize; +}; + +/** + * Helper class used to keep track of read-write accesses for a given memory region. + */ +class ReadWriteByteArrayView { + public: + ReadWriteByteArrayView() : mPtr(nullptr), mSize(0) {} + + ReadWriteByteArrayView(uint8_t *const ptr, const size_t size) + : mPtr(ptr), mSize(size) {} + + AK_FORCE_INLINE size_t size() const { + return mSize; + } + + AK_FORCE_INLINE uint8_t *data() const { + return mPtr; + } + + AK_FORCE_INLINE ReadOnlyByteArrayView getReadOnlyView() const { + return ReadOnlyByteArrayView(mPtr, mSize); + } + + ReadWriteByteArrayView subView(const size_t start, const size_t n) const { + ASSERT(start + n <= mSize); + return ReadWriteByteArrayView(mPtr + start, n); + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(ReadWriteByteArrayView); + + uint8_t *const mPtr; + const size_t mSize; +}; + +} // namespace latinime +#endif // LATINIME_BYTE_ARRAY_VIEW_H diff --git a/native/jni/src/utils/int_array_view.h b/native/jni/src/utils/int_array_view.h new file mode 100644 index 000000000..c1ddc9812 --- /dev/null +++ b/native/jni/src/utils/int_array_view.h @@ -0,0 +1,105 @@ +/* + * 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_INT_ARRAY_VIEW_H +#define LATINIME_INT_ARRAY_VIEW_H + +#include <cstdint> +#include <cstdlib> +#include <vector> + +#include "defines.h" + +namespace latinime { + +/** + * Helper class used to provide a read-only view of a given range of integer array. This class + * does not take ownership of the underlying integer array but is designed to be a lightweight + * object that obeys value semantics. + * + * Example: + * <code> + * bool constinsX(IntArrayView view) { + * for (size_t i = 0; i < view.size(); ++i) { + * if (view[i] == 'X') { + * return true; + * } + * } + * return false; + * } + * + * const int codePointArray[] = { 'A', 'B', 'X', 'Z' }; + * auto view = IntArrayView(codePointArray, NELEMS(codePointArray)); + * const bool hasX = constinsX(view); + * </code> + */ +class IntArrayView { + public: + IntArrayView() : mPtr(nullptr), mSize(0) {} + + IntArrayView(const int *const ptr, const size_t size) + : mPtr(ptr), mSize(size) {} + + explicit IntArrayView(const std::vector<int> &vector) + : mPtr(vector.data()), mSize(vector.size()) {} + + template <int N> + AK_FORCE_INLINE static IntArrayView fromFixedSizeArray(const int (&array)[N]) { + return IntArrayView(array, N); + } + + // Returns a view that points one int object. Does not take ownership of the given object. + AK_FORCE_INLINE static IntArrayView fromObject(const int *const object) { + return IntArrayView(object, 1); + } + + AK_FORCE_INLINE int operator[](const size_t index) const { + ASSERT(index < mSize); + return mPtr[index]; + } + + AK_FORCE_INLINE bool empty() const { + return size() == 0; + } + + AK_FORCE_INLINE size_t size() const { + return mSize; + } + + AK_FORCE_INLINE const int *data() const { + return mPtr; + } + + AK_FORCE_INLINE const int *begin() const { + return mPtr; + } + + AK_FORCE_INLINE const int *end() const { + return mPtr + mSize; + } + + private: + DISALLOW_ASSIGNMENT_OPERATOR(IntArrayView); + + const int *const mPtr; + const size_t mSize; +}; + +using WordIdArrayView = IntArrayView; +using PtNodePosArrayView = IntArrayView; + +} // namespace latinime +#endif // LATINIME_MEMORY_VIEW_H diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp new file mode 100644 index 000000000..6eef2040b --- /dev/null +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h" + +#include <gtest/gtest.h> + +#include "utils/int_array_view.h" + +namespace latinime { +namespace { + +TEST(LanguageModelDictContentTest, TestUnigramProbability) { + LanguageModelDictContent LanguageModelDictContent(false /* useHistoricalInfo */); + + const int flag = 0xFF; + const int probability = 10; + const int wordId = 100; + const ProbabilityEntry probabilityEntry(flag, probability); + LanguageModelDictContent.setProbabilityEntry(wordId, &probabilityEntry); + const ProbabilityEntry entry = + LanguageModelDictContent.getProbabilityEntry(wordId); + EXPECT_EQ(flag, entry.getFlags()); + EXPECT_EQ(probability, entry.getProbability()); +} + +TEST(LanguageModelDictContentTest, TestUnigramProbabilityWithHistoricalInfo) { + LanguageModelDictContent LanguageModelDictContent(true /* useHistoricalInfo */); + + const int flag = 0xF0; + const int timestamp = 0x3FFFFFFF; + const int level = 3; + const int count = 10; + const int wordId = 100; + const HistoricalInfo historicalInfo(timestamp, level, count); + const ProbabilityEntry probabilityEntry(flag, NOT_A_PROBABILITY, &historicalInfo); + LanguageModelDictContent.setProbabilityEntry(wordId, &probabilityEntry); + const ProbabilityEntry entry = LanguageModelDictContent.getProbabilityEntry(wordId); + EXPECT_EQ(flag, entry.getFlags()); + EXPECT_EQ(timestamp, entry.getHistoricalInfo()->getTimeStamp()); + EXPECT_EQ(level, entry.getHistoricalInfo()->getLevel()); + EXPECT_EQ(count, entry.getHistoricalInfo()->getCount()); +} + +} // namespace +} // namespace latinime diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp new file mode 100644 index 000000000..db94550ef --- /dev/null +++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp @@ -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. + */ + +#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h" + +#include <gtest/gtest.h> + +#include "defines.h" + +namespace latinime { +namespace { + +TEST(ProbabilityEntryTest, TestEncodeDecode) { + const int flag = 0xFF; + const int probability = 10; + + const ProbabilityEntry entry(flag, probability); + const uint64_t encodedEntry = entry.encode(false /* hasHistoricalInfo */); + const ProbabilityEntry decodedEntry = + ProbabilityEntry::decode(encodedEntry, false /* hasHistoricalInfo */); + EXPECT_EQ(0xFF0Aull, encodedEntry); + EXPECT_EQ(flag, decodedEntry.getFlags()); + EXPECT_EQ(probability, decodedEntry.getProbability()); +} + +TEST(ProbabilityEntryTest, TestEncodeDecodeWithHistoricalInfo) { + const int flag = 0xF0; + const int timestamp = 0x3FFFFFFF; + const int level = 3; + const int count = 10; + + const HistoricalInfo historicalInfo(timestamp, level, count); + const ProbabilityEntry entry(flag, NOT_A_PROBABILITY, &historicalInfo); + + const uint64_t encodedEntry = entry.encode(true /* hasHistoricalInfo */); + EXPECT_EQ(0xF03FFFFFFF030Aull, encodedEntry); + const ProbabilityEntry decodedEntry = + ProbabilityEntry::decode(encodedEntry, true /* hasHistoricalInfo */); + + EXPECT_EQ(flag, decodedEntry.getFlags()); + EXPECT_EQ(timestamp, decodedEntry.getHistoricalInfo()->getTimeStamp()); + EXPECT_EQ(level, decodedEntry.getHistoricalInfo()->getLevel()); + EXPECT_EQ(count, decodedEntry.getHistoricalInfo()->getCount()); +} + +} // namespace +} // namespace latinime diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp new file mode 100644 index 000000000..df778b6cf --- /dev/null +++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp @@ -0,0 +1,224 @@ +/* + * 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/policyimpl/dictionary/utils/trie_map.h" + +#include <gtest/gtest.h> + +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <map> +#include <random> +#include <unordered_map> + +namespace latinime { +namespace { + +TEST(TrieMapTest, TestSetAndGet) { + TrieMap trieMap; + trieMap.putRoot(10, 10); + EXPECT_EQ(10ull, trieMap.getRoot(10).mValue); + trieMap.putRoot(0x10A, 10); + EXPECT_EQ(10ull, trieMap.getRoot(10).mValue); + EXPECT_EQ(10ull, trieMap.getRoot(0x10A).mValue); + trieMap.putRoot(10, 1000); + EXPECT_EQ(1000ull, trieMap.getRoot(10).mValue); + trieMap.putRoot(11, 1000); + EXPECT_EQ(1000ull, trieMap.getRoot(11).mValue); + const int next = trieMap.getNextLevelBitmapEntryIndex(10); + trieMap.put(9, 9, next); + EXPECT_EQ(9ull, trieMap.get(9, next).mValue); + EXPECT_FALSE(trieMap.get(11, next).mIsValid); + trieMap.putRoot(0, 0xFFFFFFFFFull); + EXPECT_EQ(0xFFFFFFFFFull, trieMap.getRoot(0).mValue); +} + +TEST(TrieMapTest, TestSetAndGetLarge) { + static const int ELEMENT_COUNT = 200000; + TrieMap trieMap; + for (int i = 0; i < ELEMENT_COUNT; ++i) { + EXPECT_TRUE(trieMap.putRoot(i, i)); + } + for (int i = 0; i < ELEMENT_COUNT; ++i) { + EXPECT_EQ(static_cast<uint64_t>(i), trieMap.getRoot(i).mValue); + } +} + +TEST(TrieMapTest, TestRandSetAndGetLarge) { + static const int ELEMENT_COUNT = 100000; + TrieMap trieMap; + std::unordered_map<int, uint64_t> testKeyValuePairs; + + // Use the uniform integer distribution [S_INT_MIN, S_INT_MAX]. + std::uniform_int_distribution<int> keyDistribution(S_INT_MIN, S_INT_MAX); + auto keyRandomNumberGenerator = std::bind(keyDistribution, std::mt19937()); + + // Use the uniform distribution [0, TrieMap::MAX_VALUE]. + std::uniform_int_distribution<uint64_t> valueDistribution(0, TrieMap::MAX_VALUE); + auto valueRandomNumberGenerator = std::bind(valueDistribution, std::mt19937()); + + for (int i = 0; i < ELEMENT_COUNT; ++i) { + const int key = keyRandomNumberGenerator(); + const uint64_t value = valueRandomNumberGenerator(); + EXPECT_TRUE(trieMap.putRoot(key, value)) << key << " " << value; + testKeyValuePairs[key] = value; + } + for (const auto &v : testKeyValuePairs) { + EXPECT_EQ(v.second, trieMap.getRoot(v.first).mValue); + } +} + +TEST(TrieMapTest, TestMultiLevel) { + static const int FIRST_LEVEL_ENTRY_COUNT = 10000; + static const int SECOND_LEVEL_ENTRY_COUNT = 20000; + static const int THIRD_LEVEL_ENTRY_COUNT = 40000; + + TrieMap trieMap; + std::vector<int> firstLevelKeys; + std::map<int, uint64_t> firstLevelEntries; + std::vector<std::pair<int, int>> secondLevelKeys; + std::map<int, std::map<int, uint64_t>> twoLevelMap; + std::map<int, std::map<int, std::map<int, uint64_t>>> threeLevelMap; + + // Use the uniform integer distribution [0, S_INT_MAX]. + std::uniform_int_distribution<int> distribution(0, S_INT_MAX); + auto keyRandomNumberGenerator = std::bind(distribution, std::mt19937()); + auto randomNumberGeneratorForKeySelection = std::bind(distribution, std::mt19937()); + + // Use the uniform distribution [0, TrieMap::MAX_VALUE]. + std::uniform_int_distribution<uint64_t> valueDistribution(0, TrieMap::MAX_VALUE); + auto valueRandomNumberGenerator = std::bind(valueDistribution, std::mt19937()); + + for (int i = 0; i < FIRST_LEVEL_ENTRY_COUNT; ++i) { + const int key = keyRandomNumberGenerator(); + const uint64_t value = valueRandomNumberGenerator(); + EXPECT_TRUE(trieMap.putRoot(key, value)); + firstLevelKeys.push_back(key); + firstLevelEntries[key] = value; + } + + for (int i = 0; i < SECOND_LEVEL_ENTRY_COUNT; ++i) { + const int key = keyRandomNumberGenerator(); + const uint64_t value = valueRandomNumberGenerator(); + const int firstLevelKey = + firstLevelKeys[randomNumberGeneratorForKeySelection() % FIRST_LEVEL_ENTRY_COUNT]; + const int nextLevelBitmapEntryIndex = trieMap.getNextLevelBitmapEntryIndex(firstLevelKey); + EXPECT_NE(TrieMap::INVALID_INDEX, nextLevelBitmapEntryIndex); + EXPECT_TRUE(trieMap.put(key, value, nextLevelBitmapEntryIndex)); + secondLevelKeys.push_back(std::make_pair(firstLevelKey, key)); + twoLevelMap[firstLevelKey][key] = value; + } + + for (int i = 0; i < THIRD_LEVEL_ENTRY_COUNT; ++i) { + const int key = keyRandomNumberGenerator(); + const uint64_t value = valueRandomNumberGenerator(); + const std::pair<int, int> secondLevelKey = + secondLevelKeys[randomNumberGeneratorForKeySelection() % SECOND_LEVEL_ENTRY_COUNT]; + const int secondLevel = trieMap.getNextLevelBitmapEntryIndex(secondLevelKey.first); + EXPECT_NE(TrieMap::INVALID_INDEX, secondLevel); + const int thirdLevel = trieMap.getNextLevelBitmapEntryIndex( + secondLevelKey.second, secondLevel); + EXPECT_NE(TrieMap::INVALID_INDEX, thirdLevel); + EXPECT_TRUE(trieMap.put(key, value, thirdLevel)); + threeLevelMap[secondLevelKey.first][secondLevelKey.second][key] = value; + } + + for (const auto &firstLevelEntry : firstLevelEntries) { + EXPECT_EQ(firstLevelEntry.second, trieMap.getRoot(firstLevelEntry.first).mValue); + } + + for (const auto &firstLevelEntry : twoLevelMap) { + const int secondLevel = trieMap.getNextLevelBitmapEntryIndex(firstLevelEntry.first); + EXPECT_NE(TrieMap::INVALID_INDEX, secondLevel); + for (const auto &secondLevelEntry : firstLevelEntry.second) { + EXPECT_EQ(secondLevelEntry.second, + trieMap.get(secondLevelEntry.first, secondLevel).mValue); + } + } + + for (const auto &firstLevelEntry : threeLevelMap) { + const int secondLevel = trieMap.getNextLevelBitmapEntryIndex(firstLevelEntry.first); + EXPECT_NE(TrieMap::INVALID_INDEX, secondLevel); + for (const auto &secondLevelEntry : firstLevelEntry.second) { + const int thirdLevel = + trieMap.getNextLevelBitmapEntryIndex(secondLevelEntry.first, secondLevel); + EXPECT_NE(TrieMap::INVALID_INDEX, thirdLevel); + for (const auto &thirdLevelEntry : secondLevelEntry.second) { + EXPECT_EQ(thirdLevelEntry.second, + trieMap.get(thirdLevelEntry.first, thirdLevel).mValue); + } + } + } + + // Iteration + for (const auto &firstLevelEntry : trieMap.getEntriesInRootLevel()) { + EXPECT_EQ(trieMap.getRoot(firstLevelEntry.key()).mValue, firstLevelEntry.value()); + EXPECT_EQ(firstLevelEntries[firstLevelEntry.key()], firstLevelEntry.value()); + firstLevelEntries.erase(firstLevelEntry.key()); + for (const auto &secondLevelEntry : firstLevelEntry.getEntriesInNextLevel()) { + EXPECT_EQ(twoLevelMap[firstLevelEntry.key()][secondLevelEntry.key()], + secondLevelEntry.value()); + twoLevelMap[firstLevelEntry.key()].erase(secondLevelEntry.key()); + for (const auto &thirdLevelEntry : secondLevelEntry.getEntriesInNextLevel()) { + EXPECT_EQ(threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()] + [thirdLevelEntry.key()], thirdLevelEntry.value()); + threeLevelMap[firstLevelEntry.key()][secondLevelEntry.key()].erase( + thirdLevelEntry.key()); + } + } + } + + // Ensure all entries have been traversed. + EXPECT_TRUE(firstLevelEntries.empty()); + for (const auto &secondLevelEntry : twoLevelMap) { + EXPECT_TRUE(secondLevelEntry.second.empty()); + } + for (const auto &secondLevelEntry : threeLevelMap) { + for (const auto &thirdLevelEntry : secondLevelEntry.second) { + EXPECT_TRUE(thirdLevelEntry.second.empty()); + } + } +} + +TEST(TrieMapTest, TestIteration) { + static const int ELEMENT_COUNT = 200000; + TrieMap trieMap; + std::unordered_map<int, uint64_t> testKeyValuePairs; + + // Use the uniform integer distribution [S_INT_MIN, S_INT_MAX]. + std::uniform_int_distribution<int> keyDistribution(S_INT_MIN, S_INT_MAX); + auto keyRandomNumberGenerator = std::bind(keyDistribution, std::mt19937()); + + // Use the uniform distribution [0, TrieMap::MAX_VALUE]. + std::uniform_int_distribution<uint64_t> valueDistribution(0, TrieMap::MAX_VALUE); + auto valueRandomNumberGenerator = std::bind(valueDistribution, std::mt19937()); + for (int i = 0; i < ELEMENT_COUNT; ++i) { + const int key = keyRandomNumberGenerator(); + const uint64_t value = valueRandomNumberGenerator(); + EXPECT_TRUE(trieMap.putRoot(key, value)); + testKeyValuePairs[key] = value; + } + for (const auto &entry : trieMap.getEntriesInRootLevel()) { + EXPECT_EQ(trieMap.getRoot(entry.key()).mValue, entry.value()); + EXPECT_EQ(testKeyValuePairs[entry.key()], entry.value()); + testKeyValuePairs.erase(entry.key()); + } + EXPECT_TRUE(testKeyValuePairs.empty()); +} + +} // namespace +} // namespace latinime diff --git a/native/jni/tests/utils/int_array_view_test.cpp b/native/jni/tests/utils/int_array_view_test.cpp new file mode 100644 index 000000000..bd843ab02 --- /dev/null +++ b/native/jni/tests/utils/int_array_view_test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/int_array_view.h" + +#include <gtest/gtest.h> + +#include <vector> + +namespace latinime { +namespace { + +TEST(IntArrayViewTest, TestAccess) { + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + EXPECT_EQ(intVector.size(), intArrayView.size()); + for (int i = 0; i < static_cast<int>(intVector.size()); ++i) { + EXPECT_EQ(intVector[i], intArrayView[i]); + } +} + +TEST(IntArrayViewTest, TestIteration) { + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + size_t expectedIndex = 0; + for (const int element : intArrayView) { + EXPECT_EQ(intVector[expectedIndex], element); + ++expectedIndex; + } + EXPECT_EQ(expectedIndex, intArrayView.size()); +} + +TEST(IntArrayViewTest, TestConstructFromArray) { + const size_t ARRAY_SIZE = 100; + int intArray[ARRAY_SIZE]; + const auto intArrayView = IntArrayView::fromFixedSizeArray(intArray); + EXPECT_EQ(ARRAY_SIZE, intArrayView.size()); +} + +TEST(IntArrayViewTest, TestConstructFromObject) { + const int object = 10; + const auto intArrayView = IntArrayView::fromObject(&object); + EXPECT_EQ(1, intArrayView.size()); + EXPECT_EQ(object, intArrayView[0]); +} + +} // namespace +} // namespace latinime diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelBase.java new file mode 100644 index 000000000..a25d6d6e7 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelBase.java @@ -0,0 +1,156 @@ +/* + * 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.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.utils.RunInLocale; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.Locale; + +abstract class KeyboardLayoutSetActionLabelBase extends KeyboardLayoutSetTestsBase { + public void testActionUnspecified() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "unspecifiled " + + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_UNSPECIFIED, + KeyboardIconsSet.NAME_ENTER_KEY); + } + } + + public void testActionNone() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, + KeyboardIconsSet.NAME_ENTER_KEY); + } + } + + public void testActionSearch() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "search " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_SEARCH, + KeyboardIconsSet.NAME_SEARCH_KEY); + } + } + + public abstract void testActionGo(); + public abstract void testActionSend(); + public abstract void testActionNext(); + public abstract void testActionDone(); + public abstract void testActionPrevious(); + + public void testActionCustom() { + for (final InputMethodSubtype subtype : getAllSubtypesList()) { + final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + final CharSequence customLabel = "customLabel"; + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED; + editorInfo.actionLabel = customLabel; + doTestActionKeyLabel(tag, subtype, editorInfo, customLabel); + } + } + + private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet, + final int elementId, final CharSequence label, final int iconId) { + final Keyboard keyboard = layoutSet.getKeyboard(elementId); + final Key enterKey = keyboard.getKey(Constants.CODE_ENTER); + assertNotNull(tag + " enter key on " + keyboard.mId, enterKey); + assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel()); + assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId()); + } + + protected void doTestActionKeyLabelResId(final String tag, final InputMethodSubtype subtype, + final int actionId, final int labelResId) { + final Locale labelLocale = subtype.getLocale().equals(SubtypeLocaleUtils.NO_LANGUAGE) + ? null : SubtypeLocaleUtils.getSubtypeLocale(subtype); + doTestActionKeyLabelResIdInLocale(tag, subtype, actionId, labelLocale, labelResId); + } + + protected void doTestActionKeyLabelResIdInLocale(final String tag, + final InputMethodSubtype subtype, final int actionId, final Locale labelLocale, + 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 String label = job.runInLocale(getContext().getResources(), labelLocale); + doTestActionKeyLabel(tag, subtype, editorInfo, label); + } + + protected void doTestActionKeyLabel(final String tag, final InputMethodSubtype subtype, + final EditorInfo editorInfo, final CharSequence label) { + // Test text layouts. + editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; + final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); + 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); + } + + protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype, + final int actionId, final String iconName) { + final int iconId = KeyboardIconsSet.getIconId(iconName); + final EditorInfo editorInfo = new EditorInfo(); + 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); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java index 96f925554..322a344ff 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelKlpTests.java @@ -18,174 +18,125 @@ 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.RichInputMethodManager; import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; +import java.util.Locale; + @MediumTest -public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetTestsBase { +public class KeyboardLayoutSetActionLabelKlpTests extends KeyboardLayoutSetActionLabelBase { @Override protected int getKeyboardThemeForTests() { return KeyboardTheme.THEME_ID_KLP; } - private static void doTestActionKey(final String tag, final KeyboardLayoutSet layoutSet, - final int elementId, final CharSequence label, final int iconId) { - final Keyboard keyboard = layoutSet.getKeyboard(elementId); - final Key enterKey = keyboard.getKey(Constants.CODE_ENTER); - assertNotNull(tag + " enter key on " + keyboard.mId, enterKey); - assertEquals(tag + " enter label " + enterKey, label, enterKey.getLabel()); - assertEquals(tag + " enter icon " + enterKey, iconId, enterKey.getIconId()); - } - - protected 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)); - } - doTestActionLabel(tag, subtype, editorInfo, label); - } - - protected void doTestActionLabel(final String tag, final InputMethodSubtype subtype, - final EditorInfo editorInfo, final CharSequence label) { - // Test text layouts. - editorInfo.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL; - final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); - 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); - } - - protected void doTestActionKeyIcon(final String tag, final InputMethodSubtype subtype, - final int actionId, final String iconName) { - final int iconId = KeyboardIconsSet.getIconId(iconName); - final EditorInfo editorInfo = new EditorInfo(); - 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, - KeyboardIconsSet.NAME_ENTER_KEY); - } - } - - public void testActionNone() { - for (final InputMethodSubtype subtype : getAllSubtypesList()) { - final String tag = "none " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); - doTestActionKeyIcon(tag, subtype, EditorInfo.IME_ACTION_NONE, - KeyboardIconsSet.NAME_ENTER_KEY); - } - } - + @Override 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, - KeyboardIconsSet.NAME_SEARCH_KEY); + doTestActionKeyLabelResId(tag, subtype, EditorInfo.IME_ACTION_GO, + R.string.label_go_key); } } + @Override 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); + doTestActionKeyLabelResId(tag, subtype, EditorInfo.IME_ACTION_SEND, + R.string.label_send_key); } } + @Override 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); + doTestActionKeyLabelResId(tag, subtype, EditorInfo.IME_ACTION_NEXT, + R.string.label_next_key); } } + @Override 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); + doTestActionKeyLabelResId(tag, subtype, EditorInfo.IME_ACTION_DONE, + R.string.label_done_key); } } + @Override 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); + doTestActionKeyLabelResId(tag, subtype, EditorInfo.IME_ACTION_PREVIOUS, + R.string.label_previous_key); } } - public void testActionCustom() { - for (final InputMethodSubtype subtype : getAllSubtypesList()) { - final String tag = "custom " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); - final CharSequence customLabel = "customLabel"; - final EditorInfo editorInfo = new EditorInfo(); - editorInfo.imeOptions = EditorInfo.IME_ACTION_UNSPECIFIED; - editorInfo.actionLabel = customLabel; - doTestActionLabel(tag, subtype, editorInfo, customLabel); + // Working variable to simulate system locale changing. + private Locale mSystemLocale = Locale.getDefault(); + + private void doTestActionLabelInLocale(final InputMethodSubtype subtype, + final Locale labelLocale, final Locale systemLocale) { + // Simulate system locale changing, see {@link SystemBroadcastReceiver}. + if (!systemLocale.equals(mSystemLocale)) { + KeyboardLayoutSet.onSystemLocaleChanged(); + mSystemLocale = systemLocale; } + final String tag = "label=" + labelLocale + " system=" + systemLocale + + " " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); + final RunInLocale<Void> job = new RunInLocale<Void>() { + @Override + public Void job(final Resources res) { + doTestActionKeyIcon(tag + " unspecified", subtype, + EditorInfo.IME_ACTION_UNSPECIFIED, KeyboardIconsSet.NAME_ENTER_KEY); + doTestActionKeyIcon(tag + " none", subtype, + EditorInfo.IME_ACTION_NONE, KeyboardIconsSet.NAME_ENTER_KEY); + doTestActionKeyLabelResIdInLocale(tag + " go", subtype, + EditorInfo.IME_ACTION_GO, labelLocale, R.string.label_go_key); + doTestActionKeyIcon(tag + " search", subtype, + EditorInfo.IME_ACTION_SEARCH, KeyboardIconsSet.NAME_SEARCH_KEY); + doTestActionKeyLabelResIdInLocale(tag + " send", subtype, + EditorInfo.IME_ACTION_SEND, labelLocale, R.string.label_send_key); + doTestActionKeyLabelResIdInLocale(tag + " next", subtype, + EditorInfo.IME_ACTION_NEXT, labelLocale, R.string.label_next_key); + doTestActionKeyLabelResIdInLocale(tag + " done", subtype, + EditorInfo.IME_ACTION_DONE, labelLocale, R.string.label_done_key); + doTestActionKeyLabelResIdInLocale(tag + " previous", subtype, + EditorInfo.IME_ACTION_PREVIOUS, labelLocale, R.string.label_previous_key); + return null; + } + }; + job.runInLocale(getContext().getResources(), systemLocale); + } + + public void testActionLabelInOtherLocale() { + final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + final InputMethodSubtype italian = richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.ITALIAN.toString(), SubtypeLocaleUtils.QWERTY); + // An action label should be displayed in subtype's locale regardless of the system locale. + doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.US); + doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.FRENCH); + doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.ITALIAN); + doTestActionLabelInLocale(italian, Locale.ITALIAN, Locale.JAPANESE); + } + + public void testNoLanguageSubtypeActionLabel() { + final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + final InputMethodSubtype noLanguage = richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY); + // An action label of no language keyboard should be displayed in the system locale. + doTestActionLabelInLocale(noLanguage, Locale.US, Locale.US); + doTestActionLabelInLocale(noLanguage, Locale.FRENCH, Locale.FRENCH); + doTestActionLabelInLocale(noLanguage, Locale.ITALIAN, Locale.ITALIAN); + doTestActionLabelInLocale(noLanguage, Locale.JAPANESE, Locale.JAPANESE); } } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java index 7747ac5f9..028b3e400 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetActionLabelLxxTests.java @@ -24,20 +24,10 @@ import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @MediumTest -public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActionLabelKlpTests { +public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActionLabelBase { @Override protected int getKeyboardThemeForTests() { - return KeyboardTheme.THEME_ID_LXX_DARK; - } - - @Override - public void testActionUnspecified() { - super.testActionUnspecified(); - } - - @Override - public void testActionNone() { - super.testActionNone(); + return KeyboardTheme.THEME_ID_LXX_LIGHT; } @Override @@ -50,11 +40,6 @@ public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActio } @Override - public void testActionSearch() { - super.testActionSearch(); - } - - @Override public void testActionSend() { for (final InputMethodSubtype subtype : getAllSubtypesList()) { final String tag = "send " + SubtypeLocaleUtils.getSubtypeNameForLogging(subtype); @@ -89,9 +74,4 @@ public class KeyboardLayoutSetActionLabelLxxTests extends KeyboardLayoutSetActio KeyboardIconsSet.NAME_PREVIOUS_KEY); } } - - @Override - public void testActionCustom() { - super.testActionCustom(); - } } diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java new file mode 100644 index 000000000..8a55455d0 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysBase.java @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard; + +import android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +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.R; +import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; + +import java.util.Arrays; +import java.util.Locale; + +abstract class KeyboardLayoutSetNavigateMoreKeysBase extends KeyboardLayoutSetTestsBase { + private ExpectedMoreKey mExpectedNavigateNextMoreKey; + private ExpectedMoreKey mExpectedNavigatePreviousMoreKey; + private ExpectedMoreKey mExpectedEmojiMoreKey; + + protected ExpectedMoreKey getExpectedNavigateNextMoreKey() { + return new ExpectedMoreKey(R.string.label_next_key); + } + + protected ExpectedMoreKey getExpectedNavigatePreviousMoreKey() { + return new ExpectedMoreKey(R.string.label_previous_key); + } + + protected ExpectedMoreKey getExpectedEmojiMoreKey() { + return new ExpectedMoreKey(KeyboardIconsSet.NAME_EMOJI_ACTION_KEY); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mExpectedNavigateNextMoreKey = getExpectedNavigateNextMoreKey(); + mExpectedNavigatePreviousMoreKey = getExpectedNavigatePreviousMoreKey(); + mExpectedEmojiMoreKey = getExpectedEmojiMoreKey(); + } + + /** + * This class represents an expected more key. + */ + protected static class ExpectedMoreKey { + public static final int NO_LABEL = 0; + public static final ExpectedMoreKey[] EMPTY_MORE_KEYS = new ExpectedMoreKey[0]; + + public final int mLabelResId; + public final int mIconId; + + public ExpectedMoreKey(final String iconName) { + mLabelResId = NO_LABEL; + mIconId = KeyboardIconsSet.getIconId(iconName); + } + + public ExpectedMoreKey(final int labelResId) { + mLabelResId = labelResId; + mIconId = KeyboardIconsSet.ICON_UNDEFINED; + } + } + + private void doTestMoreKeysOf(final int code, final InputMethodSubtype subtype, + final int elementId, final int inputType, final int imeOptions, + final ExpectedMoreKey ... expectedMoreKeys) { + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.inputType = inputType; + editorInfo.imeOptions = imeOptions; + final KeyboardLayoutSet layoutSet = createKeyboardLayoutSet(subtype, editorInfo); + final Keyboard keyboard = layoutSet.getKeyboard(elementId); + + final Key actualKey = keyboard.getKey(code); + final MoreKeySpec[] actualMoreKeys = actualKey.getMoreKeys(); + final String tag = actualKey.toString() + " moreKeys=" + Arrays.toString(actualMoreKeys); + if (expectedMoreKeys.length == 0) { + assertEquals(tag, null, actualMoreKeys); + return; + } + if (expectedMoreKeys.length == 1) { + assertEquals(tag + " fixedOrder", false, actualKey.isMoreKeysFixedOrder()); + assertEquals(tag + " fixedColumn", false, actualKey.isMoreKeysFixedColumn()); + } else { + assertEquals(tag + " fixedOrder", true, actualKey.isMoreKeysFixedOrder()); + assertEquals(tag + " fixedColumn", true, actualKey.isMoreKeysFixedColumn()); + // TODO: Can't handle multiple rows of more keys. + assertEquals(tag + " column", + expectedMoreKeys.length, actualKey.getMoreKeysColumnNumber()); + } + assertNotNull(tag + " moreKeys", actualMoreKeys); + assertEquals(tag, expectedMoreKeys.length, actualMoreKeys.length); + for (int index = 0; index < actualMoreKeys.length; index++) { + final int expectedLabelResId = expectedMoreKeys[index].mLabelResId; + if (expectedLabelResId == ExpectedMoreKey.NO_LABEL) { + assertEquals(tag + " label " + index, null, actualMoreKeys[index].mLabel); + } else { + final CharSequence expectedLabel = getContext().getText(expectedLabelResId); + assertEquals(tag + " label " + index, expectedLabel, actualMoreKeys[index].mLabel); + } + final int expectedIconId = expectedMoreKeys[index].mIconId; + assertEquals(tag + " icon " + index, expectedIconId, actualMoreKeys[index].mIconId); + } + } + + private void doTestNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype, + final int elementId, final int inputType) { + // No navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_NULL, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // With next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT, + mExpectedNavigateNextMoreKey); + // With previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedNavigatePreviousMoreKey); + // With next and previous naviagte flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedNavigatePreviousMoreKey, mExpectedNavigateNextMoreKey); + // Action next. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedNavigatePreviousMoreKey); + // Action next with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedNavigatePreviousMoreKey); + // Action previous. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + mExpectedNavigateNextMoreKey); + // Action previous with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedNavigateNextMoreKey); + } + + private void doTestNavigationWithEmojiMoreKeysOf(final int code, + final InputMethodSubtype subtype, final int elementId, final int inputType) { + // No navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_NULL, + mExpectedEmojiMoreKey); + // With next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT, + mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey); + // With previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey); + // With next and previous naviagte flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey, + mExpectedNavigateNextMoreKey); + // Action next. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT, + mExpectedEmojiMoreKey); + // Action next with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + mExpectedEmojiMoreKey); + // Action next with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey); + // Action next with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey, mExpectedNavigatePreviousMoreKey); + // Action previous. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS, + mExpectedEmojiMoreKey); + // Action previous with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey); + // Action previous with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey); + // Action previous with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + mExpectedEmojiMoreKey, mExpectedNavigateNextMoreKey); + } + + private void doTestNoNavigationMoreKeysOf(final int code, final InputMethodSubtype subtype, + final int elementId, final int inputType) { + // No navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_NULL, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // With next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // With previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // With next and previous naviagte flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_FLAG_NAVIGATE_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action next with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous with next navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous with previous navigate flag. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + // Action previous with next and previous navigate flags. + doTestMoreKeysOf(code, subtype, elementId, inputType, + EditorInfo.IME_ACTION_PREVIOUS | EditorInfo.IME_FLAG_NAVIGATE_NEXT + | EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS, + ExpectedMoreKey.EMPTY_MORE_KEYS); + } + + public void testMoreKeysOfEnterKey() { + final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + final InputMethodSubtype subtype = richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), SubtypeLocaleUtils.QWERTY); + + // Password field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + // Email field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + // Url field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI); + // Phone number field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_PHONE, + InputType.TYPE_CLASS_PHONE); + // Number field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER, + InputType.TYPE_CLASS_NUMBER); + // Date-time field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER, + InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_NORMAL); + // Date field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER, + InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE); + // Time field. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, KeyboardId.ELEMENT_NUMBER, + InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME); + // Text field. + if (isPhone()) { + // The enter key has an Emoji key as one of more keys. + doTestNavigationWithEmojiMoreKeysOf(Constants.CODE_ENTER, subtype, + KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT); + } else { + // Tablet has a dedicated Emoji key, so the Enter key has no Emoji more key. + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, + KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT); + } + // Short message field. + if (isPhone()) { + // Enter key is switched to Emoji key on a short message field. + // Emoji key has no navigation more keys. + doTestNoNavigationMoreKeysOf(Constants.CODE_EMOJI, subtype, + KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); + } else { + doTestNavigationMoreKeysOf(Constants.CODE_ENTER, subtype, + KeyboardId.ELEMENT_ALPHABET, + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysKlpTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysKlpTests.java new file mode 100644 index 000000000..286c69d33 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysKlpTests.java @@ -0,0 +1,28 @@ +/* + * 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; + +@SmallTest +public class KeyboardLayoutSetNavigateMoreKeysKlpTests + extends KeyboardLayoutSetNavigateMoreKeysBase { + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_KLP; + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysLxxTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysLxxTests.java new file mode 100644 index 000000000..d2bb41e5d --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetNavigateMoreKeysLxxTests.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; + +@SmallTest +public class KeyboardLayoutSetNavigateMoreKeysLxxTests + extends KeyboardLayoutSetNavigateMoreKeysBase { + @Override + protected int getKeyboardThemeForTests() { + return KeyboardTheme.THEME_ID_LXX_LIGHT; + } + + @Override + protected ExpectedMoreKey getExpectedNavigateNextMoreKey() { + return new ExpectedMoreKey(KeyboardIconsSet.NAME_NEXT_KEY); + } + + @Override + protected ExpectedMoreKey getExpectedNavigatePreviousMoreKey() { + return new ExpectedMoreKey(KeyboardIconsSet.NAME_PREVIOUS_KEY); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java index eb67bc134..6b0652c59 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java @@ -25,7 +25,7 @@ import java.util.ArrayList; @SmallTest public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase { - private static final int NUMBER_OF_SUBTYPES = 77; + private static final int NUMBER_OF_SUBTYPES = 76; private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 45; private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2; diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java index cf884bfea..a002bbe48 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetTestsBase.java @@ -42,7 +42,6 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { private final ArrayList<InputMethodSubtype> mAsciiCapableSubtypesList = new ArrayList<>(); private final ArrayList<InputMethodSubtype> mAdditionalSubtypesList = new ArrayList<>(); - private Context mThemeContext; private int mScreenMetrics; protected abstract int getKeyboardThemeForTests(); @@ -50,12 +49,14 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); - mScreenMetrics = mContext.getResources().getInteger(R.integer.config_screen_metrics); - final KeyboardTheme keyboardTheme = KeyboardTheme.searchKeyboardThemeById( getKeyboardThemeForTests()); - mThemeContext = new ContextThemeWrapper(mContext, keyboardTheme.mStyleId); - RichInputMethodManager.init(mThemeContext); + setContext(new ContextThemeWrapper(getContext(), keyboardTheme.mStyleId)); + KeyboardLayoutSet.onKeyboardThemeChanged(); + + final Context context = getContext(); + mScreenMetrics = context.getResources().getInteger(R.integer.config_screen_metrics); + RichInputMethodManager.init(context); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); final InputMethodInfo imi = richImm.getInputMethodInfoOfThisIme(); @@ -112,16 +113,16 @@ public abstract class KeyboardLayoutSetTestsBase extends AndroidTestCase { "Unknown subtype: locale=" + locale + " keyboardLayout=" + keyboardLayout); } - protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, final EditorInfo editorInfo) { return createKeyboardLayoutSet(subtype, editorInfo, false /* voiceInputKeyEnabled */, false /* languageSwitchKeyEnabled */); } - protected final KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, final boolean languageSwitchKeyEnabled) { - final Context context = mThemeContext; + final Context context = getContext(); final Resources res = context.getResources(); final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java index 0c7e4000e..c20954f81 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import static com.android.inputmethod.compat.BuildCompatUtils.VERSION_CODES_LXX; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_ICS; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_KLP; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_DARK; @@ -31,9 +32,6 @@ import android.test.suitebuilder.annotation.SmallTest; public class KeyboardThemeTests extends AndroidTestCase { private SharedPreferences mPrefs; - // TODO: Remove this constant once the *next* version becomes available. - private static final int VERSION_CODES_LXX = VERSION_CODES.CUR_DEVELOPMENT; - private static final int THEME_ID_NULL = -1; private static final int THEME_ID_UNKNOWN = -2; private static final int THEME_ID_ILLEGAL = -3; diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java new file mode 100644 index 000000000..ebefe2d0e --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderAutoOrderTests.java @@ -0,0 +1,2464 @@ +/* + * 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.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; + +import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams; + +@MediumTest +public class MoreKeysKeyboardBuilderAutoOrderTests extends AndroidTestCase { + private static final int WIDTH = 10; + private static final int HEIGHT = 10; + + private static final int KEYBOARD_WIDTH = WIDTH * 10; + private static final int XPOS_L0 = WIDTH * 0 + WIDTH / 2; + private static final int XPOS_L1 = WIDTH * 1 + WIDTH / 2; + private static final int XPOS_L2 = WIDTH * 2 + WIDTH / 2; + private static final int XPOS_L3 = WIDTH * 3 + WIDTH / 2; + private static final int XPOS_M0 = WIDTH * 4 + WIDTH / 2; + private static final int XPOS_M1 = WIDTH * 5 + WIDTH / 2; + private static final int XPOS_R3 = WIDTH * 6 + WIDTH / 2; + private static final int XPOS_R2 = WIDTH * 7 + WIDTH / 2; + private static final int XPOS_R1 = WIDTH * 8 + WIDTH / 2; + private static final int XPOS_R0 = WIDTH * 9 + WIDTH / 2; + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + private static MoreKeysKeyboardParams createParams(final int numKeys, final int columnNum, + final int coordXInParent) { + final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams(); + params.setParameters(numKeys, columnNum, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH, + true /* isMoreKeysFixedColumn */, false /* isMoreKeysFixedOrder */, + 0 /* dividerWidth */); + return params; + } + + public void testLayoutError() { + MoreKeysKeyboardParams params = null; + try { + final int maxColumns = KEYBOARD_WIDTH / WIDTH; + params = createParams(maxColumns + 1, maxColumns + 1, HEIGHT); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Too small keyboard to hold more keys keyboard. + } + assertNull("Too small keyboard to hold more keys keyboard", params); + } + + // More keys keyboard layout test. + // "[n]" represents n-th key position in more keys keyboard. + // "<1>" is the default key. + + // <1> + public void testLayout1KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_M0); + assertEquals("1 key auto 5 M0 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 M0 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 M0 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 M0 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 M0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |<1> + public void testLayout1KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L0); + assertEquals("1 key auto 5 L0 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 L0 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 L0 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> + public void testLayout1KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L1); + assertEquals("1 key auto 5 L1 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 L1 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 L1 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ ___ <1> + public void testLayout1KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_L2); + assertEquals("1 key auto 5 L2 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 L2 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 L2 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 L2 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 L2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // <1>| + public void testLayout1KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R0); + assertEquals("1 key auto 5 R0 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 R0 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 R0 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 R0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // <1> ___| + public void testLayout1KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R1); + assertEquals("1 key auto 5 R1 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 R1 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 R1 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 R1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // <1> ___ ___| + public void testLayout1KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(1, 5, XPOS_R2); + assertEquals("1 key auto 5 R2 columns", 1, params.mNumColumns); + assertEquals("1 key auto 5 R2 rows", 1, params.mNumRows); + assertEquals("1 key auto 5 R2 left", 0, params.mLeftKeys); + assertEquals("1 key auto 5 R2 right", 1, params.mRightKeys); + assertEquals("1 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("1 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("1 key auto 5 R2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // <1> [2] + public void testLayout2KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_M0); + assertEquals("2 key auto 5 M0 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 M0 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 M0 left", 0, params.mLeftKeys); + assertEquals("2 key auto 5 M0 right", 2, params.mRightKeys); + assertEquals("2 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("2 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 M0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |<1> [2] + public void testLayout2KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L0); + assertEquals("2 key auto 5 L0 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 L0 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("2 key auto 5 L0 right", 2, params.mRightKeys); + assertEquals("2 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("2 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] + public void testLayout2KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L1); + assertEquals("2 key auto 5 L1 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 L1 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("2 key auto 5 L1 right", 2, params.mRightKeys); + assertEquals("2 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("2 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ ___ <1> [2] + public void testLayout2KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_L2); + assertEquals("2 key auto 5 L2 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 L2 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 L2 left", 0, params.mLeftKeys); + assertEquals("2 key auto 5 L2 right", 2, params.mRightKeys); + assertEquals("2 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("2 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 L2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // [2] <1>| + public void testLayout2KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R0); + assertEquals("2 key auto 5 R0 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 R0 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 R0 left", 1, params.mLeftKeys); + assertEquals("2 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("2 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("2 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 R0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [2] <1> ___| + public void testLayout2KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R1); + assertEquals("2 key auto 5 R1 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 R1 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 R1 left", 1, params.mLeftKeys); + assertEquals("2 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("2 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("2 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 R1 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // <1> [2] ___| + public void testLayout2KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(2, 5, XPOS_R2); + assertEquals("2 key auto 5 R2 columns", 2, params.mNumColumns); + assertEquals("2 key auto 5 R2 rows", 1, params.mNumRows); + assertEquals("2 key auto 5 R2 left", 0, params.mLeftKeys); + assertEquals("2 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("2 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("2 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("2 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("2 key auto 5 R2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // [3] <1> [2] + public void testLayout3KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_M0); + assertEquals("3 key auto 5 M0 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 M0 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 M0 left", 1, params.mLeftKeys); + assertEquals("3 key auto 5 M0 right", 2, params.mRightKeys); + assertEquals("3 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("3 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |<1> [2] [3] + public void testLayout3KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L0); + assertEquals("3 key auto 5 L0 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 L0 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("3 key auto 5 L0 right", 3, params.mRightKeys); + assertEquals("3 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("3 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] [3] + public void testLayout3KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L1); + assertEquals("3 key auto 5 L1 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 L1 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("3 key auto 5 L1 right", 3, params.mRightKeys); + assertEquals("3 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("3 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] <1> [2] + public void testLayout3KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_L2); + assertEquals("3 key auto 5 L2 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 L2 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("3 key auto 5 L2 right", 2, params.mRightKeys); + assertEquals("3 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("3 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [3] [2] <1>| + public void testLayout3KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R0); + assertEquals("3 key auto 5 R0 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 R0 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 R0 left", 2, params.mLeftKeys); + assertEquals("3 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("3 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("3 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("3 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 R0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [3] [2] <1> ___| + public void testLayout3KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R1); + assertEquals("3 key auto 5 R1 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 R1 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 R1 left", 2, params.mLeftKeys); + assertEquals("3 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("3 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("3 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("3 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 R1 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [3] <1> [2] ___| + public void testLayout3KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(3, 5, XPOS_R2); + assertEquals("3 key auto 5 R2 columns", 3, params.mNumColumns); + assertEquals("3 key auto 5 R2 rows", 1, params.mNumRows); + assertEquals("3 key auto 5 R2 left", 1, params.mLeftKeys); + assertEquals("3 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("3 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("3 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 5 R2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [3] + // <1> [2] + public void testLayout3KeyAuto2M0() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_M0); + assertEquals("3 key auto 2 M0 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 M0 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 M0 left", 0, params.mLeftKeys); + assertEquals("3 key auto 2 M0 right", 2, params.mRightKeys); + assertEquals("3 key auto 2 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 2 M0 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 M0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |[3] + // |<1> [2] + public void testLayout3KeyAuto2L0() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L0); + assertEquals("3 key auto 2 L0 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 L0 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 L0 left", 0, params.mLeftKeys); + assertEquals("3 key auto 2 L0 right", 2, params.mRightKeys); + assertEquals("3 key auto 2 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 2 L0 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] + // |___ <1> [2] + public void testLayout3KeyAuto2L1() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L1); + assertEquals("3 key auto 2 L1 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 L1 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 L1 left", 0, params.mLeftKeys); + assertEquals("3 key auto 2 L1 right", 2, params.mRightKeys); + assertEquals("3 key auto 2 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 2 L1 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // | [3] + // |___ ___ <1> [2] + public void testLayout3KeyAuto2L2() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_L2); + assertEquals("3 key auto 2 L2 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 L2 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 L2 left", 0, params.mLeftKeys); + assertEquals("3 key auto 2 L2 right", 2, params.mRightKeys); + assertEquals("3 key auto 2 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 2 L2 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 L2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // [3]| + // [2] <1>| + public void testLayout3KeyAuto2R0() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R0); + assertEquals("3 key auto 2 R0 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 R0 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 R0 left", 1, params.mLeftKeys); + assertEquals("3 key auto 2 R0 right", 1, params.mRightKeys); + assertEquals("3 key auto 2 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("3 key auto 2 R0 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 R0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [3] | + // [2] <1> ___| + public void testLayout3KeyAuto2R1() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R1); + assertEquals("3 key auto 2 R1 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 R1 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 R1 left", 1, params.mLeftKeys); + assertEquals("3 key auto 2 R1 right", 1, params.mRightKeys); + assertEquals("3 key auto 2 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("3 key auto 2 R1 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 R1 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [3] | + // <1> [2] ___| + public void testLayout3KeyAuto2R2() { + MoreKeysKeyboardParams params = createParams(3, 2, XPOS_R2); + assertEquals("3 key auto 2 R2 columns", 2, params.mNumColumns); + assertEquals("3 key auto 2 R2 rows", 2, params.mNumRows); + assertEquals("3 key auto 2 R2 left", 0, params.mLeftKeys); + assertEquals("3 key auto 2 R2 right", 2, params.mRightKeys); + assertEquals("3 key auto 2 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("3 key auto 2 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("3 key auto 2 R2 [3]", 0, params.getColumnPos(2)); + assertEquals("3 key auto 2 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("3 key auto 2 R2 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // [4] + // [3] <1> [2] + public void testLayout4KeyAuto3M0() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_M0); + assertEquals("4 key auto 3 M0 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 M0 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 M0 left", 1, params.mLeftKeys); + assertEquals("4 key auto 3 M0 right", 2, params.mRightKeys); + assertEquals("4 key auto 3 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 3 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 3 M0 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |[4] + // |<1> [2] [3] + public void testLayout4KeyAuto3L0() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L0); + assertEquals("4 key auto 3 L0 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 L0 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 L0 left", 0, params.mLeftKeys); + assertEquals("4 key auto 3 L0 right", 3, params.mRightKeys); + assertEquals("4 key auto 3 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 3 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 3 L0 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [4] + // |___ <1> [2] [3] + public void testLayout4KeyAuto3L1() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L1); + assertEquals("4 key auto 3 L1 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 L1 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 L1 left", 0, params.mLeftKeys); + assertEquals("4 key auto 3 L1 right", 3, params.mRightKeys); + assertEquals("4 key auto 3 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 3 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 3 L1 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ ___ [4] + // |___ [3] <1> [2] + public void testLayout4KeyAuto3L2() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_L2); + assertEquals("4 key auto 3 L2 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 L2 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 L2 left", 1, params.mLeftKeys); + assertEquals("4 key auto 3 L2 right", 2, params.mRightKeys); + assertEquals("4 key auto 3 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 3 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 3 L2 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [4]| + // [3] [2] <1>| + public void testLayout4KeyAuto3R0() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R0); + assertEquals("4 key auto 3 R0 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 R0 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 R0 left", 2, params.mLeftKeys); + assertEquals("4 key auto 3 R0 right", 1, params.mRightKeys); + assertEquals("4 key auto 3 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 3 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 3 R0 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [4] ___| + // [3] [2] <1> ___| + public void testLayout4KeyAuto3R1() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R1); + assertEquals("4 key auto 3 R1 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 R1 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 R1 left", 2, params.mLeftKeys); + assertEquals("4 key auto 3 R1 right", 1, params.mRightKeys); + assertEquals("4 key auto 3 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 3 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 3 R1 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [4] ___| + // [3] <1> [2] ___| + public void testLayout4KeyAuto3R2() { + MoreKeysKeyboardParams params = createParams(4, 3, XPOS_R2); + assertEquals("4 key auto 3 R2 columns", 3, params.mNumColumns); + assertEquals("4 key auto 3 R2 rows", 2, params.mNumRows); + assertEquals("4 key auto 3 R2 left", 1, params.mLeftKeys); + assertEquals("4 key auto 3 R2 right", 2, params.mRightKeys); + assertEquals("4 key auto 3 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 3 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 3 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 3 R2 [4]", 0, params.getColumnPos(3)); + assertEquals("4 key auto 3 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [3] <1> [2] [4] + public void testLayout4KeyAuto4M0() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_M0); + assertEquals("4 key auto 4 M0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 M0 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 M0 left", 1, params.mLeftKeys); + assertEquals("4 key auto 4 M0 right", 3, params.mRightKeys); + assertEquals("4 key auto 4 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 4 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 4 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("4 key auto 4 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |<1> [2] [3] [4] + public void testLayout4KeyAuto4L0() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L0); + assertEquals("4 key auto 4 L0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 L0 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 L0 left", 0, params.mLeftKeys); + assertEquals("4 key auto 4 L0 right", 4, params.mRightKeys); + assertEquals("4 key auto 4 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 4 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 4 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("4 key auto 4 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] [3] [4] + public void testLayout4KeyAuto4L1() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L1); + assertEquals("4 key auto 4 L1 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 L1 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 L1 left", 0, params.mLeftKeys); + assertEquals("4 key auto 4 L1 right", 4, params.mRightKeys); + assertEquals("4 key auto 4 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 4 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 4 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("4 key auto 4 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] <1> [2] [4] + public void testLayout4KeyAuto4L2() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_L2); + assertEquals("4 key auto 4 L2 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 L2 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 L2 left", 1, params.mLeftKeys); + assertEquals("4 key auto 4 L2 right", 3, params.mRightKeys); + assertEquals("4 key auto 4 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 4 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 4 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("4 key auto 4 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [4] [3] [2] <1>| + public void testLayout4KeyAuto4R0() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R0); + assertEquals("4 key auto 4 R0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 R0 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 R0 left", 3, params.mLeftKeys); + assertEquals("4 key auto 4 R0 right", 1, params.mRightKeys); + assertEquals("4 key auto 4 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 4 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 4 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("4 key auto 4 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [4] [3] [2] <1> ___| + public void testLayout4KeyAuto4R1() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R1); + assertEquals("4 key auto 4 R1 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 R1 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 R1 left", 3, params.mLeftKeys); + assertEquals("4 key auto 4 R1 right", 1, params.mRightKeys); + assertEquals("4 key auto 4 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 4 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 4 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("4 key auto 4 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [4] [3] <1> [2] ___| + public void testLayout4KeyAuto4R2() { + MoreKeysKeyboardParams params = createParams(4, 4, XPOS_R2); + assertEquals("4 key auto 4 R2 columns", 4, params.mNumColumns); + assertEquals("4 key auto 4 R2 rows", 1, params.mNumRows); + assertEquals("4 key auto 4 R2 left", 2, params.mLeftKeys); + assertEquals("4 key auto 4 R2 right", 2, params.mRightKeys); + assertEquals("4 key auto 4 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 4 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 4 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 4 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("4 key auto 4 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [3] <1> [2] [4] + public void testLayout4KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_M0); + assertEquals("4 key auto 5 M0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 M0 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 M0 left", 1, params.mLeftKeys); + assertEquals("4 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("4 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("4 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |<1> [2] [3] [4] + public void testLayout4KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L0); + assertEquals("4 key auto 5 L0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 L0 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("4 key auto 5 L0 right", 4, params.mRightKeys); + assertEquals("4 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("4 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] [3] [4] + public void testLayout4KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L1); + assertEquals("4 key auto 5 L1 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 L1 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("4 key auto 5 L1 right", 4, params.mRightKeys); + assertEquals("4 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("4 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("4 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] <1> [2] [4] + public void testLayout4KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_L2); + assertEquals("4 key auto 5 L2 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 L2 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("4 key auto 5 L2 right", 3, params.mRightKeys); + assertEquals("4 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("4 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [4] [3] [2] <1>| + public void testLayout4KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R0); + assertEquals("4 key auto 5 R0 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 R0 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 R0 left", 3, params.mLeftKeys); + assertEquals("4 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("4 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("4 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 R0 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [4] [3] [2] <1> ___| + public void testLayout4KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R1); + assertEquals("4 key auto 5 R1 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 R1 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 R1 left", 3, params.mLeftKeys); + assertEquals("4 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("4 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("4 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("4 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("4 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 R1 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [4] [3] <1> [2] ___| + public void testLayout4KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(4, 5, XPOS_R2); + assertEquals("4 key auto 5 R2 columns", 4, params.mNumColumns); + assertEquals("4 key auto 5 R2 rows", 1, params.mNumRows); + assertEquals("4 key auto 5 R2 left", 2, params.mLeftKeys); + assertEquals("4 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("4 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("4 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("4 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("4 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("4 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("4 key auto 5 R2 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [4] [5] + // [3] <1> [2] + public void testLayout5KeyAuto3M0() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_M0); + assertEquals("5 key auto 3 M0 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 M0 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 M0 left", 1, params.mLeftKeys); + assertEquals("5 key auto 3 M0 right", 2, params.mRightKeys); + assertEquals("5 key auto 3 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 3 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 3 M0 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 M0 [5]", 1, params.getColumnPos(4)); + assertEquals("5 key auto 3 M0 adjust", -1, params.mTopRowAdjustment); + assertEquals("5 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |[4] [5] + // |<1> [2] [3] + public void testLayout5KeyAuto3L0() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L0); + assertEquals("5 key auto 3 L0 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 L0 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 L0 left", 0, params.mLeftKeys); + assertEquals("5 key auto 3 L0 right", 3, params.mRightKeys); + assertEquals("5 key auto 3 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 3 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 3 L0 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 L0 [5]", 1, params.getColumnPos(4)); + assertEquals("5 key auto 3 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [4] [5] + // |___ <1> [2] [3] + public void testLayout5KeyAuto3L1() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L1); + assertEquals("5 key auto 3 L1 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 L1 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 L1 left", 0, params.mLeftKeys); + assertEquals("5 key auto 3 L1 right", 3, params.mRightKeys); + assertEquals("5 key auto 3 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 3 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 3 L1 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 L1 [5]", 1, params.getColumnPos(4)); + assertEquals("5 key auto 3 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [4] [5] + // |___ [3] <1> [2] + public void testLayout5KeyAuto3L2() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_L2); + assertEquals("5 key auto 3 L2 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 L2 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 L2 left", 1, params.mLeftKeys); + assertEquals("5 key auto 3 L2 right", 2, params.mRightKeys); + assertEquals("5 key auto 3 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 3 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 3 L2 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 L2 [5]", 1, params.getColumnPos(4)); + assertEquals("5 key auto 3 L2 adjust", -1, params.mTopRowAdjustment); + assertEquals("5 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [5] [4]| + // [3] [2] <1>| + public void testLayout5KeyAuto3R0() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R0); + assertEquals("5 key auto 3 R0 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 R0 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 R0 left", 2, params.mLeftKeys); + assertEquals("5 key auto 3 R0 right", 1, params.mRightKeys); + assertEquals("5 key auto 3 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 3 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 3 R0 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 R0 [5]", -1, params.getColumnPos(4)); + assertEquals("5 key auto 3 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [5] [4] ___| + // [3] [2] <1> ___| + public void testLayout5KeyAuto3R1() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R1); + assertEquals("5 key auto 3 R1 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 R1 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 R1 left", 2, params.mLeftKeys); + assertEquals("5 key auto 3 R1 right", 1, params.mRightKeys); + assertEquals("5 key auto 3 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 3 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 3 R1 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 R1 [5]", -1, params.getColumnPos(4)); + assertEquals("5 key auto 3 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [4] [5] ___| + // [3] <1> [2] ___| + public void testLayout5KeyAuto3R2() { + MoreKeysKeyboardParams params = createParams(5, 3, XPOS_R2); + assertEquals("5 key auto 3 R2 columns", 3, params.mNumColumns); + assertEquals("5 key auto 3 R2 rows", 2, params.mNumRows); + assertEquals("5 key auto 3 R2 left", 1, params.mLeftKeys); + assertEquals("5 key auto 3 R2 right", 2, params.mRightKeys); + assertEquals("5 key auto 3 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 3 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 3 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 3 R2 [4]", 0, params.getColumnPos(3)); + assertEquals("5 key auto 3 R2 [5]", 1, params.getColumnPos(4)); + assertEquals("5 key auto 3 R2 adjust", -1, params.mTopRowAdjustment); + assertEquals("5 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [5] + // [3] <1> [2] [4] + public void testLayout5KeyAuto4M0() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_M0); + assertEquals("5 key auto 4 M0 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 M0 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 M0 left", 1, params.mLeftKeys); + assertEquals("5 key auto 4 M0 right", 3, params.mRightKeys); + assertEquals("5 key auto 4 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 4 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 4 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("5 key auto 4 M0 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |[5] + // |<1> [2] [3] [4] + public void testLayout5KeyAuto4L0() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L0); + assertEquals("5 key auto 4 L0 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 L0 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 L0 left", 0, params.mLeftKeys); + assertEquals("5 key auto 4 L0 right", 4, params.mRightKeys); + assertEquals("5 key auto 4 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 4 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 4 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("5 key auto 4 L0 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [5] + // |___ <1> [2] [3] [4] + public void testLayout5KeyAuto4L1() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L1); + assertEquals("5 key auto 4 L1 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 L1 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 L1 left", 0, params.mLeftKeys); + assertEquals("5 key auto 4 L1 right", 4, params.mRightKeys); + assertEquals("5 key auto 4 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 4 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 4 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("5 key auto 4 L1 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [5] + // |___ [3] <1> [2] [4] + public void testLayout5KeyAuto4L2() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_L2); + assertEquals("5 key auto 4 L2 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 L2 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 L2 left", 1, params.mLeftKeys); + assertEquals("5 key auto 4 L2 right", 3, params.mRightKeys); + assertEquals("5 key auto 4 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 4 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 4 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("5 key auto 4 L2 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [5]| + // [4] [3] [2] <1>| + public void testLayout5KeyAuto4R0() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R0); + assertEquals("5 key auto 4 R0 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 R0 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 R0 left", 3, params.mLeftKeys); + assertEquals("5 key auto 4 R0 right", 1, params.mRightKeys); + assertEquals("5 key auto 4 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 4 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 4 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("5 key auto 4 R0 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [5] ___| + // [4] [3] [2] <1> ___| + public void testLayout5KeyAuto4R1() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R1); + assertEquals("5 key auto 4 R1 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 R1 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 R1 left", 3, params.mLeftKeys); + assertEquals("5 key auto 4 R1 right", 1, params.mRightKeys); + assertEquals("5 key auto 4 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 4 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 4 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("5 key auto 4 R1 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [5] ___| + // [4] [3] <1> [2] ___| + public void testLayout5KeyAuto4R2() { + MoreKeysKeyboardParams params = createParams(5, 4, XPOS_R2); + assertEquals("5 key auto 4 R2 columns", 4, params.mNumColumns); + assertEquals("5 key auto 4 R2 rows", 2, params.mNumRows); + assertEquals("5 key auto 4 R2 left", 2, params.mLeftKeys); + assertEquals("5 key auto 4 R2 right", 2, params.mRightKeys); + assertEquals("5 key auto 4 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 4 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 4 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 4 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("5 key auto 4 R2 [5]", 0, params.getColumnPos(4)); + assertEquals("5 key auto 4 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [5] [3] <1> [2] [4] + public void testLayout5KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_M0); + assertEquals("5 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 M0 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("5 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("5 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("5 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("5 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |<1> [2] [3] [4] [5] + public void testLayout5KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L0); + assertEquals("5 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 L0 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("5 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("5 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("5 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("5 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] [3] [4] [5] + public void testLayout5KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L1); + assertEquals("5 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 L1 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("5 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("5 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("5 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("5 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("5 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] <1> [2] [4] [5] + public void testLayout5KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_L2); + assertEquals("5 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 L2 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("5 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("5 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("5 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("5 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [5] [4] [3] [2] <1>| + public void testLayout5KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R0); + assertEquals("5 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 R0 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("5 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("5 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("5 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("5 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [5] [4] [3] [2] <1> ___| + public void testLayout5KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R1); + assertEquals("5 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 R1 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("5 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("5 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("5 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("5 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("5 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("5 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [5] [4] [3] <1> [2] ___| + public void testLayout5KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(5, 5, XPOS_R2); + assertEquals("5 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("5 key auto 5 R2 rows", 1, params.mNumRows); + assertEquals("5 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("5 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("5 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("5 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("5 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("5 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("5 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("5 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("5 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [5] [6] + // [3] <1> [2] [4] + public void testLayout6KeyAuto4M0() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_M0); + assertEquals("6 key auto 4 M0 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 M0 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 M0 left", 1, params.mLeftKeys); + assertEquals("6 key auto 4 M0 right", 3, params.mRightKeys); + assertEquals("6 key auto 4 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 4 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 4 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("6 key auto 4 M0 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 M0 [6]", 1, params.getColumnPos(5)); + assertEquals("6 key auto 4 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |[5] [6] + // |<1> [2] [3] [4] + public void testLayout6KeyAuto4L0() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L0); + assertEquals("6 key auto 4 L0 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 L0 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 L0 left", 0, params.mLeftKeys); + assertEquals("6 key auto 4 L0 right", 4, params.mRightKeys); + assertEquals("6 key auto 4 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 4 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("6 key auto 4 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("6 key auto 4 L0 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 L0 [6]", 1, params.getColumnPos(5)); + assertEquals("6 key auto 4 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [5] [6] + // |___ <1> [2] [3] [4] + public void testLayout6KeyAuto4L1() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L1); + assertEquals("6 key auto 4 L1 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 L1 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 L1 left", 0, params.mLeftKeys); + assertEquals("6 key auto 4 L1 right", 4, params.mRightKeys); + assertEquals("6 key auto 4 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 4 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("6 key auto 4 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("6 key auto 4 L1 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 L1 [6]", 1, params.getColumnPos(5)); + assertEquals("6 key auto 4 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [5] [6] + // |___ [3] <1> [2] [4] + public void testLayout6KeyAuto4L2() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_L2); + assertEquals("6 key auto 4 L2 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 L2 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 L2 left", 1, params.mLeftKeys); + assertEquals("6 key auto 4 L2 right", 3, params.mRightKeys); + assertEquals("6 key auto 4 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 4 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 4 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("6 key auto 4 L2 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 L2 [6]", 1, params.getColumnPos(5)); + assertEquals("6 key auto 4 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [6] [5]| + // [4] [3] [2] <1>| + public void testLayout6KeyAuto4R0() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R0); + assertEquals("6 key auto 4 R0 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 R0 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 R0 left", 3, params.mLeftKeys); + assertEquals("6 key auto 4 R0 right", 1, params.mRightKeys); + assertEquals("6 key auto 4 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("6 key auto 4 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("6 key auto 4 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("6 key auto 4 R0 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 R0 [6]", -1, params.getColumnPos(5)); + assertEquals("6 key auto 4 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 R0 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [6] [5] ___| + // [4] [3] [2] <1> ___| + public void testLayout6KeyAuto4R1() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R1); + assertEquals("6 key auto 4 R1 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 R1 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 R1 left", 3, params.mLeftKeys); + assertEquals("6 key auto 4 R1 right", 1, params.mRightKeys); + assertEquals("6 key auto 4 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("6 key auto 4 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("6 key auto 4 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("6 key auto 4 R1 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 R1 [6]", -1, params.getColumnPos(5)); + assertEquals("6 key auto 4 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 R1 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [5] [6] ___| + // [4] [3] <1> [2] ___| + public void testLayout6KeyAuto4R2() { + MoreKeysKeyboardParams params = createParams(6, 4, XPOS_R2); + assertEquals("6 key auto 4 R2 columns", 4, params.mNumColumns); + assertEquals("6 key auto 4 R2 rows", 2, params.mNumRows); + assertEquals("6 key auto 4 R2 left", 2, params.mLeftKeys); + assertEquals("6 key auto 4 R2 right", 2, params.mRightKeys); + assertEquals("6 key auto 4 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 4 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 4 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 4 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("6 key auto 4 R2 [5]", 0, params.getColumnPos(4)); + assertEquals("6 key auto 4 R2 [6]", 1, params.getColumnPos(5)); + assertEquals("6 key auto 4 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 4 R2 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [6] + // [5] [3] <1> [2] [4] + public void testLayout6KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_M0); + assertEquals("6 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 M0 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("6 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("6 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("6 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("6 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |[6] + // |<1> [2] [3] [4] [5] + public void testLayout6KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L0); + assertEquals("6 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 L0 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("6 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("6 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("6 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("6 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("6 key auto 5 L0 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] + // |___ <1> [2] [3] [4] [5] + public void testLayout6KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L1); + assertEquals("6 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 L1 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("6 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("6 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("6 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("6 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("6 key auto 5 L1 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] + // |___ [3] <1> [2] [4] [5] + public void testLayout6KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_L2); + assertEquals("6 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 L2 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("6 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("6 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("6 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("6 key auto 5 L2 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [6]| + // [5] [4] [3] [2] <1>| + public void testLayout6KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R0); + assertEquals("6 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 R0 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("6 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("6 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("6 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("6 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("6 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("6 key auto 5 R0 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [6] ___| + // [5] [4] [3] [2] <1> ___| + public void testLayout6KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R1); + assertEquals("6 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 R1 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("6 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("6 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("6 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("6 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("6 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("6 key auto 5 R1 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [6] ___| + // [5] [4] [3] <1> [2] ___| + public void testLayout6KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(6, 5, XPOS_R2); + assertEquals("6 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("6 key auto 5 R2 rows", 2, params.mNumRows); + assertEquals("6 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("6 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("6 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("6 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("6 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("6 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("6 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("6 key auto 5 R2 [6]", 0, params.getColumnPos(5)); + assertEquals("6 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("6 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // |<1> [2] [3] [4] [5] [6] [7] ___ ___ ___| + public void testLayout7KeyAuto7L0() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L0); + assertEquals("7 key auto 7 L0 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 L0 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 L0 left", 0, params.mLeftKeys); + assertEquals("7 key auto 7 L0 right", 7, params.mRightKeys); + assertEquals("7 key auto 7 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 7 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("7 key auto 7 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("7 key auto 7 L0 [6]", 5, params.getColumnPos(5)); + assertEquals("7 key auto 7 L0 [7]", 6, params.getColumnPos(6)); + assertEquals("7 key auto 7 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ <1> [2] [3] [4] [5] [6] [7] ___ ___| + public void testLayout7KeyAuto7L1() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L1); + assertEquals("7 key auto 7 L1 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 L1 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 L1 left", 0, params.mLeftKeys); + assertEquals("7 key auto 7 L1 right", 7, params.mRightKeys); + assertEquals("7 key auto 7 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 7 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("7 key auto 7 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("7 key auto 7 L1 [6]", 5, params.getColumnPos(5)); + assertEquals("7 key auto 7 L1 [7]", 6, params.getColumnPos(6)); + assertEquals("7 key auto 7 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [3] <1> [2] [4] [5] [6] [7] ___ ___| + public void testLayout7KeyAuto7L2() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L2); + assertEquals("7 key auto 7 L2 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 L2 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 L2 left", 1, params.mLeftKeys); + assertEquals("7 key auto 7 L2 right", 6, params.mRightKeys); + assertEquals("7 key auto 7 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 7 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("7 key auto 7 L2 [6]", 4, params.getColumnPos(5)); + assertEquals("7 key auto 7 L2 [7]", 5, params.getColumnPos(6)); + assertEquals("7 key auto 7 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |___ [5] [3] <1> [2] [4] [6] [7] ___ ___| + public void testLayout7KeyAuto7L3() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_L3); + assertEquals("7 key auto 7 L3 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 L3 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 L3 left", 2, params.mLeftKeys); + assertEquals("7 key auto 7 L3 right", 5, params.mRightKeys); + assertEquals("7 key auto 7 L3 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 L3 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 L3 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 L3 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 7 L3 [5]", -2, params.getColumnPos(4)); + assertEquals("7 key auto 7 L3 [6]", 3, params.getColumnPos(5)); + assertEquals("7 key auto 7 L3 [7]", 4, params.getColumnPos(6)); + assertEquals("7 key auto 7 L3 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 L3 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |___ [7] [5] [3] <1> [2] [4] [6] ___ ___| + public void testLayout7KeyAuto7M0() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_M0); + assertEquals("7 key auto 7 M0 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 M0 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 M0 left", 3, params.mLeftKeys); + assertEquals("7 key auto 7 M0 right", 4, params.mRightKeys); + assertEquals("7 key auto 7 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 7 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("7 key auto 7 M0 [6]", 3, params.getColumnPos(5)); + assertEquals("7 key auto 7 M0 [7]", -3, params.getColumnPos(6)); + assertEquals("7 key auto 7 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 M0 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // |___ ___ [7] [5] [3] <1> [2] [4] [6] ___| + public void testLayout7KeyAuto7M1() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_M1); + assertEquals("7 key auto 7 M1 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 M1 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 M1 left", 3, params.mLeftKeys); + assertEquals("7 key auto 7 M1 right", 4, params.mRightKeys); + assertEquals("7 key auto 7 M1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 M1 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 M1 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 M1 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 7 M1 [5]", -2, params.getColumnPos(4)); + assertEquals("7 key auto 7 M1 [6]", 3, params.getColumnPos(5)); + assertEquals("7 key auto 7 M1 [7]", -3, params.getColumnPos(6)); + assertEquals("7 key auto 7 M1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 M1 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // |___ ___ [7] [6] [5] [3] <1> [2] [4] ___| + public void testLayout7KeyAuto7R3() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R3); + assertEquals("7 key auto 7 R3 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 R3 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 R3 left", 4, params.mLeftKeys); + assertEquals("7 key auto 7 R3 right", 3, params.mRightKeys); + assertEquals("7 key auto 7 R3 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 R3 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 R3 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 R3 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 7 R3 [5]", -2, params.getColumnPos(4)); + assertEquals("7 key auto 7 R3 [6]", -3, params.getColumnPos(5)); + assertEquals("7 key auto 7 R3 [7]", -4, params.getColumnPos(6)); + assertEquals("7 key auto 7 R3 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 R3 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // |___ ___ [7] [6] [5] [4] [3] <1> [2] ___| + public void testLayout7KeyAuto7R2() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R2); + assertEquals("7 key auto 7 R2 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 R2 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 R2 left", 5, params.mLeftKeys); + assertEquals("7 key auto 7 R2 right", 2, params.mRightKeys); + assertEquals("7 key auto 7 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 7 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 7 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("7 key auto 7 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("7 key auto 7 R2 [6]", -4, params.getColumnPos(5)); + assertEquals("7 key auto 7 R2 [7]", -5, params.getColumnPos(6)); + assertEquals("7 key auto 7 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 R2 default", WIDTH * 5, params.getDefaultKeyCoordX()); + } + + // |___ ___ [7] [6] [5] [4] [3] [2] <1> ___| + public void testLayout7KeyAuto7R1() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R1); + assertEquals("7 key auto 7 R1 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 R1 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 R1 left", 6, params.mLeftKeys); + assertEquals("7 key auto 7 R1 right", 1, params.mRightKeys); + assertEquals("7 key auto 7 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 7 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 7 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("7 key auto 7 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("7 key auto 7 R1 [6]", -5, params.getColumnPos(5)); + assertEquals("7 key auto 7 R1 [7]", -6, params.getColumnPos(6)); + assertEquals("7 key auto 7 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 R1 default", WIDTH * 6, params.getDefaultKeyCoordX()); + } + + // |___ ___ [7] [6] [5] [4] [3] [2] <1>| + public void testLayout7KeyAuto7R0() { + MoreKeysKeyboardParams params = createParams(7, 7, XPOS_R0); + assertEquals("7 key auto 7 R0 columns", 7, params.mNumColumns); + assertEquals("7 key auto 7 R0 rows", 1, params.mNumRows); + assertEquals("7 key auto 7 R0 left", 6, params.mLeftKeys); + assertEquals("7 key auto 7 R0 right", 1, params.mRightKeys); + assertEquals("7 key auto 7 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 7 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 7 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 7 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("7 key auto 7 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("7 key auto 7 R0 [6]", -5, params.getColumnPos(5)); + assertEquals("7 key auto 7 R0 [7]", -6, params.getColumnPos(6)); + assertEquals("7 key auto 7 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 7 R0 default", WIDTH * 6, params.getDefaultKeyCoordX()); + } + + // [6] [7] + // [5] [3] <1> [2] [4] + public void testLayout7KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_M0); + assertEquals("7 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 M0 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("7 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("7 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("7 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("7 key auto 5 M0 adjust", -1, params.mTopRowAdjustment); + assertEquals("7 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |[6] [7] + // |<1> [2] [3] [4] [5] + public void testLayout7KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L0); + assertEquals("7 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 L0 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("7 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("7 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("7 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("7 key auto 5 L0 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 L0 [7]", 1, params.getColumnPos(6)); + assertEquals("7 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] + // |___ <1> [2] [3] [4] [5] + public void testLayout7KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L1); + assertEquals("7 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 L1 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("7 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("7 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("7 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("7 key auto 5 L1 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 L1 [7]", 1, params.getColumnPos(6)); + assertEquals("7 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] + // |___ [3] <1> [2] [4] [5] + public void testLayout7KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_L2); + assertEquals("7 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 L2 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("7 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("7 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("7 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("7 key auto 5 L2 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 L2 [7]", 1, params.getColumnPos(6)); + assertEquals("7 key auto 5 L2 adjust", -1, params.mTopRowAdjustment); + assertEquals("7 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [7] [6]| + // [5] [4] [3] [2] <1>| + public void testLayout7KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R0); + assertEquals("7 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 R0 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("7 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("7 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("7 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("7 key auto 5 R0 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 R0 [7]", -1, params.getColumnPos(6)); + assertEquals("7 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [7] [6] ___| + // [5] [4] [3] [2] <1> ___| + public void testLayout7KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R1); + assertEquals("7 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 R1 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("7 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("7 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("7 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("7 key auto 5 R1 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 R1 [7]", -1, params.getColumnPos(6)); + assertEquals("7 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [6] [7] ___| + // [5] [4] [3] <1> [2] ___| + public void testLayout7KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(7, 5, XPOS_R2); + assertEquals("7 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("7 key auto 5 R2 rows", 2, params.mNumRows); + assertEquals("7 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("7 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("7 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("7 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("7 key auto 5 R2 [6]", 0, params.getColumnPos(5)); + assertEquals("7 key auto 5 R2 [7]", 1, params.getColumnPos(6)); + assertEquals("7 key auto 5 R2 adjust", -1, params.mTopRowAdjustment); + assertEquals("7 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [7] + // [6] [4] [5] + // [3] <1> [2] + public void testLayout7KeyAuto3M0() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_M0); + assertEquals("7 key auto 3 M0 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 M0 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 M0 left", 1, params.mLeftKeys); + assertEquals("7 key auto 3 M0 right", 2, params.mRightKeys); + assertEquals("7 key auto 3 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 3 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 3 M0 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 M0 [5]", 1, params.getColumnPos(4)); + assertEquals("7 key auto 3 M0 [6]", -1, params.getColumnPos(5)); + assertEquals("7 key auto 3 M0 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 M0 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // |[7] + // |[4] [5] [6] + // |<1> [2] [3] + public void testLayout7KeyAuto3L0() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L0); + assertEquals("7 key auto 3 L0 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 L0 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 L0 left", 0, params.mLeftKeys); + assertEquals("7 key auto 3 L0 right", 3, params.mRightKeys); + assertEquals("7 key auto 3 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 3 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 3 L0 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 L0 [5]", 1, params.getColumnPos(4)); + assertEquals("7 key auto 3 L0 [6]", 2, params.getColumnPos(5)); + assertEquals("7 key auto 3 L0 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [7] + // |___ [4] [5] [6] + // |___ <1> [2] [3] + public void testLayout7KeyAuto3L1() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L1); + assertEquals("7 key auto 3 L1 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 L1 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 L1 left", 0, params.mLeftKeys); + assertEquals("7 key auto 3 L1 right", 3, params.mRightKeys); + assertEquals("7 key auto 3 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 3 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("7 key auto 3 L1 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 L1 [5]", 1, params.getColumnPos(4)); + assertEquals("7 key auto 3 L1 [6]", 2, params.getColumnPos(5)); + assertEquals("7 key auto 3 L1 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [7] + // |___ [6] [4] [5] + // |___ [3] <1> [2] + public void testLayout7KeyAuto3L2() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_L2); + assertEquals("7 key auto 3 L2 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 L2 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 L2 left", 1, params.mLeftKeys); + assertEquals("7 key auto 3 L2 right", 2, params.mRightKeys); + assertEquals("7 key auto 3 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 3 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 3 L2 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 L2 [5]", 1, params.getColumnPos(4)); + assertEquals("7 key auto 3 L2 [6]", -1, params.getColumnPos(5)); + assertEquals("7 key auto 3 L2 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [7]| + // [6] [5] [4]| + // [3] [2] <1>| + public void testLayout7KeyAuto3R0() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R0); + assertEquals("7 key auto 3 R0 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 R0 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 R0 left", 2, params.mLeftKeys); + assertEquals("7 key auto 3 R0 right", 1, params.mRightKeys); + assertEquals("7 key auto 3 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 3 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 3 R0 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 R0 [5]", -1, params.getColumnPos(4)); + assertEquals("7 key auto 3 R0 [6]", -2, params.getColumnPos(5)); + assertEquals("7 key auto 3 R0 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 R0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [7] ___| + // [6] [5] [4] ___| + // [3] [2] <1> ___| + public void testLayout7KeyAuto3R1() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R1); + assertEquals("7 key auto 3 R1 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 R1 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 R1 left", 2, params.mLeftKeys); + assertEquals("7 key auto 3 R1 right", 1, params.mRightKeys); + assertEquals("7 key auto 3 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("7 key auto 3 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("7 key auto 3 R1 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 R1 [5]", -1, params.getColumnPos(4)); + assertEquals("7 key auto 3 R1 [6]", -2, params.getColumnPos(5)); + assertEquals("7 key auto 3 R1 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 R1 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [7] ___| + // [6] [4] [5] ___| + // [3] <1> [2] ___| + public void testLayout7KeyAuto3R2() { + MoreKeysKeyboardParams params = createParams(7, 3, XPOS_R2); + assertEquals("7 key auto 3 R2 columns", 3, params.mNumColumns); + assertEquals("7 key auto 3 R2 rows", 3, params.mNumRows); + assertEquals("7 key auto 3 R2 left", 1, params.mLeftKeys); + assertEquals("7 key auto 3 R2 right", 2, params.mRightKeys); + assertEquals("7 key auto 3 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("7 key auto 3 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("7 key auto 3 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("7 key auto 3 R2 [4]", 0, params.getColumnPos(3)); + assertEquals("7 key auto 3 R2 [5]", 1, params.getColumnPos(4)); + assertEquals("7 key auto 3 R2 [6]", -1, params.getColumnPos(5)); + assertEquals("7 key auto 3 R2 [7]", 0, params.getColumnPos(6)); + assertEquals("7 key auto 3 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("7 key auto 3 R2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [8] [6] [7] + // [5] [3] <1> [2] [4] + public void testLayout8KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_M0); + assertEquals("8 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 M0 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("8 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("8 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("8 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("8 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("8 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("8 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("8 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("8 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |[6] [7] [8] + // |<1> [2] [3] [4] [5] + public void testLayout8KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L0); + assertEquals("8 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 L0 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("8 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("8 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("8 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("8 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("8 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("8 key auto 5 L0 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 L0 [7]", 1, params.getColumnPos(6)); + assertEquals("8 key auto 5 L0 [8]", 2, params.getColumnPos(7)); + assertEquals("8 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] [8] + // |___ <1> [2] [3] [4] [5] + public void testLayout8KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L1); + assertEquals("8 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 L1 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("8 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("8 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("8 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("8 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("8 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("8 key auto 5 L1 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 L1 [7]", 1, params.getColumnPos(6)); + assertEquals("8 key auto 5 L1 [8]", 2, params.getColumnPos(7)); + assertEquals("8 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [8] [6] [7] + // |___ [3] <1> [2] [4] [5] + public void testLayout8KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_L2); + assertEquals("8 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 L2 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("8 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("8 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("8 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("8 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("8 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("8 key auto 5 L2 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 L2 [7]", 1, params.getColumnPos(6)); + assertEquals("8 key auto 5 L2 [8]", -1, params.getColumnPos(7)); + assertEquals("8 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [8] [7] [6]| + // [5] [4] [3] [2] <1>| + public void testLayout8KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R0); + assertEquals("8 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 R0 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("8 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("8 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("8 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("8 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("8 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("8 key auto 5 R0 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 R0 [7]", -1, params.getColumnPos(6)); + assertEquals("8 key auto 5 R0 [8]", -2, params.getColumnPos(7)); + assertEquals("8 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [8] [7] [6] ___| + // [5] [4] [3] [2] <1> ___| + public void testLayout8KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R1); + assertEquals("8 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 R1 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("8 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("8 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("8 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("8 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("8 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("8 key auto 5 R1 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 R1 [7]", -1, params.getColumnPos(6)); + assertEquals("8 key auto 5 R1 [8]", -2, params.getColumnPos(7)); + assertEquals("8 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [8] [6] [7] ___| + // [5] [4] [3] <1> [2] ___| + public void testLayout8KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(8, 5, XPOS_R2); + assertEquals("8 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("8 key auto 5 R2 rows", 2, params.mNumRows); + assertEquals("8 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("8 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("8 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("8 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("8 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("8 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("8 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("8 key auto 5 R2 [6]", 0, params.getColumnPos(5)); + assertEquals("8 key auto 5 R2 [7]", 1, params.getColumnPos(6)); + assertEquals("8 key auto 5 R2 [8]", -1, params.getColumnPos(7)); + assertEquals("8 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("8 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout9KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_M0); + assertEquals("9 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 M0 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("9 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("9 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("9 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("9 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("9 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("9 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("9 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("9 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("9 key auto 5 M0 adjust", -1, params.mTopRowAdjustment); + assertEquals("9 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |[6] [7] [8] [9] + // |<1> [2] [3] [4] [5] + public void testLayout9KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L0); + assertEquals("9 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 L0 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("9 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("9 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("9 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("9 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("9 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("9 key auto 5 L0 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 L0 [7]", 1, params.getColumnPos(6)); + assertEquals("9 key auto 5 L0 [8]", 2, params.getColumnPos(7)); + assertEquals("9 key auto 5 L0 [9]", 3, params.getColumnPos(8)); + assertEquals("9 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("9 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] [8] [9] + // |___ <1> [2] [3] [4] [5] + public void testLayout9KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L1); + assertEquals("9 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 L1 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("9 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("9 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("9 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("9 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("9 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("9 key auto 5 L1 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 L1 [7]", 1, params.getColumnPos(6)); + assertEquals("9 key auto 5 L1 [8]", 2, params.getColumnPos(7)); + assertEquals("9 key auto 5 L1 [9]", 3, params.getColumnPos(8)); + assertEquals("9 key auto 5 L1 adjust",0, params.mTopRowAdjustment); + assertEquals("9 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] [8] [9] + // |___ [3] <1> [2] [4] [5] + public void testLayout9KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_L2); + assertEquals("9 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 L2 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("9 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("9 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("9 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("9 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("9 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("9 key auto 5 L2 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 L2 [7]", 1, params.getColumnPos(6)); + assertEquals("9 key auto 5 L2 [8]", 2, params.getColumnPos(7)); + assertEquals("9 key auto 5 L2 [9]", 3, params.getColumnPos(8)); + assertEquals("9 key auto 5 L2 adjust", -1, params.mTopRowAdjustment); + assertEquals("9 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [9] [8] [7] [6]| + // [5] [4] [3] [2] <1>| + public void testLayout9KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R0); + assertEquals("9 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 R0 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("9 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("9 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("9 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("9 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("9 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("9 key auto 5 R0 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 R0 [7]", -1, params.getColumnPos(6)); + assertEquals("9 key auto 5 R0 [8]", -2, params.getColumnPos(7)); + assertEquals("9 key auto 5 R0 [9]", -3, params.getColumnPos(8)); + assertEquals("9 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("9 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [9] [8] [7] [6] ___| + // [5] [4] [3] [2] <1> ___| + public void testLayout9KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R1); + assertEquals("9 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 R1 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("9 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("9 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("9 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("9 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("9 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("9 key auto 5 R1 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 R1 [7]", -1, params.getColumnPos(6)); + assertEquals("9 key auto 5 R1 [8]", -2, params.getColumnPos(7)); + assertEquals("9 key auto 5 R1 [9]", -3, params.getColumnPos(8)); + assertEquals("9 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("9 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [9] [8] [6] [7] ___| + // [5] [4] [3] <1> [2] ___| + public void testLayout9KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(9, 5, XPOS_R2); + assertEquals("9 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("9 key auto 5 R2 rows", 2, params.mNumRows); + assertEquals("9 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("9 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("9 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("9 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("9 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("9 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("9 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("9 key auto 5 R2 [6]", 0, params.getColumnPos(5)); + assertEquals("9 key auto 5 R2 [7]", 1, params.getColumnPos(6)); + assertEquals("9 key auto 5 R2 [8]", -1, params.getColumnPos(7)); + assertEquals("9 key auto 5 R2 [9]", -2, params.getColumnPos(8)); + assertEquals("9 key auto 5 R2 adjust", -1, params.mTopRowAdjustment); + assertEquals("9 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [A] [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout10KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_M0); + assertEquals("10 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 M0 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("10 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("10 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("10 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("10 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("10 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("10 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("10 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("10 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("10 key auto 5 M0 [A]", -2, params.getColumnPos(9)); + assertEquals("10 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // |[6] [7] [8] [9] [A] + // |<1> [2] [3] [4] [5] + public void testLayout10KeyAuto5L0() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L0); + assertEquals("10 key auto 5 L0 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 L0 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 L0 left", 0, params.mLeftKeys); + assertEquals("10 key auto 5 L0 right", 5, params.mRightKeys); + assertEquals("10 key auto 5 L0 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 L0 [2]", 1, params.getColumnPos(1)); + assertEquals("10 key auto 5 L0 [3]", 2, params.getColumnPos(2)); + assertEquals("10 key auto 5 L0 [4]", 3, params.getColumnPos(3)); + assertEquals("10 key auto 5 L0 [5]", 4, params.getColumnPos(4)); + assertEquals("10 key auto 5 L0 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 L0 [7]", 1, params.getColumnPos(6)); + assertEquals("10 key auto 5 L0 [8]", 2, params.getColumnPos(7)); + assertEquals("10 key auto 5 L0 [9]", 3, params.getColumnPos(8)); + assertEquals("10 key auto 5 L0 [A]", 4, params.getColumnPos(9)); + assertEquals("10 key auto 5 L0 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 L0 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [6] [7] [8] [9] [A] + // |___ <1> [2] [3] [4] [5] + public void testLayout10KeyAuto5L1() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L1); + assertEquals("10 key auto 5 L1 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 L1 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 L1 left", 0, params.mLeftKeys); + assertEquals("10 key auto 5 L1 right", 5, params.mRightKeys); + assertEquals("10 key auto 5 L1 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 L1 [2]", 1, params.getColumnPos(1)); + assertEquals("10 key auto 5 L1 [3]", 2, params.getColumnPos(2)); + assertEquals("10 key auto 5 L1 [4]", 3, params.getColumnPos(3)); + assertEquals("10 key auto 5 L1 [5]", 4, params.getColumnPos(4)); + assertEquals("10 key auto 5 L1 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 L1 [7]", 1, params.getColumnPos(6)); + assertEquals("10 key auto 5 L1 [8]", 2, params.getColumnPos(7)); + assertEquals("10 key auto 5 L1 [9]", 3, params.getColumnPos(8)); + assertEquals("10 key auto 5 L1 [A]", 4, params.getColumnPos(9)); + assertEquals("10 key auto 5 L1 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 L1 default", WIDTH * 0, params.getDefaultKeyCoordX()); + } + + // |___ [8] [6] [7] [9] [A] + // |___ [3] <1> [2] [4] [5] + public void testLayout10KeyAuto5L2() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_L2); + assertEquals("10 key auto 5 L2 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 L2 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 L2 left", 1, params.mLeftKeys); + assertEquals("10 key auto 5 L2 right", 4, params.mRightKeys); + assertEquals("10 key auto 5 L2 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 L2 [2]", 1, params.getColumnPos(1)); + assertEquals("10 key auto 5 L2 [3]", -1, params.getColumnPos(2)); + assertEquals("10 key auto 5 L2 [4]", 2, params.getColumnPos(3)); + assertEquals("10 key auto 5 L2 [5]", 3, params.getColumnPos(4)); + assertEquals("10 key auto 5 L2 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 L2 [7]", 1, params.getColumnPos(6)); + assertEquals("10 key auto 5 L2 [8]", -1, params.getColumnPos(7)); + assertEquals("10 key auto 5 L2 [9]", 2, params.getColumnPos(8)); + assertEquals("10 key auto 5 L2 [A]", 3, params.getColumnPos(9)); + assertEquals("10 key auto 5 L2 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 L2 default", WIDTH * 1, params.getDefaultKeyCoordX()); + } + + // [A] [9] [8] [7] [6]| + // [5] [4] [3] [2] <1>| + public void testLayout10KeyAuto5R0() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R0); + assertEquals("10 key auto 5 R0 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 R0 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 R0 left", 4, params.mLeftKeys); + assertEquals("10 key auto 5 R0 right", 1, params.mRightKeys); + assertEquals("10 key auto 5 R0 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 R0 [2]", -1, params.getColumnPos(1)); + assertEquals("10 key auto 5 R0 [3]", -2, params.getColumnPos(2)); + assertEquals("10 key auto 5 R0 [4]", -3, params.getColumnPos(3)); + assertEquals("10 key auto 5 R0 [5]", -4, params.getColumnPos(4)); + assertEquals("10 key auto 5 R0 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 R0 [7]", -1, params.getColumnPos(6)); + assertEquals("10 key auto 5 R0 [8]", -2, params.getColumnPos(7)); + assertEquals("10 key auto 5 R0 [9]", -3, params.getColumnPos(8)); + assertEquals("10 key auto 5 R0 [A]", -4, params.getColumnPos(9)); + assertEquals("10 key auto 5 R0 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 R0 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [A] [9] [8] [7] [6] ___| + // [5] [4] [3] [2] <1> ___| + public void testLayout10KeyAuto5R1() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R1); + assertEquals("10 key auto 5 R1 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 R1 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 R1 left", 4, params.mLeftKeys); + assertEquals("10 key auto 5 R1 right", 1, params.mRightKeys); + assertEquals("10 key auto 5 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("10 key auto 5 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("10 key auto 5 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("10 key auto 5 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("10 key auto 5 R1 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 R1 [7]", -1, params.getColumnPos(6)); + assertEquals("10 key auto 5 R1 [8]", -2, params.getColumnPos(7)); + assertEquals("10 key auto 5 R1 [9]", -3, params.getColumnPos(8)); + assertEquals("10 key auto 5 R1 [A]", -4, params.getColumnPos(9)); + assertEquals("10 key auto 5 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 R1 default", WIDTH * 4, params.getDefaultKeyCoordX()); + } + + // [A] [9] [8] [6] [7] ___| + // [5] [4] [3] <1> [2] ___| + public void testLayout10KeyAuto5R2() { + MoreKeysKeyboardParams params = createParams(10, 5, XPOS_R2); + assertEquals("10 key auto 5 R2 columns", 5, params.mNumColumns); + assertEquals("10 key auto 5 R2 rows", 2, params.mNumRows); + assertEquals("10 key auto 5 R2 left", 3, params.mLeftKeys); + assertEquals("10 key auto 5 R2 right", 2, params.mRightKeys); + assertEquals("10 key auto 5 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("10 key auto 5 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("10 key auto 5 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("10 key auto 5 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("10 key auto 5 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("10 key auto 5 R2 [6]", 0, params.getColumnPos(5)); + assertEquals("10 key auto 5 R2 [7]", 1, params.getColumnPos(6)); + assertEquals("10 key auto 5 R2 [8]", -1, params.getColumnPos(7)); + assertEquals("10 key auto 5 R2 [9]", -2, params.getColumnPos(8)); + assertEquals("10 key auto 5 R2 [A]", -3, params.getColumnPos(9)); + assertEquals("10 key auto 5 R2 adjust", 0, params.mTopRowAdjustment); + assertEquals("10 key auto 5 R2 default", WIDTH * 3, params.getDefaultKeyCoordX()); + } + + // [B] + // [A] [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout11KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(11, 5, XPOS_M0); + assertEquals("11 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("11 key auto 5 M0 rows", 3, params.mNumRows); + assertEquals("11 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("11 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("11 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("11 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("11 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("11 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("11 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("11 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("11 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("11 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("11 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("11 key auto 5 M0 [A]", -2, params.getColumnPos(9)); + assertEquals("11 key auto 5 M0 [B]", 0, params.getColumnPos(10)); + assertEquals("11 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("11 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [B] [C] + // [A] [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout12KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(12, 5, XPOS_M0); + assertEquals("12 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("12 key auto 5 M0 rows", 3, params.mNumRows); + assertEquals("12 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("12 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("12 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("12 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("12 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("12 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("12 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("12 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("12 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("12 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("12 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("12 key auto 5 M0 [A]", -2, params.getColumnPos(9)); + assertEquals("12 key auto 5 M0 [B]", 0, params.getColumnPos(10)); + assertEquals("12 key auto 5 M0 [C]", 1, params.getColumnPos(11)); + assertEquals("12 key auto 5 M0 adjust", -1, params.mTopRowAdjustment); + assertEquals("12 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [D] [B] [C] + // [A] [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout13KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(13, 5, XPOS_M0); + assertEquals("13 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("13 key auto 5 M0 rows", 3, params.mNumRows); + assertEquals("13 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("13 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("13 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("13 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("13 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("13 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("13 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("13 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("13 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("13 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("13 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("13 key auto 5 M0 [A]", -2, params.getColumnPos(9)); + assertEquals("13 key auto 5 M0 [B]", 0, params.getColumnPos(10)); + assertEquals("13 key auto 5 M0 [C]", 1, params.getColumnPos(11)); + assertEquals("13 key auto 5 M0 [D]", -1, params.getColumnPos(12)); + assertEquals("13 key auto 5 M0 adjust", 0, params.mTopRowAdjustment); + assertEquals("13 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [D] [B] [C] [E] + // [A] [8] [6] [7] [9] + // [5] [3] <1> [2] [4] + public void testLayout14KeyAuto5M0() { + MoreKeysKeyboardParams params = createParams(14, 5, XPOS_M0); + assertEquals("13 key auto 5 M0 columns", 5, params.mNumColumns); + assertEquals("13 key auto 5 M0 rows", 3, params.mNumRows); + assertEquals("13 key auto 5 M0 left", 2, params.mLeftKeys); + assertEquals("13 key auto 5 M0 right", 3, params.mRightKeys); + assertEquals("13 key auto 5 M0 <1>", 0, params.getColumnPos(0)); + assertEquals("13 key auto 5 M0 [2]", 1, params.getColumnPos(1)); + assertEquals("13 key auto 5 M0 [3]", -1, params.getColumnPos(2)); + assertEquals("13 key auto 5 M0 [4]", 2, params.getColumnPos(3)); + assertEquals("13 key auto 5 M0 [5]", -2, params.getColumnPos(4)); + assertEquals("13 key auto 5 M0 [6]", 0, params.getColumnPos(5)); + assertEquals("13 key auto 5 M0 [7]", 1, params.getColumnPos(6)); + assertEquals("13 key auto 5 M0 [8]", -1, params.getColumnPos(7)); + assertEquals("13 key auto 5 M0 [9]", 2, params.getColumnPos(8)); + assertEquals("13 key auto 5 M0 [A]", -2, params.getColumnPos(9)); + assertEquals("13 key auto 5 M0 [B]", 0, params.getColumnPos(10)); + assertEquals("13 key auto 5 M0 [C]", 1, params.getColumnPos(11)); + assertEquals("13 key auto 5 M0 [D]", -1, params.getColumnPos(12)); + assertEquals("13 key auto 5 M0 [E]", 2, params.getColumnPos(13)); + assertEquals("13 key auto 5 M0 adjust", -1, params.mTopRowAdjustment); + assertEquals("13 key auto 5 M0 default", WIDTH * 2, params.getDefaultKeyCoordX()); + } + + // [J] [I] [H] ___| + // [G] [F] [E] [D] [C] [B] [A] [9] ___| + // [8] [7] [6] [5] [4] [3] [2] <1> ___| + public void testLayout19KeyAuto8R1() { + MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R1); + assertEquals("19 key auto 8 R1 columns", 8, params.mNumColumns); + assertEquals("19 key auto 8 R1 rows", 3, params.mNumRows); + assertEquals("19 key auto 8 R1 left", 7, params.mLeftKeys); + assertEquals("19 key auto 8 R1 right", 1, params.mRightKeys); + assertEquals("19 key auto 8 R1 <1>", 0, params.getColumnPos(0)); + assertEquals("19 key auto 8 R1 [2]", -1, params.getColumnPos(1)); + assertEquals("19 key auto 8 R1 [3]", -2, params.getColumnPos(2)); + assertEquals("19 key auto 8 R1 [4]", -3, params.getColumnPos(3)); + assertEquals("19 key auto 8 R1 [5]", -4, params.getColumnPos(4)); + assertEquals("19 key auto 8 R1 [6]", -5, params.getColumnPos(5)); + assertEquals("19 key auto 8 R1 [7]", -6, params.getColumnPos(6)); + assertEquals("19 key auto 8 R1 [8]", -7, params.getColumnPos(7)); + assertEquals("19 key auto 8 R1 [9]", 0, params.getColumnPos(8)); + assertEquals("19 key auto 8 R1 [A]", -1, params.getColumnPos(9)); + assertEquals("19 key auto 8 R1 [B]", -2, params.getColumnPos(10)); + assertEquals("19 key auto 8 R1 [C]", -3, params.getColumnPos(11)); + assertEquals("19 key auto 8 R1 [D]", -4, params.getColumnPos(12)); + assertEquals("19 key auto 8 R1 [E]", -5, params.getColumnPos(13)); + assertEquals("19 key auto 8 R1 [F]", -6, params.getColumnPos(14)); + assertEquals("19 key auto 8 R1 [G]", -7, params.getColumnPos(15)); + assertEquals("19 key auto 8 R1 [H]", 0, params.getColumnPos(16)); + assertEquals("19 key auto 8 R1 [I]", -1, params.getColumnPos(17)); + assertEquals("19 key auto 8 R1 [J]", -2, params.getColumnPos(18)); + assertEquals("19 key auto 8 R1 adjust", 0, params.mTopRowAdjustment); + assertEquals("19 key auto 8 R1 default", WIDTH * 7, params.getDefaultKeyCoordX()); + } + + // [J] [H] [I] ___| + // [G] [F] [E] [D] [C] [B] [9] [A] ___| + // [8] [7] [6] [5] [4] [3] <1> [2] ___| + public void testLayout19KeyAuto8R2() { + MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R2); + assertEquals("19 key auto 8 R2 columns", 8, params.mNumColumns); + assertEquals("19 key auto 8 R2 rows", 3, params.mNumRows); + assertEquals("19 key auto 8 R2 left", 6, params.mLeftKeys); + assertEquals("19 key auto 8 R2 right", 2, params.mRightKeys); + assertEquals("19 key auto 8 R2 <1>", 0, params.getColumnPos(0)); + assertEquals("19 key auto 8 R2 [2]", 1, params.getColumnPos(1)); + assertEquals("19 key auto 8 R2 [3]", -1, params.getColumnPos(2)); + assertEquals("19 key auto 8 R2 [4]", -2, params.getColumnPos(3)); + assertEquals("19 key auto 8 R2 [5]", -3, params.getColumnPos(4)); + assertEquals("19 key auto 8 R2 [6]", -4, params.getColumnPos(5)); + assertEquals("19 key auto 8 R2 [7]", -5, params.getColumnPos(6)); + assertEquals("19 key auto 8 R2 [8]", -6, params.getColumnPos(7)); + assertEquals("19 key auto 8 R2 [9]", 0, params.getColumnPos(8)); + assertEquals("19 key auto 8 R2 [A]", 1, params.getColumnPos(9)); + assertEquals("19 key auto 8 R2 [B]", -1, params.getColumnPos(10)); + assertEquals("19 key auto 8 R2 [C]", -2, params.getColumnPos(11)); + assertEquals("19 key auto 8 R2 [D]", -3, params.getColumnPos(12)); + assertEquals("19 key auto 8 R2 [E]", -4, params.getColumnPos(13)); + assertEquals("19 key auto 8 R2 [F]", -5, params.getColumnPos(14)); + assertEquals("19 key auto 8 R2 [G]", -6, params.getColumnPos(15)); + assertEquals("19 key auto 8 R2 [H]", 0, params.getColumnPos(16)); + assertEquals("19 key auto 8 R2 [I]", 1, params.getColumnPos(17)); + assertEquals("19 key auto 8 R2 [J]", -1, params.getColumnPos(18)); + assertEquals("19 key auto 8 R2 adjust", -1, params.mTopRowAdjustment); + assertEquals("19 key auto 8 R2 default", WIDTH * 6, params.getDefaultKeyCoordX()); + } + + // [J] [H] [I] ___| + // [G] [F] [E] [D] [B] [9] [A] [C] ___| + // [8] [7] [6] [5] [3] <1> [2] [4] ___| + public void testLayout19KeyAuto8R3() { + MoreKeysKeyboardParams params = createParams(19, 8, XPOS_R3); + assertEquals("19 key auto 8 R3 columns", 8, params.mNumColumns); + assertEquals("19 key auto 8 R3 rows", 3, params.mNumRows); + assertEquals("19 key auto 8 R3 left", 5, params.mLeftKeys); + assertEquals("19 key auto 8 R3 right", 3, params.mRightKeys); + assertEquals("19 key auto 8 R3 <1>", 0, params.getColumnPos(0)); + assertEquals("19 key auto 8 R3 [2]", 1, params.getColumnPos(1)); + assertEquals("19 key auto 8 R3 [3]", -1, params.getColumnPos(2)); + assertEquals("19 key auto 8 R3 [4]", 2, params.getColumnPos(3)); + assertEquals("19 key auto 8 R3 [5]", -2, params.getColumnPos(4)); + assertEquals("19 key auto 8 R3 [6]", -3, params.getColumnPos(5)); + assertEquals("19 key auto 8 R3 [7]", -4, params.getColumnPos(6)); + assertEquals("19 key auto 8 R3 [8]", -5, params.getColumnPos(7)); + assertEquals("19 key auto 8 R3 [9]", 0, params.getColumnPos(8)); + assertEquals("19 key auto 8 R3 [A]", 1, params.getColumnPos(9)); + assertEquals("19 key auto 8 R3 [B]", -1, params.getColumnPos(10)); + assertEquals("19 key auto 8 R3 [C]", 2, params.getColumnPos(11)); + assertEquals("19 key auto 8 R3 [D]", -2, params.getColumnPos(12)); + assertEquals("19 key auto 8 R3 [E]", -3, params.getColumnPos(13)); + assertEquals("19 key auto 8 R3 [F]", -4, params.getColumnPos(14)); + assertEquals("19 key auto 8 R3 [G]", -5, params.getColumnPos(15)); + assertEquals("19 key auto 8 R3 [H]", 0, params.getColumnPos(16)); + assertEquals("19 key auto 8 R3 [I]", 1, params.getColumnPos(17)); + assertEquals("19 key auto 8 R3 [J]", -1, params.getColumnPos(18)); + assertEquals("19 key auto 8 R3 adjust", -1, params.mTopRowAdjustment); + assertEquals("19 key auto 8 R3 default", WIDTH * 5, params.getDefaultKeyCoordX()); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java index 6d9c3fdbb..7e51d955d 100644 --- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java +++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderFixedOrderTests.java @@ -47,7 +47,8 @@ public class MoreKeysKeyboardBuilderFixedOrderTests extends AndroidTestCase { final int coordXInParent) { final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams(); params.setParameters(numKeys, columnNum, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH, - true /* isFixedOrderColumn */, 0 /* dividerWidth */); + true /* isMoreKeysFixedColumn */, true /* isMoreKeysFixedOrder */, + 0 /* dividerWidth */); return params; } diff --git a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderMaxOrderTests.java index b213721bd..806790eff 100644 --- a/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderTests.java +++ b/tests/src/com/android/inputmethod/keyboard/MoreKeysKeyboardBuilderMaxOrderTests.java @@ -22,7 +22,7 @@ import android.test.suitebuilder.annotation.MediumTest; import com.android.inputmethod.keyboard.MoreKeysKeyboard.MoreKeysKeyboardParams; @MediumTest -public class MoreKeysKeyboardBuilderTests extends AndroidTestCase { +public class MoreKeysKeyboardBuilderMaxOrderTests extends AndroidTestCase { private static final int WIDTH = 10; private static final int HEIGHT = 10; @@ -47,7 +47,8 @@ public class MoreKeysKeyboardBuilderTests extends AndroidTestCase { final int coordXInParent) { final MoreKeysKeyboardParams params = new MoreKeysKeyboardParams(); params.setParameters(numKeys, maxColumns, WIDTH, HEIGHT, coordXInParent, KEYBOARD_WIDTH, - false /* isFixedOrderColumn */, 0 /* dividerWidth */); + false /* isMoreKeysFixedColumn */, false /* isMoreKeysFixedOrder */, + 0 /* dividerWidth */); return params; } diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java index 29b169d80..922d2a8c8 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java @@ -53,7 +53,7 @@ public class MoreKeySpecSplitTests extends InstrumentationTestCase { private static String[] getAllResourceIdNames(final Class<?> resourceIdClass) { final ArrayList<String> names = new ArrayList<>(); for (final Field field : resourceIdClass.getFields()) { - if (field.getType() == Integer.TYPE) { + if (field.getType() == int.class) { names.add(field.getName()); } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java index fa818654e..3f85e4baa 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Arabic.java @@ -78,8 +78,7 @@ public final class Arabic extends LayoutBase { // U+061F: "؟" ARABIC QUESTION MARK // U+061B: "؛" ARABIC SEMICOLON return joinKeys(key("\u060C", joinMoreKeys( - ":", "!", "\u061F", "\u061B", "-", "\"", "'", SETTINGS_KEY)), - "_"); + ":", "!", "\u061F", "\u061B", "-", "\"", "'", SETTINGS_KEY))); } @Override @@ -90,7 +89,7 @@ public final class Arabic extends LayoutBase { // U+060C: "،" ARABIC COMMA // U+061F: "؟" ARABIC QUESTION MARK // U+061B: "؛" ARABIC SEMICOLON - return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone))); + return joinKeys(key(".", getPunctuationMoreKeys(isPhone))); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java index 42ce0c1ea..2cecedceb 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java @@ -39,6 +39,9 @@ public final class ArmenianPhonetic extends LayoutBase { public ArmenianPhoneticCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return ARMENIAN_ALPHABET_KEY; } @Override @@ -59,14 +62,14 @@ public final class ArmenianPhonetic extends LayoutBase { public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { // U+055D: "՝" ARMENIAN COMMA return isPhone ? joinKeys(key("\u055D", SETTINGS_KEY)) - : joinKeys(key("\u055D", SETTINGS_KEY), "_"); + : joinKeys(key("\u055D", SETTINGS_KEY)); } @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+0589: "։" ARMENIAN FULL STOP final ExpectedKey fullStopKey = key("\u0589", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(fullStopKey) : joinKeys("/", fullStopKey); + return joinKeys(fullStopKey); } @Override @@ -112,29 +115,6 @@ public final class ArmenianPhonetic extends LayoutBase { return builder.build(); } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0567: "է" ARMENIAN SMALL LETTER EH diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java index e75cfd0ff..ba94c8dee 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Dvorak.java @@ -20,13 +20,14 @@ import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyboardBuilder; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey; +import com.android.inputmethod.latin.settings.CustomInputStyleSettingsFragment; import java.util.Locale; /** - * The QWERTY alphabet keyboard. + * The Dvorak alphabet keyboard. */ -public final class Dvorak extends LayoutBase { +public class Dvorak extends LayoutBase { private static final String LAYOUT_NAME = "dvorak"; public Dvorak(final LayoutCustomizer customizer) { @@ -51,17 +52,19 @@ public final class Dvorak extends LayoutBase { @Override public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { - return isPhone ? joinKeys(key("q", SETTINGS_KEY)) : - joinKeys(SETTINGS_KEY, key("_", moreKey("-"))); + // U+00A1: "¡" INVERTED EXCLAMATION MARK + return isPhone ? joinKeys(key("q", SETTINGS_KEY)) + : joinKeys(key("!", joinMoreKeys("\u00A1", SETTINGS_KEY))); } @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { final ExpectedAdditionalMoreKey[] punctuationMoreKeys = convertToAdditionalMoreKeys(getPunctuationMoreKeys(isPhone)); + // U+00BF: "¿" INVERTED QUESTION MARK return isPhone ? joinKeys(key("z", punctuationMoreKeys)) - : joinKeys("/", key("?", moreKey("!"))); + : joinKeys(key("?", joinMoreKeys(punctuationMoreKeys, "\u00BF"))); } private static ExpectedAdditionalMoreKey[] convertToAdditionalMoreKeys( @@ -76,7 +79,33 @@ public final class Dvorak extends LayoutBase { } @Override - ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { return ALPHABET_COMMON; } + public ExpectedKey[][] getCommonAlphabetLayout(final boolean isPhone) { + return ALPHABET_COMMON; + } + + protected ExpectedKey getRow1_1Key(final boolean isPhone, final int elementId) { + if (elementId == KeyboardId.ELEMENT_ALPHABET + || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { + return key("'", joinMoreKeys(additionalMoreKey("1"), "!", "\"")); + } + return key("\"", additionalMoreKey("1")); + } + + protected ExpectedKey getRow1_2Key(final boolean isPhone, final int elementId) { + if (elementId == KeyboardId.ELEMENT_ALPHABET + || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { + return key(",", joinMoreKeys(additionalMoreKey("2"), "?", "<")); + } + return key("<", additionalMoreKey("2")); + } + + protected ExpectedKey getRow1_3Key(final boolean isPhone, final int elementId) { + if (elementId == KeyboardId.ELEMENT_ALPHABET + || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { + return key(".", joinMoreKeys(additionalMoreKey("3"), ">")); + } + return key(">", additionalMoreKey("3")); + } @Override public ExpectedKey[][] getLayout(final boolean isPhone, final int elementId) { @@ -86,18 +115,9 @@ public final class Dvorak extends LayoutBase { } final ExpectedKeyboardBuilder builder = new ExpectedKeyboardBuilder( getCommonAlphabetLayout(isPhone)); - if (elementId == KeyboardId.ELEMENT_ALPHABET - || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) { - builder.addKeysOnTheLeftOfRow(1, - key("'", joinMoreKeys(additionalMoreKey("1"), "!", "\"")), - key(",", joinMoreKeys(additionalMoreKey("2"), "?", "<")), - key(".", joinMoreKeys(additionalMoreKey("3"), ">"))); - } else { - builder.addKeysOnTheLeftOfRow(1, - key("\"", additionalMoreKey("1")), - key("<", additionalMoreKey("2")), - key(">", additionalMoreKey("3"))); - } + builder.replaceKeyOfLabel(ROW1_1, getRow1_1Key(isPhone, elementId)) + .replaceKeyOfLabel(ROW1_2, getRow1_2Key(isPhone, elementId)) + .replaceKeyOfLabel(ROW1_3, getRow1_3Key(isPhone, elementId)); convertCommonLayoutToKeyboard(builder, isPhone); getCustomizer().setAccentedLetters(builder); if (elementId != KeyboardId.ELEMENT_ALPHABET) { @@ -107,8 +127,13 @@ public final class Dvorak extends LayoutBase { return builder.build(); } + public static final String ROW1_1 = "ROW1_1"; + public static final String ROW1_2 = "ROW1_2"; + public static final String ROW1_3 = "ROW1_3"; + private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, + ROW1_1, ROW1_2, ROW1_3, key("p", additionalMoreKey("4")), key("y", additionalMoreKey("5")), key("f", additionalMoreKey("6")), diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java index a513740e7..7390457d0 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Farsi.java @@ -77,8 +77,7 @@ public final class Farsi extends LayoutBase { // U+061B: "؛" ARABIC SEMICOLON return joinKeys(key("\u060C", joinMoreKeys( ":", "!", "\u061F", "\u061B", "-", RtlSymbols.DOUBLE_ANGLE_QUOTES_LR_RTL, - SETTINGS_KEY)), - "_"); + SETTINGS_KEY))); } @Override @@ -86,7 +85,7 @@ public final class Farsi extends LayoutBase { if (isPhone) { return super.getKeysRightToSpacebar(isPhone); } - return joinKeys("/", key(".", getPunctuationMoreKeys(isPhone))); + return joinKeys(key(".", getPunctuationMoreKeys(isPhone))); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java index 2b625c32b..c2a15f3f3 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/HindiCompact.java @@ -50,7 +50,7 @@ public final class HindiCompact extends LayoutBase { public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+0964: "।" DEVANAGARI DANDA final ExpectedKey periodKey = key("\u0964", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); + return joinKeys(periodKey); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java index 5ce7f4d9c..8bee1f83b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Kannada.java @@ -171,11 +171,12 @@ public final class Kannada extends LayoutBase { // U+0C8E: "ಎ" KANNADA LETTER E key("\u0C82", moreKey("\u0C8E")), // U+0CAE: "ಮ" KANNADA LETTER MA - // U+0CA3: "ಣ" KANNADA LETTER NNA - key("\u0CAE", moreKey("\u0CA3")), + "\u0CAE", // U+0CA8: "ನ" KANNADA LETTER NA + // U+0CA3: "ಣ" KANNADA LETTER NNA + key("\u0CA8", moreKey("\u0CA3")), // U+0CB5: "ವ" KANNADA LETTER VA - "\u0CA8", "\u0CB5", + "\u0CB5", // U+0CB2: "ಲ" KANNADA LETTER LA // U+0CB3: "ಳ" KANNADA LETTER LLA key("\u0CB2", moreKey("\u0CB3")), diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java index 143ccf6eb..7e4f159ab 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java @@ -40,6 +40,9 @@ public final class Khmer extends LayoutBase { public KhmerCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return KHMER_ALPHABET_KEY; } @Override @@ -79,29 +82,6 @@ public final class Khmer extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+17E1: "១" KHMER DIGIT ONE diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java index e7be9982a..aaa1c8a8a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java @@ -40,6 +40,9 @@ public final class Lao extends LayoutBase { public LaoCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return LAO_ALPHABET_KEY; } @Override @@ -83,29 +86,6 @@ public final class Lao extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0EA2: "ຢ" LAO LETTER YO diff --git a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java index c5223720c..b05789b73 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java @@ -46,6 +46,10 @@ public abstract class LayoutBase extends AbstractLayoutBase { return mLocale; } + public int getNumberOfRows() { + return 4; + } + /** * Set accented letters to common layout. * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard @@ -145,6 +149,24 @@ public abstract class LayoutBase extends AbstractLayoutBase { } /** + * Get the enter key. + * @param isPhone true if requesting phone's key. + * @return the array of {@link ExpectedKey} that should be placed as an enter key. + */ + public ExpectedKey getEnterKey(final boolean isPhone) { + return isPhone ? key(ENTER_KEY, EMOJI_ACTION_KEY) : ENTER_KEY; + } + + /** + * Get the emoji key. + * @param isPhone true if requesting phone's key. + * @return the array of {@link ExpectedKey} that should be placed as an emoji key. + */ + public ExpectedKey getEmojiKey(final boolean isPhone) { + return EMOJI_NORMAL_KEY; + } + + /** * Get the space keys. * @param isPhone true if requesting phone's keys. * @return the array of {@link ExpectedKey} that should be placed at the center of the @@ -161,8 +183,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { */ public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { // U+002C: "," COMMA - return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY)) - : joinKeys(key("\u002C", SETTINGS_KEY), "_"); + return joinKeys(key("\u002C", SETTINGS_KEY)); } /** @@ -172,7 +193,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { */ public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { final ExpectedKey periodKey = key(".", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); + return joinKeys(periodKey); } /** @@ -237,7 +258,25 @@ public abstract class LayoutBase extends AbstractLayoutBase { */ public final LayoutCustomizer getCustomizer() { return mCustomizer; } - // Icon id. + // Icon ids. + private static final int ICON_DELETE = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_DELETE_KEY); + private static final int ICON_SPACE = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_SPACE_KEY); + private static final int ICON_TAB = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_TAB_KEY); + private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_SHORTCUT_KEY); + private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_SETTINGS_KEY); + private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY); + private static final int ICON_ENTER = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_ENTER_KEY); + private static final int ICON_EMOJI_ACTION = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_EMOJI_ACTION_KEY); + private static final int ICON_EMOJI_NORMAL = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_EMOJI_NORMAL_KEY); private static final int ICON_SHIFT = KeyboardIconsSet.getIconId( KeyboardIconsSet.NAME_SHIFT_KEY); private static final int ICON_SHIFTED_SHIFT = KeyboardIconsSet.getIconId( @@ -247,11 +286,21 @@ public abstract class LayoutBase extends AbstractLayoutBase { private static final int ICON_ZWJ = KeyboardIconsSet.getIconId( KeyboardIconsSet.NAME_ZWJ_KEY); - // Functional key. + // Functional keys. + public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE); + public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB); + public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT); + public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS); + public static final ExpectedKey LANGUAGE_SWITCH_KEY = key( + ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH); + public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER); + public static final ExpectedKey EMOJI_ACTION_KEY = key(ICON_EMOJI_ACTION, Constants.CODE_EMOJI); + public static final ExpectedKey EMOJI_NORMAL_KEY = key(ICON_EMOJI_NORMAL, Constants.CODE_EMOJI); + public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE); static final ExpectedKey CAPSLOCK_MORE_KEY = key(" ", Constants.CODE_CAPSLOCK); - static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT, + public static final ExpectedKey SHIFT_KEY = key(ICON_SHIFT, Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY); - static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT, + public static final ExpectedKey SHIFTED_SHIFT_KEY = key(ICON_SHIFTED_SHIFT, Constants.CODE_SHIFT, CAPSLOCK_MORE_KEY); static final ExpectedKey ALPHABET_KEY = key("ABC", Constants.CODE_SWITCH_ALPHA_SYMBOL); static final ExpectedKey SYMBOLS_KEY = key("?123", Constants.CODE_SWITCH_ALPHA_SYMBOL); @@ -267,6 +316,9 @@ public abstract class LayoutBase extends AbstractLayoutBase { // U+200D: ZERO WIDTH JOINER static final ExpectedKey ZWNJ_KEY = key(ICON_ZWNJ, "\u200C"); static final ExpectedKey ZWJ_KEY = key(ICON_ZWJ, "\u200D"); + // Domain key + public static final ExpectedKey DOMAIN_KEY = + key(".com", joinMoreKeys(".net", ".org", ".gov", ".edu")).preserveCase(); // Punctuation more keys for phone form factor. public static final ExpectedKey[] PHONE_PUNCTUATION_MORE_KEYS = joinKeys( @@ -277,7 +329,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { ",", "'", "#", ")", "(", "/", ";", "@", ":", "-", "\"", "+", "%", "&"); - /** + /** * Helper method to create alphabet layout adding special function keys. * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard * layout @@ -287,21 +339,26 @@ public abstract class LayoutBase extends AbstractLayoutBase { ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, final boolean isPhone) { final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(4, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(4, (Object[])customizer.getKeysRightToSpacebar(isPhone)); + final int numberOfRows = customizer.getNumberOfRows(); + builder.setKeysOfRow(numberOfRows, (Object[])customizer.getSpaceKeys(isPhone)); + builder.addKeysOnTheLeftOfRow( + numberOfRows, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); + builder.addKeysOnTheRightOfRow( + numberOfRows, (Object[])customizer.getKeysRightToSpacebar(isPhone)); if (isPhone) { - builder.addKeysOnTheRightOfRow(3, DELETE_KEY) - .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + builder.addKeysOnTheRightOfRow(numberOfRows - 1, DELETE_KEY) + .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey()) + .addKeysOnTheRightOfRow(numberOfRows, customizer.getEnterKey(isPhone)); } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(2, ENTER_KEY) - .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(numberOfRows - 2, customizer.getEnterKey(isPhone)) + .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey()) + .addKeysOnTheRightOfRow(numberOfRows, customizer.getEmojiKey(isPhone)); } - builder.addKeysOnTheLeftOfRow(3, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(3, (Object[])customizer.getRightShiftKeys(isPhone)); + builder.addKeysOnTheLeftOfRow( + numberOfRows - 1, (Object[])customizer.getLeftShiftKeys(isPhone)); + builder.addKeysOnTheRightOfRow( + numberOfRows - 1, (Object[])customizer.getRightShiftKeys(isPhone)); return builder; } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java index f2a2dfdd8..3c70d3266 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java @@ -40,6 +40,9 @@ public final class Myanmar extends LayoutBase { public MyanmarCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return MYANMAR_ALPHABET_KEY; } @Override @@ -52,14 +55,14 @@ public final class Myanmar extends LayoutBase { // U+002C: "," COMMA // U+104A: "၊" MYANMAR SIGN LITTLE SECTION return isPhone ? joinKeys(key("\u002C", SETTINGS_KEY)) - : joinKeys(key("\u104A", moreKey(","), SETTINGS_KEY), "_"); + : joinKeys(key("\u104A", moreKey(","), SETTINGS_KEY)); } @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { // U+104B: "။" MYANMAR SIGN SECTION final ExpectedKey periodKey = key("\u104B", getPunctuationMoreKeys(isPhone)); - return isPhone ? joinKeys(periodKey) : joinKeys("/", periodKey); + return joinKeys(periodKey); } @Override @@ -97,29 +100,6 @@ public final class Myanmar extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+1041: "၁" MYANMAR DIGIT ONE diff --git a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java index 9da6dcc44..3f7340fd0 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java @@ -39,6 +39,9 @@ public final class PcQwerty extends LayoutBase { public PcQwertyCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return joinKeys(SHIFT_KEY); } @@ -55,7 +58,9 @@ public final class PcQwerty extends LayoutBase { @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { - return isPhone ? joinKeys(key(ENTER_KEY, EMOJI_KEY)) : joinKeys(EMOJI_KEY); + return isPhone + ? joinKeys(key(ENTER_KEY, EMOJI_ACTION_KEY)) + : joinKeys(EMOJI_NORMAL_KEY); } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java index 5c0ffb4f9..354141d5a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Sinhala.java @@ -43,6 +43,14 @@ public final class Sinhala extends LayoutBase { public ExpectedKey getAlphabetKey() { return SINHALA_ALPHABET_KEY; } @Override + public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } + + @Override + public ExpectedKey[] getOtherCurrencyKeys() { + return SymbolsShifted.CURRENCIES_OTHER_GENERIC; + } + + @Override public ExpectedKey[] getRightShiftKeys(final boolean isPhone) { return isPhone ? EMPTY_KEYS : EXCLAMATION_AND_QUESTION_MARKS; } @@ -51,6 +59,10 @@ public final class Sinhala extends LayoutBase { // U+0D86: "ආ" SINHALA LETTER AAYANNA private static final ExpectedKey SINHALA_ALPHABET_KEY = key( "\u0D85,\u0D86", Constants.CODE_SWITCH_ALPHA_SYMBOL); + + // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA + private static final ExpectedKey CURRENCY_RUPEE = key("\u0DBB\u0DD4", + Symbols.CURRENCY_GENERIC_MORE_KEYS); } @Override diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java index 5f3e4b196..803089721 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java @@ -41,18 +41,18 @@ public class Symbols extends AbstractLayoutBase { customizer.getSingleQuoteMoreKeys(), customizer.getSingleAngleQuoteKeys()))); if (isPhone) { builder.addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone)) - .addKeysOnTheRightOfRow(3, DELETE_KEY) + .addKeysOnTheRightOfRow(3, LayoutBase.DELETE_KEY) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + .addKeysOnTheRightOfRow(4, customizer.getEnterKey(isPhone)); } else { // Tablet symbols keyboard has extra two keys at the left edge of the 3rd row. builder.addKeysOnTheLeftOfRow(3, (Object[])joinKeys("\\", "=")); - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(2, ENTER_KEY) + builder.addKeysOnTheRightOfRow(1, LayoutBase.DELETE_KEY) + .addKeysOnTheRightOfRow(2, customizer.getEnterKey(isPhone)) .addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone)) .addKeysOnTheRightOfRow(3, customizer.getSymbolsShiftKey(isPhone)) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(4, customizer.getEmojiKey(isPhone)); } return builder.build(); } @@ -167,7 +167,7 @@ public class Symbols extends AbstractLayoutBase { // U+00BF: "¿" INVERTED QUESTION MARK key("?", moreKey("\u00BF"))) .setKeysOfRow(4, - key(","), key("_"), SPACE_KEY, key("/"), + key(","), key("_"), LayoutBase.SPACE_KEY, key("/"), // U+2026: "…" HORIZONTAL ELLIPSIS key(".", moreKey("\u2026"))) .build(); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java index 3265e10e1..19cb6075a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java @@ -37,20 +37,20 @@ public class SymbolsShifted extends AbstractLayoutBase { builder.replaceKeyOfLabel(OTHER_CURRENCIES, (Object[])customizer.getOtherCurrencyKeys()); if (isPhone) { builder.addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey()) - .addKeysOnTheRightOfRow(3, DELETE_KEY) + .addKeysOnTheRightOfRow(3, LayoutBase.DELETE_KEY) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + .addKeysOnTheRightOfRow(4, customizer.getEnterKey(isPhone)); } else { // Tablet symbols shifted keyboard has extra two keys at the right edge of the 3rd row. // U+00BF: "¿" INVERTED QUESTION MARK // U+00A1: "¡" INVERTED EXCLAMATION MARK builder.addKeysOnTheRightOfRow(3, (Object[])joinKeys("\u00A1", "\u00BF")); - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(2, ENTER_KEY) + builder.addKeysOnTheRightOfRow(1, LayoutBase.DELETE_KEY) + .addKeysOnTheRightOfRow(2, customizer.getEnterKey(isPhone)) .addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey()) .addKeysOnTheRightOfRow(3, customizer.getBackToSymbolsKey()) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(4, customizer.getEmojiKey(isPhone)); } return builder.build(); } @@ -122,7 +122,7 @@ public class SymbolsShifted extends AbstractLayoutBase { // U+2264: "≤" LESS-THAN OR EQUAL TO // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK key("<", joinMoreKeys("\u2039", "\u2264", "\u00AB")), - SPACE_KEY, + LayoutBase.SPACE_KEY, // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK // U+2265: "≥" GREATER-THAN EQUAL TO // U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java index 70385c7d0..597b6fa55 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Tamil.java @@ -42,14 +42,6 @@ public final class Tamil extends LayoutBase { public ExpectedKey getAlphabetKey() { return TAMIL_ALPHABET_KEY; } @Override - public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } - - @Override - public ExpectedKey[] getOtherCurrencyKeys() { - return SymbolsShifted.CURRENCIES_OTHER_GENERIC; - } - - @Override public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return EMPTY_KEYS; } @@ -64,10 +56,6 @@ public final class Tamil extends LayoutBase { // U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA private static final ExpectedKey TAMIL_ALPHABET_KEY = key( "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD", Constants.CODE_SWITCH_ALPHA_SYMBOL); - - // U+0BF9: "௹" TAMIL RUPEE SIGN - private static final ExpectedKey CURRENCY_RUPEE = key("\u0BF9", - Symbols.CURRENCY_GENERIC_MORE_KEYS); } @Override @@ -81,15 +69,20 @@ public final class Tamil extends LayoutBase { private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0B94: "ஔ" TAMIL LETTER AU - key("\u0B94", moreKey("1")), + // U+0BCC: "ௌ" TAMIL VOWEL SIGN AU + key("\u0B94", joinMoreKeys("\u0BCC", "1")), // U+0B90: "ஐ" TAMIL LETTER AI - key("\u0B90", moreKey("2")), + // U+0BC8: "ை" TAMIL VOWEL SIGN AI + key("\u0B90", joinMoreKeys("\u0BC8", "2")), // U+0B86: "ஆ" TAMIL LETTER AA - key("\u0B86", moreKey("3")), + // U+0BBE: "ா" TAMIL VOWEL SIGN AA + key("\u0B86", joinMoreKeys("\u0BBE", "3")), // U+0B88: "ஈ" TAMIL LETTER II - key("\u0B88", moreKey("4")), + // U+0BC0: "ீ" TAMIL VOWEL SIGN II + key("\u0B88", joinMoreKeys("\u0BC0", "4")), // U+0B8A: "ஊ" TAMIL LETTER UU - key("\u0B8A", moreKey("5")), + // U+0BC2: "ூ" TAMIL VOWEL SIGN UU + key("\u0B8A", joinMoreKeys("\u0BC2","5")), // U+0BAE: "ம" TAMIL LETTER MA key("\u0BAE", moreKey("6")), // U+0BA9: "ன" TAMIL LETTER NNNA @@ -104,18 +97,24 @@ public final class Tamil extends LayoutBase { "\u0B9E") .setKeysOfRow(2, // U+0B93: "ஓ" TAMIL LETTER OO + // U+0BCB: "ோ" TAMIL VOWEL SIGN OO // U+0BD0: "ௐ" TAMIL OM - key("\u0B93", moreKey("\u0BD0")), + key("\u0B93", joinMoreKeys("\u0BCB", "\u0BD0")), // U+0B8F: "ஏ" TAMIL LETTER EE - "\u0B8F", + // U+0BC7: "ே" TAMIL VOWEL SIGN EE + key("\u0B8F", moreKey("\u0BC7")), // U+0B85: "அ" TAMIL LETTER A // U+0B83: "ஃ" TAMIL SIGN VISARGA key("\u0B85", moreKey("\u0B83")), // U+0B87: "இ" TAMIL LETTER I + // U+0BBF: "ி" TAMIL VOWEL SIGN I + key("\u0B87", moreKey("\u0BBF")), // U+0B89: "உ" TAMIL LETTER U + // U+0BC1: "ு" TAMIL VOWEL SIGN U + key("\u0B89", moreKey("\u0BC1")), // U+0BB1: "ற" TAMIL LETTER RRA // U+0BAA: "ப" TAMIL LETTER PA - "\u0B87", "\u0B89", "\u0BB1", "\u0BAA", + "\u0BB1", "\u0BAA", // U+0B95: "க" TAMIL LETTER KA // U+0BB9: "ஹ" TAMIL LETTER HA // U+0B95/U+0BCD/U+0BB7: @@ -133,7 +132,11 @@ public final class Tamil extends LayoutBase { "\u0B9F") .setKeysOfRow(3, // U+0B92: "ஒ" TAMIL LETTER O + // U+0BCA: "ொ" TAMIL VOWEL SIGN O + key("\u0B92", moreKey("\u0BCA")), // U+0B8E: "எ" TAMIL LETTER E + // U+0BC6: "ெ" TAMIL VOWEL SIGN E + key("\u0B8E", moreKey("\u0BC6")), // U+0BCD: "்" TAMIL SIGN VIRAMA // U+0BB0: "ர" TAMIL LETTER RA // U+0BB5: "வ" TAMIL LETTER VA @@ -141,8 +144,7 @@ public final class Tamil extends LayoutBase { // U+0BB2: "ல" TAMIL LETTER LA // U+0BB3: "ள" TAMIL LETTER LLA // U+0BAF: "ய" TAMIL LETTER YA - "\u0B92", "\u0B8E", "\u0BCD", "\u0BB0", "\u0BB5", "\u0BB4", "\u0BB2", "\u0BB3", - "\u0BAF", + "\u0BCD", "\u0BB0", "\u0BB5", "\u0BB4", "\u0BB2", "\u0BB3", "\u0BAF", // U+0BB7: "ஷ" TAMIL LETTER SSA // U+0B9C: "ஜ" TAMIL LETTER JA key("\u0BB7", moreKey("\u0B9C"))) diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java index af4abea93..cfda2947c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java @@ -40,6 +40,9 @@ public final class Thai extends LayoutBase { public ThaiCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return THAI_ALPHABET_KEY; } @Override @@ -96,29 +99,6 @@ public final class Thai extends LayoutBase { return builder.build(); } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java index 9e0039d84..3556cb4bf 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java @@ -16,9 +16,7 @@ package com.android.inputmethod.keyboard.layout.expected; -import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.layout.expected.ExpectedKey.ExpectedAdditionalMoreKey; -import com.android.inputmethod.latin.Constants; /** * Base class to create an expected keyboard for unit test. @@ -104,33 +102,4 @@ public abstract class AbstractLayoutBase { public static ExpectedKey[] joinKeys(final Object ... keys) { return ExpectedKeyboardBuilder.joinKeys(keys); } - - // Icon ids. - private static final int ICON_DELETE = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_DELETE_KEY); - private static final int ICON_SPACE = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_SPACE_KEY); - private static final int ICON_TAB = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_TAB_KEY); - private static final int ICON_SHORTCUT = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_SHORTCUT_KEY); - private static final int ICON_SETTINGS = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_SETTINGS_KEY); - private static final int ICON_LANGUAGE_SWITCH = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY); - private static final int ICON_ENTER = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_ENTER_KEY); - private static final int ICON_EMOJI = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_EMOJI_KEY); - - // Functional keys. - public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE); - public static final ExpectedKey TAB_KEY = key(ICON_TAB, Constants.CODE_TAB); - public static final ExpectedKey SHORTCUT_KEY = key(ICON_SHORTCUT, Constants.CODE_SHORTCUT); - public static final ExpectedKey SETTINGS_KEY = key(ICON_SETTINGS, Constants.CODE_SETTINGS); - public static final ExpectedKey LANGUAGE_SWITCH_KEY = key( - ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH); - public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER); - public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI); - public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java index 0e1c71cd1..2674a6a69 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKey.java @@ -148,6 +148,18 @@ public class ExpectedKey { return newInstance(mVisual.toUpperCase(locale), mOutput.toUpperCase(locale)); } + public ExpectedKey preserveCase() { + final ExpectedKey[] moreKeys = getMoreKeys(); + final ExpectedKey[] casePreservedMoreKeys = new ExpectedKey[moreKeys.length]; + for (int index = 0; index < moreKeys.length; index++) { + final ExpectedKey moreKey = moreKeys[index]; + casePreservedMoreKeys[index] = newInstance( + moreKey.getVisual().preserveCase(), moreKey.getOutput().preserveCase()); + } + return newInstance( + getVisual().preserveCase(), getOutput().preserveCase(), casePreservedMoreKeys); + } + public boolean equalsTo(final Key key) { // This key has no "more keys". return mVisual.equalsTo(key) && mOutput.equalsTo(key) && key.getMoreKeys() == null; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java index 1be51e60b..8b2bb4289 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyOutput.java @@ -42,6 +42,7 @@ abstract class ExpectedKeyOutput { } abstract ExpectedKeyOutput toUpperCase(final Locale locale); + abstract ExpectedKeyOutput preserveCase(); abstract boolean equalsTo(final String text); abstract boolean equalsTo(final Key key); abstract boolean equalsTo(final MoreKeySpec moreKeySpec); @@ -62,13 +63,19 @@ abstract class ExpectedKeyOutput { 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)); + return newInstance(StringUtils.toUpperCaseOfStringForLocale( + codeString, true /* needsToUpperCase */, locale)); } // A special negative value has no upper case. return this; } @Override + ExpectedKeyOutput preserveCase() { + return new CasePreservedCode(mCode); + } + + @Override boolean equalsTo(final String text) { return StringUtils.codePointCount(text) == 1 && text.codePointAt(0) == mCode; } @@ -93,6 +100,16 @@ abstract class ExpectedKeyOutput { return Constants.isLetterCode(mCode) ? StringUtils.newSingleCodePointString(mCode) : Constants.printableCode(mCode); } + + private static class CasePreservedCode extends Code { + CasePreservedCode(final int code) { super(code); } + + @Override + ExpectedKeyOutput toUpperCase(final Locale locale) { return this; } + + @Override + ExpectedKeyOutput preserveCase() { return this; } + } } /** @@ -109,6 +126,11 @@ abstract class ExpectedKeyOutput { } @Override + ExpectedKeyOutput preserveCase() { + return new CasePreservedText(mText); + } + + @Override boolean equalsTo(final String text) { return text.equals(text); } @@ -134,5 +156,15 @@ abstract class ExpectedKeyOutput { public String toString() { return mText; } + + private static class CasePreservedText extends Text { + CasePreservedText(final String text) { super(text); } + + @Override + ExpectedKeyOutput toUpperCase(final Locale locale) { return this; } + + @Override + ExpectedKeyOutput preserveCase() { return this; } + } } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java index 0a0da32b6..34024a5e0 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/ExpectedKeyVisual.java @@ -19,6 +19,7 @@ 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.utils.StringUtils; import java.util.Locale; @@ -37,6 +38,7 @@ abstract class ExpectedKeyVisual { } abstract ExpectedKeyVisual toUpperCase(final Locale locale); + abstract ExpectedKeyVisual preserveCase(); abstract boolean equalsTo(final String text); abstract boolean equalsTo(final Key key); abstract boolean equalsTo(final MoreKeySpec moreKeySpec); @@ -59,6 +61,11 @@ abstract class ExpectedKeyVisual { } @Override + ExpectedKeyVisual preserveCase() { + return this; + } + + @Override boolean equalsTo(final String text) { return false; } @@ -99,7 +106,13 @@ abstract class ExpectedKeyVisual { @Override ExpectedKeyVisual toUpperCase(final Locale locale) { - return new Label(mLabel.toUpperCase(locale)); + return new Label(StringUtils.toUpperCaseOfStringForLocale( + mLabel, true /* needsToUpperCase */, locale)); + } + + @Override + ExpectedKeyVisual preserveCase() { + return new CasePreservedLabel(mLabel); } @Override @@ -131,5 +144,15 @@ abstract class ExpectedKeyVisual { public String toString() { return mLabel; } + + private static class CasePreservedLabel extends Label { + CasePreservedLabel(final String label) { super(label); } + + @Override + ExpectedKeyVisual toUpperCase(final Locale locale) { return this; } + + @Override + ExpectedKeyVisual preserveCase() { return this; } + } } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java new file mode 100644 index 000000000..37ca09238 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakEmail.java @@ -0,0 +1,91 @@ +/* + * 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 android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.layout.Dvorak; +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.tests.TestsEnglishDvorak.EnglishDvorakCustomizer; + +import java.util.Locale; + +/** + * en_US: English (United States)/dvorak, email input field. + */ +@SmallTest +public class TestsDvorakEmail extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("en", "US"); + private static final LayoutBase LAYOUT = new DvorakEmail(new DvorakEmailCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + @Override + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, + final boolean languageSwitchKeyEnabled) { + final EditorInfo emailField = new EditorInfo(); + emailField.inputType = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; + return super.createKeyboardLayoutSet( + subtype, emailField, voiceInputKeyEnabled, languageSwitchKeyEnabled); + } + + private static class DvorakEmailCustomizer extends EnglishDvorakCustomizer { + DvorakEmailCustomizer(final Locale locale) { + super(locale); + } + + @Override + public ExpectedKey getEnterKey(final boolean isPhone) { + return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone); + } + + @Override + public ExpectedKey getEmojiKey(final boolean isPhone) { + return LayoutBase.DOMAIN_KEY; + } + + @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + return isPhone ? super.getKeysLeftToSpacebar(isPhone) + : joinKeys(key("@", LayoutBase.SETTINGS_KEY)); + } + } + + private static class DvorakEmail extends Dvorak { + public DvorakEmail(final LayoutCustomizer customizer) { + super(customizer); + } + + @Override + protected ExpectedKey getRow1_1Key(final boolean isPhone, final int elementId) { + if (isPhone && (elementId == KeyboardId.ELEMENT_ALPHABET + || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)) { + return key("@", joinMoreKeys(additionalMoreKey("1"))); + } + return super.getRow1_1Key(isPhone, elementId); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java new file mode 100644 index 000000000..3bcae0ce4 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsDvorakUrl.java @@ -0,0 +1,91 @@ +/* + * 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 android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.layout.Dvorak; +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; +import com.android.inputmethod.keyboard.layout.tests.TestsEnglishDvorak.EnglishDvorakCustomizer; + +import java.util.Locale; + +/** + * en_US: English (United States)/dvorak, URL input field. + */ +@SmallTest +public class TestsDvorakUrl extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("en", "US"); + private static final LayoutBase LAYOUT = new DvorakEmail(new DvorakUrlCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + @Override + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, + final boolean languageSwitchKeyEnabled) { + final EditorInfo emailField = new EditorInfo(); + emailField.inputType = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI; + return super.createKeyboardLayoutSet( + subtype, emailField, voiceInputKeyEnabled, languageSwitchKeyEnabled); + } + + private static class DvorakUrlCustomizer extends EnglishDvorakCustomizer { + DvorakUrlCustomizer(final Locale locale) { + super(locale); + } + + @Override + public ExpectedKey getEnterKey(final boolean isPhone) { + return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone); + } + + @Override + public ExpectedKey getEmojiKey(final boolean isPhone) { + return LayoutBase.DOMAIN_KEY; + } + + @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + return isPhone ? super.getKeysLeftToSpacebar(isPhone) + : joinKeys(key("/", LayoutBase.SETTINGS_KEY)); + } + } + + private static class DvorakEmail extends Dvorak { + public DvorakEmail(final LayoutCustomizer customizer) { + super(customizer); + } + + @Override + protected ExpectedKey getRow1_1Key(final boolean isPhone, final int elementId) { + if (isPhone && (elementId == KeyboardId.ELEMENT_ALPHABET + || elementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)) { + return key("/", joinMoreKeys(additionalMoreKey("1"))); + } + return super.getRow1_1Key(isPhone, elementId); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java index a05269312..e647f8aea 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsEnglishDvorak.java @@ -36,7 +36,7 @@ public class TestsEnglishDvorak extends LayoutTestsBase { @Override LayoutBase getLayout() { return LAYOUT; } - private static class EnglishDvorakCustomizer extends DvorakCustomizer { + public static class EnglishDvorakCustomizer extends DvorakCustomizer { private final EnglishCustomizer mEnglishCustomizer; EnglishDvorakCustomizer(final Locale locale) { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java index f937de89a..b494ad37b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java @@ -25,7 +25,7 @@ import com.android.inputmethod.keyboard.layout.Malayalam.MalayalamCustomizer; import java.util.Locale; /** - * ta_IN: Malayalam (India)/malayalam + * ml_IN: Malayalam (India)/malayalam */ @SmallTest public final class TestsMalayalamIN extends LayoutTestsBase { diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java index e6d3b3b92..a0bd50c9a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMyanmarMM.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Myanmar; @@ -27,7 +27,7 @@ import java.util.Locale; /** * my_MM: Myanmar (Myanmar)/myanmar */ -@SmallTest +@Suppress public final class TestsMyanmarMM extends LayoutTestsBase { private static final Locale LOCALE = new Locale("my", "MM"); private static final LayoutBase LAYOUT = new Myanmar(new MyanmarCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java new file mode 100644 index 000000000..8563d6933 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyEmail.java @@ -0,0 +1,73 @@ +/* + * 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 android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Qwerty; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; + +import java.util.Locale; + +/** + * en_US: English (United States)/qwerty, email input field. + */ +@SmallTest +public class TestsQwertyEmail extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("en", "US"); + private static final LayoutBase LAYOUT = new Qwerty(new EnglishEmailCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + @Override + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, + final boolean languageSwitchKeyEnabled) { + final EditorInfo emailField = new EditorInfo(); + emailField.inputType = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; + return super.createKeyboardLayoutSet( + subtype, emailField, voiceInputKeyEnabled, languageSwitchKeyEnabled); + } + + private static class EnglishEmailCustomizer extends EnglishCustomizer { + EnglishEmailCustomizer(final Locale locale) { + super(locale); + } + + @Override + public ExpectedKey getEnterKey(final boolean isPhone) { + return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone); + } + + @Override + public ExpectedKey getEmojiKey(final boolean isPhone) { + return LayoutBase.DOMAIN_KEY; + } + + @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + return joinKeys(key("@", LayoutBase.SETTINGS_KEY)); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java new file mode 100644 index 000000000..1c1a2bbbd --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsQwertyUrl.java @@ -0,0 +1,73 @@ +/* + * 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 android.text.InputType; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.inputmethod.keyboard.KeyboardLayoutSet; +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Qwerty; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; + +import java.util.Locale; + +/** + * en_US: English (United States)/qwerty, URL input field. + */ +@SmallTest +public class TestsQwertyUrl extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("en", "US"); + private static final LayoutBase LAYOUT = new Qwerty(new EnglishUrlCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + @Override + protected KeyboardLayoutSet createKeyboardLayoutSet(final InputMethodSubtype subtype, + final EditorInfo editorInfo, final boolean voiceInputKeyEnabled, + final boolean languageSwitchKeyEnabled) { + final EditorInfo emailField = new EditorInfo(); + emailField.inputType = + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI; + return super.createKeyboardLayoutSet( + subtype, emailField, voiceInputKeyEnabled, languageSwitchKeyEnabled); + } + + private static class EnglishUrlCustomizer extends EnglishCustomizer { + EnglishUrlCustomizer(final Locale locale) { + super(locale); + } + + @Override + public ExpectedKey getEnterKey(final boolean isPhone) { + return isPhone ? LayoutBase.ENTER_KEY : super.getEnterKey(isPhone); + } + + @Override + public ExpectedKey getEmojiKey(final boolean isPhone) { + return LayoutBase.DOMAIN_KEY; + } + + @Override + public ExpectedKey[] getKeysLeftToSpacebar(final boolean isPhone) { + return joinKeys(key("/", LayoutBase.SETTINGS_KEY)); + } + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java index 1cea49760..8b861359b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSinhalaLK.java @@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.layout.tests; -import android.test.suitebuilder.annotation.SmallTest; +import android.test.suitebuilder.annotation.Suppress; import com.android.inputmethod.keyboard.layout.LayoutBase; import com.android.inputmethod.keyboard.layout.Sinhala; @@ -27,7 +27,7 @@ import java.util.Locale; /** * si_LK: Sinhala (Sri Lanka)/sinhala */ -@SmallTest +@Suppress public final class TestsSinhalaLK extends LayoutTestsBase { private static final Locale LOCALE = new Locale("si", "LK"); private static final LayoutBase LAYOUT = new Sinhala(new SinhalaCustomizer(LOCALE)); diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java index 5b3649d2b..31df53c0b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilIN.java @@ -19,8 +19,11 @@ package com.android.inputmethod.keyboard.layout.tests; import android.test.suitebuilder.annotation.SmallTest; import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Symbols; +import com.android.inputmethod.keyboard.layout.SymbolsShifted; import com.android.inputmethod.keyboard.layout.Tamil; import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; import java.util.Locale; @@ -30,8 +33,24 @@ import java.util.Locale; @SmallTest public final class TestsTamilIN extends LayoutTestsBase { private static final Locale LOCALE = new Locale("ta", "IN"); - private static final LayoutBase LAYOUT = new Tamil(new TamilCustomizer(LOCALE)); + private static final LayoutBase LAYOUT = new Tamil(new TamilINCustomizer(LOCALE)); @Override LayoutBase getLayout() { return LAYOUT; } + + private static class TamilINCustomizer extends TamilCustomizer { + public TamilINCustomizer(final Locale locale) { super(locale); } + + @Override + public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } + + @Override + public ExpectedKey[] getOtherCurrencyKeys() { + return SymbolsShifted.CURRENCIES_OTHER_GENERIC; + } + + // U+20B9: "₹" INDIAN RUPEE SIGN + private static final ExpectedKey CURRENCY_RUPEE = key("\u20B9", + Symbols.CURRENCY_GENERIC_MORE_KEYS); + } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java new file mode 100644 index 000000000..65ec0b036 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilLK.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.Suppress; + +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Symbols; +import com.android.inputmethod.keyboard.layout.SymbolsShifted; +import com.android.inputmethod.keyboard.layout.Tamil; +import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer; +import com.android.inputmethod.keyboard.layout.expected.ExpectedKey; + +import java.util.Locale; + +/** + * ta_LK: Tamil (Sri Lanka)/tamil + */ +@Suppress +public final class TestsTamilLK extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("ta", "LK"); + private static final LayoutBase LAYOUT = new Tamil(new TamilLKCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } + + private static class TamilLKCustomizer extends TamilCustomizer { + public TamilLKCustomizer(final Locale locale) { super(locale); } + + @Override + public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; } + + @Override + public ExpectedKey[] getOtherCurrencyKeys() { + return SymbolsShifted.CURRENCIES_OTHER_GENERIC; + } + + // U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA + private static final ExpectedKey CURRENCY_RUPEE = key("\u0DBB\u0DD4", + Symbols.CURRENCY_GENERIC_MORE_KEYS); + } +} diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java new file mode 100644 index 000000000..ade7abaf9 --- /dev/null +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsTamilSG.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.keyboard.layout.tests; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.keyboard.layout.LayoutBase; +import com.android.inputmethod.keyboard.layout.Tamil; +import com.android.inputmethod.keyboard.layout.Tamil.TamilCustomizer; + +import java.util.Locale; + +/** + * ta_SG: Tamil (Singapore)/tamil + */ +@SmallTest +public final class TestsTamilSG extends LayoutTestsBase { + private static final Locale LOCALE = new Locale("ta", "SG"); + private static final LayoutBase LAYOUT = new Tamil(new TamilCustomizer(LOCALE)); + + @Override + LayoutBase getLayout() { return LAYOUT; } +} diff --git a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java index 70b8f530a..e6fb28260 100644 --- a/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java +++ b/tests/src/com/android/inputmethod/latin/DistracterFilterTest.java @@ -16,24 +16,38 @@ package com.android.inputmethod.latin; +import java.util.ArrayList; import java.util.Locale; +import android.content.Context; +import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.LargeTest; +import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches; +import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatchesAndSuggestions; /** * Unit test for DistracterFilter */ @LargeTest -public class DistracterFilterTest extends InputTestsBase { - private DistracterFilterCheckingExactMatches mDistracterFilter; +public class DistracterFilterTest extends AndroidTestCase { + private DistracterFilterCheckingExactMatchesAndSuggestions mDistracterFilter; @Override protected void setUp() throws Exception { super.setUp(); - mDistracterFilter = new DistracterFilterCheckingExactMatches(getContext()); - mDistracterFilter.updateEnabledSubtypes(mLatinIME.getEnabledSubtypesForTest()); + final Context context = getContext(); + mDistracterFilter = new DistracterFilterCheckingExactMatchesAndSuggestions(context); + RichInputMethodManager.init(context); + final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); + final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); + subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.US.toString(), "qwerty")); + subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.FRENCH.toString(), "azerty")); + subtypes.add(richImm.findSubtypeByLocaleAndKeyboardLayoutSet( + Locale.GERMAN.toString(), "qwertz")); + mDistracterFilter.updateEnabledSubtypes(subtypes); } public void testIsDistractorToWordsInDictionaries() { @@ -104,24 +118,56 @@ public class DistracterFilterTest extends InputTestsBase { assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - final Locale localeDeDe = new Locale("de", "DE"); + typedWord = "thabk"; + // For this test case, we consider "thabk" is a distracter to "thank" + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); - typedWord = "fuer"; - // For this test case, we consider "fuer" is a distracter to "für". + typedWord = "thanks"; + // For this test case, we consider "thanks" is not a distracter to any other word + // in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "thabks"; + // For this test case, we consider "thabks" is a distracter to "thanks" assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( - EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "think"; + // For this test case, we consider "think" is not a distracter to any other word + // in dictionaries. + assertFalse(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "thibk"; + // For this test case, we consider "thibk" is a distracter to "think" + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + typedWord = "tgis"; + // For this test case, we consider "tgis" is a distracter to "this" + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeEnUs)); + + final Locale localeDeDe = new Locale("de"); typedWord = "fUEr"; // For this test case, we consider "fUEr" is a distracter to "für". assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + typedWord = "fuer"; + // For this test case, we consider "fuer" is a distracter to "für". + assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( + EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); + typedWord = "fur"; // For this test case, we consider "fur" is a distracter to "für". assertTrue(mDistracterFilter.isDistracterToWordsInDictionaries( EMPTY_PREV_WORDS_INFO, typedWord, localeDeDe)); - final Locale localeFrFr = new Locale("fr", "FR"); + final Locale localeFrFr = new Locale("fr"); typedWord = "a"; // For this test case, we consider "a" is a distracter to "à". diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java index 61eae4e8b..ab69c8592 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsReorderingMyanmar.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin; import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.Suppress; import android.util.Pair; /* @@ -77,6 +78,8 @@ import android.util.Pair; */ @LargeTest +// These tests are inactive until the combining code for Myanmar Reordering is sorted out. +@Suppress @SuppressWarnings("rawtypes") public class InputLogicTestsReorderingMyanmar extends InputTestsBase { // The tests are formatted as follows. diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java index 199922491..f9d72269e 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java @@ -215,18 +215,23 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { "abc 'def", mSpacingAndPunctuations, 2), PrevWordsInfo.EMPTY_PREV_WORDS_INFO); } - /** - * Test logic in getting the word range at the cursor. - */ - private static final int[] SPACE = { Constants.CODE_SPACE }; - static final int[] TAB = { Constants.CODE_TAB }; - private static final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t"); - // A character that needs surrogate pair to represent its code point (U+2008A). - private static final String SUPPLEMENTARY_CHAR = "\uD840\uDC8A"; - private static final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお - private static final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και - public void testGetWordRangeAtCursor() { + /** + * Test logic in getting the word range at the cursor. + */ + final SpacingAndPunctuations SPACE = new SpacingAndPunctuations( + mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); + final SpacingAndPunctuations TAB = new SpacingAndPunctuations( + mSpacingAndPunctuations, new int[] { Constants.CODE_TAB }); + final int[] SPACE_TAB = StringUtils.toSortedCodePointArray(" \t"); + // A character that needs surrogate pair to represent its code point (U+2008A). + final String SUPPLEMENTARY_CHAR_STRING = "\uD840\uDC8A"; + final SpacingAndPunctuations SUPPLEMENTARY_CHAR = new SpacingAndPunctuations( + mSpacingAndPunctuations, StringUtils.toSortedCodePointArray( + SUPPLEMENTARY_CHAR_STRING)); + final String HIRAGANA_WORD = "\u3042\u3044\u3046\u3048\u304A"; // あいうえお + final String GREEK_WORD = "\u03BA\u03B1\u03B9"; // και + ExtractedText et = new ExtractedText(); final MockInputMethodService mockInputMethodService = new MockInputMethodService(); final RichInputConnection ic = new RichInputConnection(mockInputMethodService); @@ -249,10 +254,9 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { // splitting on supplementary character mockInputMethodService.setInputConnection( - new MockConnection("one word" + SUPPLEMENTARY_CHAR + "wo", "rd", et)); + new MockConnection("one word" + SUPPLEMENTARY_CHAR_STRING + "wo", "rd", et)); ic.beginBatchEdit(); - r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), - ScriptUtils.SCRIPT_LATIN); + r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN); ic.endBatchEdit(); assertTrue(TextUtils.equals("word", r.mWord)); @@ -260,8 +264,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { mockInputMethodService.setInputConnection( new MockConnection(HIRAGANA_WORD + "wo", "rd" + GREEK_WORD, et)); ic.beginBatchEdit(); - r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), - ScriptUtils.SCRIPT_LATIN); + r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_LATIN); ic.endBatchEdit(); assertTrue(TextUtils.equals("word", r.mWord)); @@ -269,8 +272,7 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { mockInputMethodService.setInputConnection( new MockConnection("text" + GREEK_WORD, "text", et)); ic.beginBatchEdit(); - r = ic.getWordRangeAtCursor(StringUtils.toSortedCodePointArray(SUPPLEMENTARY_CHAR), - ScriptUtils.SCRIPT_GREEK); + r = ic.getWordRangeAtCursor(SUPPLEMENTARY_CHAR, ScriptUtils.SCRIPT_GREEK); ic.endBatchEdit(); assertTrue(TextUtils.equals(GREEK_WORD, r.mWord)); } @@ -286,6 +288,8 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase { } private void helpTestGetSuggestionSpansAtWord(final int cursorPos) { + final SpacingAndPunctuations SPACE = new SpacingAndPunctuations( + mSpacingAndPunctuations, new int[] { Constants.CODE_SPACE }); final MockInputMethodService mockInputMethodService = new MockInputMethodService(); final RichInputConnection ic = new RichInputConnection(mockInputMethodService); diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 66b4a9c71..869c550e0 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -23,24 +23,50 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.util.ArrayList; import java.util.Locale; -import java.util.Random; @SmallTest public class SuggestedWordsTests extends AndroidTestCase { + + /** + * Helper method to create a dummy {@link SuggestedWordInfo} with specifying + * {@link SuggestedWordInfo#KIND_TYPED}. + * + * @param word the word to be used to create {@link SuggestedWordInfo}. + * @return a new instance of {@link SuggestedWordInfo}. + */ + private static SuggestedWordInfo createTypedWordInfo(final String word) { + // Use 100 as the frequency because the numerical value does not matter as + // long as it's > 1 and < INT_MAX. + return new SuggestedWordInfo(word, 100 /* score */, + SuggestedWordInfo.KIND_TYPED, + null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + 1 /* autoCommitFirstWordConfidence */); + } + + /** + * Helper method to create a dummy {@link SuggestedWordInfo} with specifying + * {@link SuggestedWordInfo#KIND_CORRECTION}. + * + * @param word the word to be used to create {@link SuggestedWordInfo}. + * @return a new instance of {@link SuggestedWordInfo}. + */ + private static SuggestedWordInfo createCorrectionWordInfo(final String word) { + return new SuggestedWordInfo(word, 1 /* score */, + SuggestedWordInfo.KIND_CORRECTION, + null /* sourceDict */, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + } + public void testGetSuggestedWordsExcludingTypedWord() { final String TYPED_WORD = "typed"; - final int TYPED_WORD_FREQ = 5; final int NUMBER_OF_ADDED_SUGGESTIONS = 5; + final int KIND_OF_SECOND_CORRECTION = SuggestedWordInfo.KIND_CORRECTION; final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); - list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ, - SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); + list.add(createTypedWordInfo(TYPED_WORD)); for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { - list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, - null /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); + list.add(createCorrectionWordInfo(Integer.toString(i))); } final SuggestedWords words = new SuggestedWords( @@ -48,35 +74,29 @@ public class SuggestedWordsTests extends AndroidTestCase { false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - false /* isPrediction*/); + SuggestedWords.INPUT_STYLE_NONE); assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size()); assertEquals("typed", words.getWord(0)); assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED)); assertEquals("0", words.getWord(1)); - assertTrue(words.getInfo(1).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); + assertTrue(words.getInfo(1).isKindOf(KIND_OF_SECOND_CORRECTION)); assertEquals("4", words.getWord(5)); - assertTrue(words.getInfo(5).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); + assertTrue(words.getInfo(5).isKindOf(KIND_OF_SECOND_CORRECTION)); - final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord(); + final SuggestedWords wordsWithoutTyped = + words.getSuggestedWordsExcludingTypedWordForRecorrection(); + // Make sure that the typed word has indeed been excluded, by testing the size of the + // suggested words, the string and the kind of the top suggestion, which should match + // the string and kind of what we inserted after the typed word. assertEquals(words.size() - 1, wordsWithoutTyped.size()); assertEquals("0", wordsWithoutTyped.getWord(0)); - assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); - } - - // Helper for testGetTransformedWordInfo - private SuggestedWordInfo createWordInfo(final String s) { - // Use 100 as the frequency because the numerical value does not matter as - // long as it's > 1 and < INT_MAX. - return new SuggestedWordInfo(s, 100, - SuggestedWordInfo.KIND_TYPED, null /* sourceDict */, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - new Random().nextInt(1000000) /* autoCommitFirstWordConfidence */); + assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(KIND_OF_SECOND_CORRECTION)); } // Helper for testGetTransformedWordInfo private SuggestedWordInfo transformWordInfo(final String info, final int trailingSingleQuotesCount) { - final SuggestedWordInfo suggestedWordInfo = createWordInfo(info); + final SuggestedWordInfo suggestedWordInfo = createTypedWordInfo(info); final SuggestedWordInfo returnedWordInfo = Suggest.getTransformedSuggestedWordInfo(suggestedWordInfo, Locale.ENGLISH, false /* isAllUpperCase */, false /* isFirstCharCapitalized */, @@ -100,4 +120,43 @@ public class SuggestedWordsTests extends AndroidTestCase { result = transformWordInfo("didn't", 3); assertEquals(result.mWord, "didn't''"); } + + public void testGetTypedWordInfoOrNull() { + final String TYPED_WORD = "typed"; + final int NUMBER_OF_ADDED_SUGGESTIONS = 5; + final ArrayList<SuggestedWordInfo> list = new ArrayList<>(); + list.add(createTypedWordInfo(TYPED_WORD)); + for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) { + list.add(createCorrectionWordInfo(Integer.toString(i))); + } + + // Make sure getTypedWordInfoOrNull() returns non-null object. + final SuggestedWords wordsWithTypedWord = new SuggestedWords( + list, null /* rawSuggestions */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_NONE); + final SuggestedWordInfo typedWord = wordsWithTypedWord.getTypedWordInfoOrNull(); + assertNotNull(typedWord); + assertEquals(TYPED_WORD, typedWord.mWord); + + // Make sure getTypedWordInfoOrNull() returns null. + final SuggestedWords wordsWithoutTypedWord = + wordsWithTypedWord.getSuggestedWordsExcludingTypedWordForRecorrection(); + assertNull(wordsWithoutTypedWord.getTypedWordInfoOrNull()); + + // Make sure getTypedWordInfoOrNull() returns null. + assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull()); + + final SuggestedWords emptySuggestedWords = new SuggestedWords( + new ArrayList<SuggestedWordInfo>(), null /* rawSuggestions */, + false /* typedWordValid */, + false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, + SuggestedWords.INPUT_STYLE_NONE); + assertNull(emptySuggestedWords.getTypedWordInfoOrNull()); + + assertNull(SuggestedWords.EMPTY.getTypedWordInfoOrNull()); + } } diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index 084371944..eabd8d722 100644 --- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -802,20 +802,13 @@ public class BinaryDictEncoderUtils { } MakedictLog.i("Statistics:\n" - + " total file size " + size + "\n" + + " Total file size " + size + "\n" + " " + ptNodeArrays.size() + " node arrays\n" + " " + ptNodes + " PtNodes (" + ((float)ptNodes / ptNodeArrays.size()) + " PtNodes per node)\n" - + " first terminal at " + firstTerminalAddress + "\n" - + " last terminal at " + lastTerminalAddress + "\n" + + " First terminal at " + firstTerminalAddress + "\n" + + " Last terminal at " + lastTerminalAddress + "\n" + " PtNode stats : max = " + maxNodes); - for (int i = 0; i < ptNodeCounts.length; ++i) { - MakedictLog.i(" " + i + " : " + ptNodeCounts[i]); - } - MakedictLog.i(" Character run stats : max = " + maxRuns); - for (int i = 0; i < runCounts.length; ++i) { - MakedictLog.i(" " + i + " : " + runCounts[i]); - } } /** diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java index 2cc22fae4..eb76032b1 100644 --- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java @@ -429,7 +429,7 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase { assertFalse("willAutoCorrect", suggestedWords.mWillAutoCorrect); assertTrue("isPunctuationSuggestions", suggestedWords.isPunctuationSuggestions()); assertFalse("isObsoleteSuggestions", suggestedWords.mIsObsoleteSuggestions); - assertFalse("isPrediction", suggestedWords.mIsPrediction); + assertFalse("isPrediction", suggestedWords.isPrediction()); assertEquals("size", punctuationLabels.length, suggestedWords.size()); for (int index = 0; index < suggestedWords.size(); index++) { assertEquals("punctuation label at " + index, diff --git a/tests/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelperTests.java b/tests/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelperTests.java new file mode 100644 index 000000000..f3273a2d1 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelperTests.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.suggestions; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.latin.SuggestedWords; + +@SmallTest +public class SuggestionStripLayoutHelperTests extends AndroidTestCase { + private static void confirmShowTypedWord(final String message, final int inputType) { + assertFalse(message, SuggestionStripLayoutHelper.shouldOmitTypedWord( + inputType, + false /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse(message, SuggestionStripLayoutHelper.shouldOmitTypedWord( + inputType, + true /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse(message, SuggestionStripLayoutHelper.shouldOmitTypedWord( + inputType, + false /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + assertFalse(message, SuggestionStripLayoutHelper.shouldOmitTypedWord( + inputType, + true /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + } + + public void testShouldShowTypedWord() { + confirmShowTypedWord("no input style", + SuggestedWords.INPUT_STYLE_NONE); + confirmShowTypedWord("application specifed", + SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED); + confirmShowTypedWord("recorrection", + SuggestedWords.INPUT_STYLE_RECORRECTION); + } + + public void testshouldOmitTypedWordWhileTyping() { + assertFalse("typing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TYPING, + false /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse("typing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TYPING, + true /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertTrue("typing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TYPING, + false /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + assertTrue("typing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TYPING, + true /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + } + + public void testshouldOmitTypedWordWhileGesturing() { + assertFalse("gesturing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_UPDATE_BATCH, + false /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse("gesturing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_UPDATE_BATCH, + true /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse("gesturing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_UPDATE_BATCH, + false /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + assertTrue("gesturing", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_UPDATE_BATCH, + true /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + } + + public void testshouldOmitTypedWordWhenGestured() { + assertFalse("gestured", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TAIL_BATCH, + false /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertFalse("gestured", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TAIL_BATCH, + true /* gestureFloatingPreviewTextEnabled */, + false /* shouldShowUiToAcceptTypedWord */)); + assertTrue("gestured", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TAIL_BATCH, + false /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + assertTrue("gestured", SuggestionStripLayoutHelper.shouldOmitTypedWord( + SuggestedWords.INPUT_STYLE_TAIL_BATCH, + true /* gestureFloatingPreviewTextEnabled */, + true /* shouldShowUiToAcceptTypedWord */)); + } + + // Note that this unit test assumes that the number of suggested words in the suggestion strip + // is 3. + private static final int POSITION_OMIT = -1; + private static final int POSITION_LEFT = 0; + private static final int POSITION_CENTER = 1; + private static final int POSITION_RIGHT = 2; + + public void testGetPositionInSuggestionStrip() { + assertEquals("1st word without auto correction", POSITION_CENTER, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_TYPED_WORD /* indexInSuggestedWords */, + false /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("2nd word without auto correction", POSITION_LEFT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_AUTO_CORRECTION /* indexInSuggestedWords */, + false /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("3rd word without auto correction", POSITION_RIGHT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 2 /* indexInSuggestedWords */, + false /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + + assertEquals("typed word with auto correction", POSITION_LEFT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_TYPED_WORD /* indexInSuggestedWords */, + true /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("2nd word with auto correction", POSITION_CENTER, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_AUTO_CORRECTION /* indexInSuggestedWords */, + true /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("3rd word with auto correction", POSITION_RIGHT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 2 /* indexInSuggestedWords */, + true /* willAutoCorrect */, + false /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + + assertEquals("1st word without auto correction", POSITION_OMIT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_TYPED_WORD /* indexInSuggestedWords */, + false /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("2nd word without auto correction", POSITION_CENTER, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_AUTO_CORRECTION /* indexInSuggestedWords */, + false /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("3rd word without auto correction", POSITION_LEFT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 2 /* indexInSuggestedWords */, + false /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("4th word without auto correction", POSITION_RIGHT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 3 /* indexInSuggestedWords */, + false /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + + assertEquals("typed word with auto correction", POSITION_OMIT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_TYPED_WORD /* indexInSuggestedWords */, + true /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("2nd word with auto correction", POSITION_CENTER, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + SuggestedWords.INDEX_OF_AUTO_CORRECTION /* indexInSuggestedWords */, + true /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("3rd word with auto correction", POSITION_LEFT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 2 /* indexInSuggestedWords */, + true /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + assertEquals("4th word with auto correction", POSITION_RIGHT, + SuggestionStripLayoutHelper.getPositionInSuggestionStrip( + 3 /* indexInSuggestedWords */, + true /* willAutoCorrect */, + true /* omitTypedWord */, + POSITION_CENTER /* centerPositionInStrip */, + POSITION_LEFT /* typedWordPositionWhenAutoCorrect */)); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java new file mode 100644 index 000000000..819d76328 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/ImportantNoticeUtilsTests.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE; +import static com.android.inputmethod.latin.utils.ImportantNoticeUtils.KEY_IMPORTANT_NOTICE_VERSION; + +import android.content.Context; +import android.content.SharedPreferences; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.TextUtils; + +import java.util.concurrent.TimeUnit; + +@SmallTest +public class ImportantNoticeUtilsTests extends AndroidTestCase { + // This should be aligned with R.integer.config_important_notice_version. + private static final int CURRENT_IMPORTANT_NOTICE_VERSION = 1; + + private ImportantNoticePreferences mImportantNoticePreferences; + + private static class ImportantNoticePreferences { + private final SharedPreferences mPref; + + private Integer mVersion; + private Long mLastTime; + + public ImportantNoticePreferences(final Context context) { + mPref = ImportantNoticeUtils.getImportantNoticePreferences(context); + } + + private Integer getInt(final String key) { + if (mPref.contains(key)) { + return mPref.getInt(key, 0); + } + return null; + } + + public Long getLong(final String key) { + if (mPref.contains(key)) { + return mPref.getLong(key, 0); + } + return null; + } + + private void putInt(final String key, final Integer value) { + if (value == null) { + removePreference(key); + } else { + mPref.edit().putInt(key, value).apply(); + } + } + + private void putLong(final String key, final Long value) { + if (value == null) { + removePreference(key); + } else { + mPref.edit().putLong(key, value).apply(); + } + } + + private void removePreference(final String key) { + mPref.edit().remove(key).apply(); + } + + public void save() { + mVersion = getInt(KEY_IMPORTANT_NOTICE_VERSION); + mLastTime = getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE); + } + + public void restore() { + putInt(KEY_IMPORTANT_NOTICE_VERSION, mVersion); + putLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE, mLastTime); + } + + public void clear() { + removePreference(KEY_IMPORTANT_NOTICE_VERSION); + removePreference(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE); + } + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + mImportantNoticePreferences = new ImportantNoticePreferences(getContext()); + mImportantNoticePreferences.save(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + mImportantNoticePreferences.restore(); + } + + public void testCurrentVersion() { + assertEquals("Current version", CURRENT_IMPORTANT_NOTICE_VERSION, + ImportantNoticeUtils.getCurrentImportantNoticeVersion(getContext())); + } + + public void testUpdateVersion() { + mImportantNoticePreferences.clear(); + + assertEquals("Current boolean before update", true, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version before update", 0, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version before update ", 1, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Current title before update", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents before update", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + + ImportantNoticeUtils.updateLastImportantNoticeVersion(getContext()); + + assertEquals("Current boolean after update", false, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version after update", 1, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version after update", 2, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Current title after update", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents after update", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + } + + private static void sleep(final long millseconds) { + try { Thread.sleep(millseconds); } catch (final Exception e) { /* ignore */ } + } + + public void testTimeout() { + final long lastTime = System.currentTimeMillis() + - ImportantNoticeUtils.TIMEOUT_OF_IMPORTANT_NOTICE + + TimeUnit.MILLISECONDS.toMillis(1000); + mImportantNoticePreferences.clear(); + assertEquals("Before set last time", null, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + assertEquals("Set last time", false, + ImportantNoticeUtils.hasTimeoutPassed(getContext(), lastTime)); + assertEquals("After set last time", (Long)lastTime, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + + // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout. + assertEquals("Current boolean before timeout 1", true, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version before timeout 1", 0, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version before timeout 1", 1, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Last time before timeout 1", (Long)lastTime, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + assertEquals("Current title before timeout 1", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents before timeout 1", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + + sleep(TimeUnit.MILLISECONDS.toMillis(600)); + + // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} before timeout + // again. + assertEquals("Current boolean before timeout 2", true, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version before timeout 2", 0, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version before timeout 2", 1, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Last time before timeout 2", (Long)lastTime, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + assertEquals("Current title before timeout 2", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents before timeout 2", false, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + + sleep(TimeUnit.MILLISECONDS.toMillis(600)); + + // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout. + assertEquals("Current boolean after timeout 1", false, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version after timeout 1", 1, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version after timeout 1", 2, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Last time aflter timeout 1", null, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + assertEquals("Current title after timeout 1", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents after timeout 1", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + + sleep(TimeUnit.MILLISECONDS.toMillis(600)); + + // Call {@link ImportantNoticeUtils#shouldShowImportantNotice(Context)} after timeout again. + assertEquals("Current boolean after timeout 2", false, + ImportantNoticeUtils.shouldShowImportantNotice(getContext())); + assertEquals("Last version after timeout 2", 1, + ImportantNoticeUtils.getLastImportantNoticeVersion(getContext())); + assertEquals("Next version after timeout 2", 2, + ImportantNoticeUtils.getNextImportantNoticeVersion(getContext())); + assertEquals("Last time aflter timeout 2", null, + mImportantNoticePreferences.getLong(KEY_TIMESTAMP_OF_FIRST_IMPORTANT_NOTICE)); + assertEquals("Current title after timeout 2", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeTitle(getContext()))); + assertEquals("Current contents after timeout 2", true, TextUtils.isEmpty( + ImportantNoticeUtils.getNextImportantNoticeContents(getContext()))); + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java new file mode 100644 index 000000000..ba2e99802 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.utils.StringUtils; + +import java.util.Locale; + +@SmallTest +public class StringUtilsTests extends AndroidTestCase { + private static final Locale US = Locale.US; + private static final Locale GERMAN = Locale.GERMAN; + private static final Locale TURKEY = new Locale("tr", "TR"); + private static final Locale GREECE = new Locale("el", "GR"); + + private static void assert_toUpperCaseOfStringForLocale(final Locale locale, + final String lowerCase, final String expected) { + assertEquals(lowerCase + " in " + locale, expected, + StringUtils.toUpperCaseOfStringForLocale( + lowerCase, true /* needsToUpperCase */, locale)); + } + + public void test_toUpperCaseOfStringForLocale() { + assert_toUpperCaseOfStringForLocale(US, null, null); + assert_toUpperCaseOfStringForLocale(US, "", ""); + assert_toUpperCaseOfStringForLocale(US, "aeiou", "AEIOU"); + // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE + // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE + // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX + // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS + // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON + // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE + // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA + // U+00C0: "À" LATIN CAPITAL LETTER A WITH GRAVE + // U+00C8: "È" LATIN CAPITAL LETTER E WITH GRAVE + // U+00CE: "Î" LATIN CAPITAL LETTER I WITH CIRCUMFLEX + // U+00D6: "Ö" LATIN CAPITAL LETTER O WITH DIAERESIS + // U+016A: "Ū" LATIN CAPITAL LETTER U WITH MACRON + // U+00D1: "Ñ" LATIN CAPITAL LETTER N WITH TILDE + // U+00C7: "Ç" LATIN CAPITAL LETTER C WITH CEDILLA + assert_toUpperCaseOfStringForLocale(US, + "\u00E0\u00E8\u00EE\u00F6\u016B\u00F1\u00E7", + "\u00C0\u00C8\u00CE\u00D6\u016A\u00D1\u00C7"); + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE + // U+0161: "š" LATIN SMALL LETTER S WITH CARON + // U+015A: "Ś" LATIN CAPITAL LETTER S WITH ACUTE + // U+0160: "Š" LATIN CAPITAL LETTER S WITH CARONZ + assert_toUpperCaseOfStringForLocale(GERMAN, + "\u00DF\u015B\u0161", + "SS\u015A\u0160"); + // U+0259: "ə" LATIN SMALL LETTER SCHWA + // U+0069: "i" LATIN SMALL LETTER I + // U+0131: "ı" LATIN SMALL LETTER DOTLESS I + // U+018F: "Ə" LATIN SMALL LETTER SCHWA + // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE + // U+0049: "I" LATIN SMALL LETTER I + assert_toUpperCaseOfStringForLocale(TURKEY, + "\u0259\u0069\u0131", + "\u018F\u0130\u0049"); + // U+03C3: "σ" GREEK SMALL LETTER SIGMA + // U+03C2: "ς" GREEK SMALL LETTER FINAL SIGMA + // U+03A3: "Σ" GREEK CAPITAL LETTER SIGMA + assert_toUpperCaseOfStringForLocale(GREECE, + "\u03C3\u03C2", + "\u03A3\u03A3"); + // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS + // U+03AD: "έ" GREEK SMALL LETTER EPSILON WITH TONOS + // U+03AE: "ή" GREEK SMALL LETTER ETA WITH TONOS + // U+03AF: "ί" GREEK SMALL LETTER IOTA WITH TONOS + // U+03CC: "ό" GREEK SMALL LETTER OMICRON WITH TONOS + // U+03CD: "ύ" GREEK SMALL LETTER UPSILON WITH TONOS + // U+03CE: "ώ" GREEK SMALL LETTER OMEGA WITH TONOS + // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS + // U+0388: "Έ" GREEK CAPITAL LETTER EPSILON WITH TONOS + // U+0389: "Ή" GREEK CAPITAL LETTER ETA WITH TONOS + // U+038A: "Ί" GREEK CAPITAL LETTER IOTA WITH TONOS + // U+038C: "Ό" GREEK CAPITAL LETTER OMICRON WITH TONOS + // U+038E: "Ύ" GREEK CAPITAL LETTER UPSILON WITH TONOS + // U+038F: "Ώ" GREEK CAPITAL LETTER OMEGA WITH TONOS + assert_toUpperCaseOfStringForLocale(GREECE, + "\u03AC\u03AD\u03AE\u03AF\u03CC\u03CD\u03CE", + "\u0386\u0388\u0389\u038A\u038C\u038E\u038F"); + // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA + // U+03CB: "ϋ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA + // U+0390: "ΐ" GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + // U+03AB: "Ϋ" GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + // U+0399: "Ι" GREEK CAPITAL LETTER IOTA + // U+03A5: "Υ" GREEK CAPITAL LETTER UPSILON + // U+0308: COMBINING DIAERESIS + // U+0301: COMBINING GRAVE ACCENT + assert_toUpperCaseOfStringForLocale(GREECE, + "\u03CA\u03CB\u0390\u03B0", + "\u03AA\u03AB\u0399\u0308\u0301\u03A5\u0308\u0301"); + } + + private static void assert_toUpperCaseOfCodeForLocale(final Locale locale, final int lowerCase, + final int expected) { + assertEquals(lowerCase + " in " + locale, expected, + StringUtils.toUpperCaseOfCodeForLocale( + lowerCase, true /* needsToUpperCase */, locale)); + } + + public void test_toUpperCaseOfCodeForLocale() { + assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_ENTER, Constants.CODE_ENTER); + assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_SPACE, Constants.CODE_SPACE); + assert_toUpperCaseOfCodeForLocale(US, Constants.CODE_COMMA, Constants.CODE_COMMA); + // U+0069: "i" LATIN SMALL LETTER I + // U+0131: "ı" LATIN SMALL LETTER DOTLESS I + // U+0130: "İ" LATIN SMALL LETTER I WITH DOT ABOVE + // U+0049: "I" LATIN SMALL LETTER I + assert_toUpperCaseOfCodeForLocale(US, 0x0069, 0x0049); // i -> I + assert_toUpperCaseOfCodeForLocale(US, 0x0131, 0x0049); // ı -> I + assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0069, 0x0130); // i -> İ + assert_toUpperCaseOfCodeForLocale(TURKEY, 0x0131, 0x0049); // ı -> I + // U+00DF: "ß" LATIN SMALL LETTER SHARP S + // The title case of "ß" is "SS". + assert_toUpperCaseOfCodeForLocale(US, 0x00DF, Constants.CODE_UNSPECIFIED); + // U+03AC: "ά" GREEK SMALL LETTER ALPHA WITH TONOS + // U+0386: "Ά" GREEK CAPITAL LETTER ALPHA WITH TONOS + assert_toUpperCaseOfCodeForLocale(GREECE, 0x03AC, 0x0386); + // U+03CA: "ϊ" GREEK SMALL LETTER IOTA WITH DIALYTIKA + // U+03AA: "Ϊ" GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + assert_toUpperCaseOfCodeForLocale(GREECE, 0x03CA, 0x03AA); + // U+03B0: "ΰ" GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + // The title case of "ΰ" is "\u03A5\u0308\u0301". + assert_toUpperCaseOfCodeForLocale(GREECE, 0x03B0, Constants.CODE_UNSPECIFIED); + } + + private static void assert_capitalizeFirstCodePoint(final Locale locale, final String text, + final String expected) { + assertEquals(text + " in " + locale, expected, + StringUtils.capitalizeFirstCodePoint(text, locale)); + } + + public void test_capitalizeFirstCodePoint() { + assert_capitalizeFirstCodePoint(US, "", ""); + assert_capitalizeFirstCodePoint(US, "a", "A"); + assert_capitalizeFirstCodePoint(US, "à", "À"); + assert_capitalizeFirstCodePoint(US, "ß", "SS"); + assert_capitalizeFirstCodePoint(US, "text", "Text"); + assert_capitalizeFirstCodePoint(US, "iGoogle", "IGoogle"); + assert_capitalizeFirstCodePoint(TURKEY, "iyi", "İyi"); + assert_capitalizeFirstCodePoint(TURKEY, "ısırdı", "Isırdı"); + assert_capitalizeFirstCodePoint(GREECE, "ά", "Ά"); + assert_capitalizeFirstCodePoint(GREECE, "άνεση", "Άνεση"); + } + + private static void assert_capitalizeFirstAndDowncaseRest(final Locale locale, + final String text, final String expected) { + assertEquals(text + " in " + locale, expected, + StringUtils.capitalizeFirstAndDowncaseRest(text, locale)); + } + + public void test_capitalizeFirstAndDowncaseRest() { + assert_capitalizeFirstAndDowncaseRest(US, "", ""); + assert_capitalizeFirstAndDowncaseRest(US, "a", "A"); + assert_capitalizeFirstAndDowncaseRest(US, "à", "À"); + assert_capitalizeFirstAndDowncaseRest(US, "ß", "SS"); + assert_capitalizeFirstAndDowncaseRest(US, "text", "Text"); + assert_capitalizeFirstAndDowncaseRest(US, "iGoogle", "Igoogle"); + assert_capitalizeFirstAndDowncaseRest(US, "invite", "Invite"); + assert_capitalizeFirstAndDowncaseRest(US, "INVITE", "Invite"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "iyi", "İyi"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "İYİ", "İyi"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "ısırdı", "Isırdı"); + assert_capitalizeFirstAndDowncaseRest(TURKEY, "ISIRDI", "Isırdı"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "ά", "Ά"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "άνεση", "Άνεση"); + assert_capitalizeFirstAndDowncaseRest(GREECE, "ΆΝΕΣΗ", "Άνεση"); + } +} diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk index b108a8a81..3e3d419e6 100644 --- a/tools/dicttool/Android.mk +++ b/tools/dicttool/Android.mk @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Only build if it's explicitly requested, or running mm/mmm. +ifneq ($(ONE_SHOT_MAKEFILE)$(filter $(MAKECMDGOALS),dicttool_aosp),) + # HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11. LATINIME_HOST_OSNAME := $(shell uname -s) ifeq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this @@ -29,10 +32,12 @@ LOCAL_PATH := $(LATINIME_DICTTOOL_AOSP_LOCAL_PATH) include $(CLEAR_VARS) LATINIME_LOCAL_DIR := ../.. -LATINIME_BASE_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/java/src/com/android/inputmethod -LATINIME_ANNOTATIONS_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/annotations -MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin/makedict -LATINIME_TESTS_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin +LATINIME_BASE_SRC_DIR := $(LATINIME_LOCAL_DIR)/java/src/com/android/inputmethod +LATINIME_BASE_OVERRIDABLE_SRC_DIR := \ + $(LATINIME_LOCAL_DIR)/java-overridable/src/com/android/inputmethod +LATINIME_ANNOTATIONS_SRC_DIR := $(LATINIME_BASE_SRC_DIR)/annotations +MAKEDICT_CORE_SRC_DIR := $(LATINIME_BASE_SRC_DIR)/latin/makedict +LATINIME_TESTS_SRC_DIR := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin # Dependencies for Dicttool. Most of these files are needed by BinaryDictionary.java. Note that # a significant part of the dependencies are mocked in the compat/ directory, with empty or @@ -48,7 +53,6 @@ LATINIME_SRC_FILES_FOR_DICTTOOL := \ latin/PrevWordsInfo.java \ latin/SuggestedWords.java \ latin/WordComposer.java \ - latin/define/DebugFlags.java \ latin/settings/NativeSuggestOptions.java \ latin/settings/SettingsValuesForSuggestion.java \ latin/utils/BinaryDictionaryUtils.java \ @@ -60,31 +64,36 @@ LATINIME_SRC_FILES_FOR_DICTTOOL := \ latin/utils/ResizableIntArray.java \ latin/utils/StringUtils.java +LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL := \ + latin/define/DebugFlags.java + LATINIME_TEST_SRC_FILES_FOR_DICTTOOL := \ utils/ByteArrayDictBuffer.java -USED_TARGETED_SRCS := \ - $(addprefix $(LATINIME_BASE_SOURCE_DIRECTORY)/, $(LATINIME_SRC_FILES_FOR_DICTTOOL)) \ - $(addprefix $(LATINIME_TESTS_SOURCE_DIRECTORY)/, $(LATINIME_TEST_SRC_FILES_FOR_DICTTOOL)) +USED_TARGETED_SRC_FILES := \ + $(addprefix $(LATINIME_BASE_SRC_DIR)/, $(LATINIME_SRC_FILES_FOR_DICTTOOL)) \ + $(addprefix $(LATINIME_BASE_OVERRIDABLE_SRC_DIR)/, \ + $(LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL)) \ + $(addprefix $(LATINIME_TESTS_SRC_DIR)/, $(LATINIME_TEST_SRC_FILES_FOR_DICTTOOL)) -DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \ +DICTTOOL_ONDEVICE_TESTS_DIR := \ $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin/makedict/ -DICTTOOL_COMPAT_TESTS_DIRECTORY := compat +DICTTOOL_COMPAT_TESTS_DIR := compat -LOCAL_MAIN_SRC_FILES := $(call all-java-files-under, $(MAKEDICT_CORE_SOURCE_DIRECTORY)) +LOCAL_MAIN_SRC_FILES := $(call all-java-files-under, $(MAKEDICT_CORE_SRC_DIR)) LOCAL_TOOL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_ANNOTATIONS_SRC_FILES := \ - $(call all-java-files-under, $(LATINIME_ANNOTATIONS_SOURCE_DIRECTORY)) +LOCAL_ANNOTATIONS_SRC_FILES := $(call all-java-files-under, $(LATINIME_ANNOTATIONS_SRC_DIR)) LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \ $(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \ - $(call all-java-files-under, $(DICTTOOL_COMPAT_TESTS_DIRECTORY)) \ - $(LOCAL_ANNOTATIONS_SRC_FILES) $(USED_TARGETED_SRCS) \ - $(LATINIME_BASE_SOURCE_DIRECTORY)/latin/Constants.java \ + $(call all-java-files-under, $(DICTTOOL_COMPAT_TESTS_DIR)) \ + $(LOCAL_ANNOTATIONS_SRC_FILES) $(USED_TARGETED_SRC_FILES) \ + $(LATINIME_BASE_SRC_DIR)/latin/Constants.java \ $(call all-java-files-under, tests) \ - $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIRECTORY)) + $(call all-java-files-under, $(DICTTOOL_ONDEVICE_TESTS_DIR)) LOCAL_JAVA_LIBRARIES := junit +LOCAL_STATIC_JAVA_LIBRARIES := jsr305lib LOCAL_REQUIRED_MODULES := $(LATINIME_HOST_NATIVE_LIBNAME) LOCAL_JAR_MANIFEST := etc/manifest.txt LOCAL_MODULE := dicttool_aosp @@ -98,3 +107,5 @@ endif # Darwin - TODO: Remove this LATINIME_DICTTOOL_AOSP_LOCAL_PATH := LATINIME_LOCAL_DIR := LATINIME_HOST_OSNAME := + +endif diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java index 458f22c45..c4457a1b7 100644 --- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java +++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java @@ -31,8 +31,12 @@ public class CombinerChain { mComposingWord = new StringBuilder(initialText); } - public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { - mComposingWord.append(newEvent.getTextToCommit()); + public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { + return newEvent; + } + + public void applyProcessedEvent(final Event event) { + mComposingWord.append(event.getTextToCommit()); } public CharSequence getComposingWordWithCombiningFeedback() { diff --git a/tools/make-keyboard-text/res/values-bn-rIN/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-bn-rIN/donottranslate-more-keys.xml index 4955cd46a..1ec4249d0 100644 --- a/tools/make-keyboard-text/res/values-bn-rIN/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-bn-rIN/donottranslate-more-keys.xml @@ -23,6 +23,6 @@ U+0996: "ख" BENGALI LETTER KHA U+0997: "ग" BENGALI LETTER GA --> <string name="keylabel_to_alpha">কখগ</string> - <!-- U+09F3: "৳" BENGALI RUPEE SIGN --> - <string name="keyspec_currency">৳</string> + <!-- U+20B9: "₹" INDIAN RUPEE SIGN --> + <string name="keyspec_currency">₹</string> </resources> diff --git a/tools/make-keyboard-text/res/values-si-rLK/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-si-rLK/donottranslate-more-keys.xml index 89c9195e8..871d82ab0 100644 --- a/tools/make-keyboard-text/res/values-si-rLK/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values-si-rLK/donottranslate-more-keys.xml @@ -22,4 +22,6 @@ U+0D85: "අ" SINHALA LETTER AYANNA U+0D86: "ආ" SINHALA LETTER AAYANNA --> <string name="keylabel_to_alpha">අ,ආ</string> + <!-- U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA --> + <string name="keyspec_currency">රු</string> </resources> diff --git a/tools/make-keyboard-text/res/values-ta-rLK/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ta-rLK/donottranslate-more-keys.xml new file mode 100644 index 000000000..9e900a6b1 --- /dev/null +++ b/tools/make-keyboard-text/res/values-ta-rLK/donottranslate-more-keys.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for "switch to alphabetic" key. + U+0BA4: "த" TAMIL LETTER TA + U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I + U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA --> + <string name="keylabel_to_alpha">தமிழ்</string> + <!-- U+0DBB/U+0DD4: "රු" SINHALA LETTER RAYANNA/SINHALA VOWEL SIGN KETTI PAA-PILLA --> + <string name="keyspec_currency">රු</string> +</resources> diff --git a/tools/make-keyboard-text/res/values-ta-rSG/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ta-rSG/donottranslate-more-keys.xml new file mode 100644 index 000000000..07e4d8beb --- /dev/null +++ b/tools/make-keyboard-text/res/values-ta-rSG/donottranslate-more-keys.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Label for "switch to alphabetic" key. + U+0BA4: "த" TAMIL LETTER TA + U+0BAE/U+0BBF: "மி" TAMIL LETTER MA/TAMIL VOWEL SIGN I + U+0BB4/U+0BCD: "ழ்" TAMIL LETTER LLLA/TAMIL SIGN VIRAMA --> + <string name="keylabel_to_alpha">தமிழ்</string> +</resources> 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 4b9ca162e..c4a1b889e 100644 --- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml @@ -87,7 +87,7 @@ U+20B1: "₱" PESO SIGN --> <string name="morekeys_currency_dollar">¢,£,€,¥,₱</string> <string name="keyspec_currency">$</string> - <string name="morekeys_currency">$,¢,€,£,¥,₱</string> + <string name="morekeys_currency_generic">$,¢,€,£,¥,₱</string> <string name="morekeys_punctuation">"!autoColumnOrder!8,\\,,?,!,#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,',@,:,-,\",+,\\%,&"</string> <string name="morekeys_tablet_punctuation">"!autoColumnOrder!7,\\,,',#,!text/keyspec_right_parenthesis,!text/keyspec_left_parenthesis,/,;,@,:,-,\",+,\\%,&"</string> <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE --> @@ -258,5 +258,5 @@ <string name="morekeys_single_quote">!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes</string> <string name="morekeys_double_quote">!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes</string> <string name="morekeys_tablet_double_quote">!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes</string> - <string name="keyspec_emoji_key">!icon/emoji_key|!code/key_emoji</string> + <string name="keyspec_emoji_action_key">!icon/emoji_action_key|!code/key_emoji</string> </resources> |