aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/Android.mk4
-rw-r--r--java/AndroidManifest.xml21
-rw-r--r--java/proguard.flags25
-rw-r--r--java/res/layout/input_view.xml8
-rw-r--r--java/res/layout/research_feedback_activity.xml31
-rw-r--r--java/res/layout/research_feedback_fragment_layout.xml112
-rw-r--r--java/res/layout/research_feedback_layout.xml50
-rw-r--r--java/res/layout/research_splash.xml88
-rw-r--r--java/res/raw/main_en.dictbin1068816 -> 1068791 bytes
-rw-r--r--java/res/raw/main_fr.dictbin1328007 -> 1327366 bytes
-rw-r--r--java/res/values-af/strings-appname.xml27
-rw-r--r--java/res/values-af/strings.xml21
-rw-r--r--java/res/values-am/strings-appname.xml27
-rw-r--r--java/res/values-am/strings.xml21
-rw-r--r--java/res/values-ar/strings-appname.xml27
-rw-r--r--java/res/values-ar/strings.xml21
-rw-r--r--java/res/values-be/strings-appname.xml27
-rw-r--r--java/res/values-be/strings.xml21
-rw-r--r--java/res/values-bg/strings-appname.xml27
-rw-r--r--java/res/values-bg/strings.xml21
-rw-r--r--java/res/values-ca/strings-appname.xml27
-rw-r--r--java/res/values-ca/strings.xml21
-rw-r--r--java/res/values-cs/strings-appname.xml27
-rw-r--r--java/res/values-cs/strings.xml21
-rw-r--r--java/res/values-da/strings-appname.xml27
-rw-r--r--java/res/values-da/strings.xml21
-rw-r--r--java/res/values-de/strings-appname.xml27
-rw-r--r--java/res/values-de/strings.xml21
-rw-r--r--java/res/values-el/strings-appname.xml27
-rw-r--r--java/res/values-el/strings.xml21
-rw-r--r--java/res/values-en-rGB/strings-appname.xml27
-rw-r--r--java/res/values-en-rGB/strings.xml21
-rw-r--r--java/res/values-en/whitelist.xml411
-rw-r--r--java/res/values-es-rUS/strings-appname.xml27
-rw-r--r--java/res/values-es-rUS/strings.xml21
-rw-r--r--java/res/values-es/strings-appname.xml27
-rw-r--r--java/res/values-es/strings.xml21
-rw-r--r--java/res/values-et/strings-appname.xml27
-rw-r--r--java/res/values-et/strings.xml21
-rw-r--r--java/res/values-fa/strings-appname.xml27
-rw-r--r--java/res/values-fa/strings.xml65
-rw-r--r--java/res/values-fi/strings-appname.xml27
-rw-r--r--java/res/values-fi/strings.xml21
-rw-r--r--java/res/values-fr/donottranslate.xml4
-rw-r--r--java/res/values-fr/strings-appname.xml27
-rw-r--r--java/res/values-fr/strings.xml21
-rw-r--r--java/res/values-hi/strings-appname.xml27
-rw-r--r--java/res/values-hi/strings.xml29
-rw-r--r--java/res/values-hr/strings-appname.xml27
-rw-r--r--java/res/values-hr/strings.xml21
-rw-r--r--java/res/values-hu/strings-appname.xml27
-rw-r--r--java/res/values-hu/strings.xml21
-rw-r--r--java/res/values-in/strings-appname.xml27
-rw-r--r--java/res/values-in/strings.xml23
-rw-r--r--java/res/values-is/strings.xml263
-rw-r--r--java/res/values-it/strings-appname.xml27
-rw-r--r--java/res/values-it/strings.xml21
-rw-r--r--java/res/values-iw/strings-appname.xml27
-rw-r--r--java/res/values-iw/strings.xml21
-rw-r--r--java/res/values-ja/strings-appname.xml27
-rw-r--r--java/res/values-ja/strings.xml21
-rw-r--r--java/res/values-ka/strings.xml263
-rw-r--r--java/res/values-ko/strings-appname.xml27
-rw-r--r--java/res/values-ko/strings.xml21
-rw-r--r--java/res/values-lt/strings-appname.xml27
-rw-r--r--java/res/values-lt/strings.xml21
-rw-r--r--java/res/values-lv/strings-appname.xml27
-rw-r--r--java/res/values-lv/strings.xml21
-rw-r--r--java/res/values-mk/strings.xml263
-rw-r--r--java/res/values-ms/strings-appname.xml27
-rw-r--r--java/res/values-ms/strings.xml21
-rw-r--r--java/res/values-nb/strings-appname.xml27
-rw-r--r--java/res/values-nb/strings.xml21
-rw-r--r--java/res/values-nl/strings-appname.xml27
-rw-r--r--java/res/values-nl/strings.xml21
-rw-r--r--java/res/values-pl/strings-appname.xml30
-rw-r--r--java/res/values-pl/strings.xml21
-rw-r--r--java/res/values-pt-rPT/strings-appname.xml27
-rw-r--r--java/res/values-pt-rPT/strings.xml21
-rw-r--r--java/res/values-pt/strings-appname.xml27
-rw-r--r--java/res/values-pt/strings.xml21
-rw-r--r--java/res/values-rm/strings.xml30
-rw-r--r--java/res/values-ro/strings-appname.xml27
-rw-r--r--java/res/values-ro/strings.xml21
-rw-r--r--java/res/values-ru/strings-appname.xml27
-rw-r--r--java/res/values-ru/strings.xml21
-rw-r--r--java/res/values-sk/strings-appname.xml27
-rw-r--r--java/res/values-sk/strings.xml21
-rw-r--r--java/res/values-sl/strings-appname.xml27
-rw-r--r--java/res/values-sl/strings.xml21
-rw-r--r--java/res/values-sr/strings-appname.xml27
-rw-r--r--java/res/values-sr/strings.xml21
-rw-r--r--java/res/values-sv/strings-appname.xml27
-rw-r--r--java/res/values-sv/strings.xml21
-rw-r--r--java/res/values-sw/strings-appname.xml27
-rw-r--r--java/res/values-sw/strings.xml21
-rw-r--r--java/res/values-sw600dp/config.xml5
-rw-r--r--java/res/values-sw768dp/config.xml4
-rw-r--r--java/res/values-th/strings-appname.xml27
-rw-r--r--java/res/values-th/strings.xml21
-rw-r--r--java/res/values-tl/strings-appname.xml27
-rw-r--r--java/res/values-tl/strings.xml21
-rw-r--r--java/res/values-tr/strings-appname.xml27
-rw-r--r--java/res/values-tr/strings.xml21
-rw-r--r--java/res/values-uk/strings-appname.xml27
-rw-r--r--java/res/values-uk/strings.xml21
-rw-r--r--java/res/values-vi/strings-appname.xml27
-rw-r--r--java/res/values-vi/strings.xml21
-rw-r--r--java/res/values-zh-rCN/strings-appname.xml27
-rw-r--r--java/res/values-zh-rCN/strings.xml21
-rw-r--r--java/res/values-zh-rTW/strings-appname.xml27
-rw-r--r--java/res/values-zh-rTW/strings.xml21
-rw-r--r--java/res/values-zu/strings-appname.xml27
-rw-r--r--java/res/values-zu/strings.xml21
-rw-r--r--java/res/values/attrs.xml36
-rw-r--r--java/res/values/config.xml15
-rw-r--r--java/res/values/dimens.xml8
-rw-r--r--java/res/values/gesture-input.xml (renamed from java/res/values-it/donottranslate.xml)7
-rw-r--r--java/res/values/strings-appname.xml33
-rw-r--r--java/res/values/strings.xml115
-rw-r--r--java/res/values/styles.xml36
-rw-r--r--java/res/values/themes-basic-highcontrast.xml4
-rw-r--r--java/res/values/themes-basic.xml4
-rw-r--r--java/res/values/themes-gingerbread.xml4
-rw-r--r--java/res/values/themes-ics.xml4
-rw-r--r--java/res/values/themes-stone-bold.xml4
-rw-r--r--java/res/values/themes-stone.xml4
-rw-r--r--java/res/values/urls.xml (renamed from java/res/values/whitelist.xml)13
-rw-r--r--java/res/xml-sw600dp/key_styles_common.xml11
-rw-r--r--java/res/xml-sw600dp/rowkeys_symbols3.xml2
-rw-r--r--java/res/xml-sw768dp/key_space.xml25
-rw-r--r--java/res/xml-sw768dp/key_styles_common.xml19
-rw-r--r--java/res/xml-sw768dp/rows_east_slavic.xml3
-rw-r--r--java/res/xml/key_styles_common.xml19
-rw-r--r--java/res/xml/key_styles_f1.xml43
-rw-r--r--java/res/xml/method.xml67
-rw-r--r--java/res/xml/prefs.xml53
-rw-r--r--java/res/xml/rowkeys_east_slavic1.xml8
-rw-r--r--java/res/xml/rowkeys_east_slavic2.xml3
-rw-r--r--java/res/xml/rowkeys_symbols3.xml2
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java3
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java33
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java10
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java20
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java34
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java14
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java22
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java194
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java51
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java35
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java92
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java311
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java (renamed from java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java)273
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java20
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java481
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java38
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java161
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java162
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java70
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java30
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java9
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java11
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java17
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java854
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java168
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java286
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java (renamed from java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java)8
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtype.java2
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java4
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java94
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java204
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java4
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java69
-rw-r--r--java/src/com/android/inputmethod/latin/BoundedTreeSet.java49
-rw-r--r--java/src/com/android/inputmethod/latin/CollectionUtils.java95
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java14
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java22
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionary.java178
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java75
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java80
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java51
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java13
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java182
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java55
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java147
-rw-r--r--java/src/com/android/inputmethod/latin/ImfUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java11
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java133
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java13
-rw-r--r--java/src/com/android/inputmethod/latin/JniUtils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java18
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java1534
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LocaleUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java757
-rw-r--r--java/src/com/android/inputmethod/latin/ResizableIntArray.java146
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java455
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java137
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java106
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java5
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java10
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java96
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java582
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java71
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java11
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java54
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java10
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java56
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java47
-rw-r--r--java/src/com/android/inputmethod/latin/UserDictionary.java225
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionary.java41
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java17
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java16
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java56
-rw-r--r--java/src/com/android/inputmethod/latin/WhitelistDictionary.java109
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java168
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java266
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java16
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Word.java8
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java475
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java154
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java25
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java303
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java59
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java10
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java10
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java (renamed from java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java)107
-rw-r--r--java/src/com/android/inputmethod/research/BootBroadcastReceiver.java33
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackActivity.java54
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackFragment.java73
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackLayout.java62
-rw-r--r--java/src/com/android/inputmethod/research/LogBuffer.java113
-rw-r--r--java/src/com/android/inputmethod/research/LogUnit.java83
-rw-r--r--java/src/com/android/inputmethod/research/MainLogBuffer.java127
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLog.java310
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java1270
-rw-r--r--java/src/com/android/inputmethod/research/Statistics.java146
-rw-r--r--java/src/com/android/inputmethod/research/UploaderService.java191
242 files changed, 11902 insertions, 6200 deletions
diff --git a/java/Android.mk b/java/Android.mk
index 52cc18b26..364973bea 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -28,9 +28,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
# We want to install libjni_latinime.so to the system partition if LatinIME gets installed.
LOCAL_REQUIRED_MODULES := libjni_latinime
-LOCAL_STATIC_JAVA_LIBRARIES := android-common
-LOCAL_STATIC_JAVA_LIBRARIES += inputmethod-common
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4
# 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 06d852bb0..3e80de22a 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -1,7 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.android.inputmethod.latin">
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
@@ -10,7 +28,8 @@
<application android:label="@string/aosp_android_keyboard_ime_name"
android:icon="@drawable/ic_ime_settings"
android:backupAgent="BackupAgent"
- android:killAfterRestore="false">
+ android:killAfterRestore="false"
+ android:supportsRtl="true">
<service android:name="LatinIME"
android:label="@string/aosp_android_keyboard_ime_name"
diff --git a/java/proguard.flags b/java/proguard.flags
index 34e23aa9a..ac5b7df16 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -3,10 +3,6 @@
<init>(...);
}
--keep class com.android.inputmethod.latin.Flag {
- *;
-}
-
-keep class com.android.inputmethod.keyboard.ProximityInfo {
<init>(com.android.inputmethod.keyboard.ProximityInfo);
}
@@ -24,11 +20,19 @@
boolean equalsIgnoreCase(...);
}
+-keep class com.android.inputmethod.latin.InputPointers {
+ *;
+}
+
+-keep class com.android.inputmethod.latin.ResizableIntArray {
+ *;
+}
+
-keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment {
*;
}
--keep class com.android.inputmethod.keyboard.LatinKeyboardView {
+-keep class com.android.inputmethod.keyboard.MainKeyboardView {
# Keep getter/setter methods for ObjectAnimator
int getLanguageOnSpacebarAnimAlpha();
void setLanguageOnSpacebarAnimAlpha(int);
@@ -40,14 +44,13 @@
<init>(...);
}
--keep class com.android.inputmethod.latin.ResearchLogger {
- void setLogFileManager(...);
- void clearAll();
- com.android.inputmethod.latin.ResearchLogger$LogFileManager getLogFileManager();
+-keepclasseswithmembernames class * {
+ native <methods>;
}
--keep class com.android.inputmethod.latin.ResearchLogger$LogFileManager {
- java.lang.String getContents();
+-keep class com.android.inputmethod.research.ResearchLogger {
+ void flush();
+ void publishCurrentLogUnit(...);
}
-keep class com.android.inputmethod.keyboard.KeyboardLayoutSet$Builder {
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 3863534be..40eff3839 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -43,20 +43,20 @@
android:layout_width="@dimen/suggestions_strip_padding"
android:layout_height="@dimen/suggestions_strip_height"
style="?attr/suggestionsStripBackgroundStyle" />
- <com.android.inputmethod.latin.suggestions.SuggestionsView
- android:id="@+id/suggestions_view"
+ <com.android.inputmethod.latin.suggestions.SuggestionStripView
+ android:id="@+id/suggestion_strip_view"
android:layout_weight="1.0"
android:layout_width="0dp"
android:layout_height="@dimen/suggestions_strip_height"
android:gravity="center_vertical"
- style="?attr/suggestionsViewStyle" />
+ style="?attr/suggestionStripViewStyle" />
<View
android:layout_width="@dimen/suggestions_strip_padding"
android:layout_height="@dimen/suggestions_strip_height"
style="?attr/suggestionsStripBackgroundStyle" />
</LinearLayout>
- <com.android.inputmethod.keyboard.LatinKeyboardView
+ <com.android.inputmethod.keyboard.MainKeyboardView
android:id="@+id/keyboard_view"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
diff --git a/java/res/layout/research_feedback_activity.xml b/java/res/layout/research_feedback_activity.xml
new file mode 100644
index 000000000..a6b8b8a43
--- /dev/null
+++ b/java/res/layout/research_feedback_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.inputmethod.research.FeedbackLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/research_feedback_layout"
+>
+
+ <fragment
+ android:id="@+id/research_feedback_fragment"
+ android:name="com.android.inputmethod.research.FeedbackFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+</com.android.inputmethod.research.FeedbackLayout>
diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
new file mode 100644
index 000000000..cc04cedf4
--- /dev/null
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+>
+
+ <!-- Mimic a dialog title. Necessary since the dialog is actually an activity, so the normal
+ dialog title construction code is not available. -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <com.android.internal.widget.DialogTitle
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="64dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:gravity="center_vertical|left"
+ android:text="@string/research_feedback_dialog_title" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
+ </LinearLayout>
+
+ <EditText
+ android:id="@+id/research_feedback_contents"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="fill_horizontal|center_vertical"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="8dip"
+ android:lines="2"
+ android:hint="@string/research_feedback_hint"
+ android:inputType="textMultiLine"
+ android:imeOptions="flagNoFullscreen"
+ >
+ <requestFocus />
+ </EditText>
+
+ <CheckBox
+ android:id="@+id/research_feedback_include_history"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="8dip"
+ android:checked="true"
+ android:text="@string/research_feedback_include_history_label"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="0dip"
+ >
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:measureWithLargestChild="true"
+ >
+ <Button
+ android:id="@+id/research_feedback_cancel_button"
+ android:layout_width="0dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_feedback_cancel"
+ android:layout_height="wrap_content"
+ />
+ <Button
+ android:id="@+id/research_feedback_send_button"
+ android:layout_width="0dip"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_feedback_send"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/res/layout/research_feedback_layout.xml b/java/res/layout/research_feedback_layout.xml
new file mode 100644
index 000000000..bacd19101
--- /dev/null
+++ b/java/res/layout/research_feedback_layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+>
+
+ <EditText
+ android:id="@+id/research_feedback_contents"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="fill_horizontal|center_vertical"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="8dip"
+ android:lines="2"
+ android:hint="@string/research_feedback_hint"
+ android:inputType="textMultiLine"
+ android:imeOptions="flagNoFullscreen"
+ android:focusable="true"
+ >
+ <requestFocus />
+ </EditText>
+
+ <CheckBox
+ android:id="@+id/research_feedback_include_history"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="8dip"
+ android:checked="true"
+ android:text="@string/research_feedback_include_history_label"
+ />
+</LinearLayout>
diff --git a/java/res/layout/research_splash.xml b/java/res/layout/research_splash.xml
new file mode 100644
index 000000000..56fd7024f
--- /dev/null
+++ b/java/res/layout/research_splash.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/research_splash_screen_layout">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <com.android.internal.widget.DialogTitle
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="64dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:gravity="center_vertical|left"
+ android:text="@string/research_splash_title" />
+ <View android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
+ </LinearLayout>
+
+ <TextView
+ android:text="@string/research_splash_content"
+ android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_gravity="fill_horizontal|center_vertical"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:layout_marginBottom="16dip"
+ android:layout_marginTop="16dip"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="0dip">
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:measureWithLargestChild="true">
+ <Button
+ android:layout_width="0dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ stype="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_dont_send_usage_info"
+ android:layout_height="wrap_content"
+ android:id="@+id/research_do_not_log_button" />
+ <Button
+ android:layout_width="0dip"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_send_usage_info"
+ android:layout_height="wrap_content"
+ android:id="@+id/research_do_log_button" />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 98a9361b5..e02e300e4 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 717078c93..8e616591c 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/values-af/strings-appname.xml b/java/res/values-af/strings-appname.xml
new file mode 100644
index 000000000..d6bb52f52
--- /dev/null
+++ b/java/res/values-af/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-sleutelbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-speltoetser"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-sleutelbordinstellings"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Speltoets tans instellings"</string>
+</resources>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 7431fced8..be692bfdf 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-sleutelbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-sleutelbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-sleutelbordinstellings"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-speltoetser"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-speltoetser (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Speltoetser se instellings"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Speltoetser gebruik inskrywings uit jou kontaklys"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Verstek"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Stel kontakname voor"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gebruik name van kontakte vir voorstelle en korreksies"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktiveer herkorrigerings"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Stel voorstelle vir herkorrigerings"</string>
<string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Voeg woordeboeke by"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hoofwoordeboek"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Matig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressief"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Baie aggressief"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Volgendewoordvoorstelle"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gebruik vorige woord om voorstelle te verbeter"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Volgendewoordvoorspelling"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gebruik vorige woord ook vir voorspelling"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Stel volgende woord voor"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Gebaseer op vorige woord"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Gebaarinvoer"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Voer \'n woord in deur die letters van \'n woord te trek"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Wys gebaarspoor"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Wys gebaar se woord"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Wys swewende voorskouwoord saam met die gebaar"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gestoor"</string>
<string name="label_go_key" msgid="1635148082137219148">"Gaan"</string>
<string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Soek"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Verander taal"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift geaktiveer"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kasslot geaktiveer"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift gedeaktiveer"</string>
diff --git a/java/res/values-am/strings-appname.xml b/java/res/values-am/strings-appname.xml
new file mode 100644
index 000000000..fd93114f3
--- /dev/null
+++ b/java/res/values-am/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"የAndroid ቁልፍ ሰሌዳ"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android የፊደል አራሚ"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android የቁልፍ ሰሌዳ ቅንብሮች"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"የፊደል አራሚ ቅንብሮች"</string>
+</resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index d70c05da9..f4a9de617 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"የAndroid ቁልፍሰሌዳ"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"የAndroid ቁልፍሰሌዳ ቅንብሮች"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android የፊደል ማረሚያ"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android የፊደል ማረሚያ (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"የፊደል አራሚ ቅንብሮች"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ፊደል አራሚ ከእውቅያ ዝርዝርህ የገቡትን ይጠቀማል"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ነባሪ"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"የዕውቂያ ስም ጠቁም"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ከዕውቂያዎች ለጥቆማዎች እና ማስተካከያዎች ስሞች ተጠቀም"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"ድጋሚ ለማስተካከል አንቃ"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"ድጋሚ ለማስተካከል ጥቆማዎችን አዘጋጅ"</string>
<string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"መዝገበ ቃላቶች ጨምር"</string>
<string name="main_dictionary" msgid="4798763781818361168">"ዋና መዝገበ ቃላት"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"መጠነኛ"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"ኃይለኛ"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"በጣም ቁጡ"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"የቀጣይ ቃል አስተያየቶች"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ምክሮችን ለማሻሻል ቀዳሚ ቃል ተጠቀም"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"የቀጣይ ቃል ግምት"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"ለትንበያ የቀደመ ቃል እንዲሁ ተጠቀም"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"የቀጣይ ቃል አስተያየቶች"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"በቀዳሚው ቃል ላይ የተመሠረተ"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"የእጅ ምልክት ግብዓት"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"የአንድ ቃል ፊደሎችን በመከታተል አንድ ቃል አስገባ"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"ምልክት የሚሄድበት መንገድ አሳይ"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"የምልክት ቃል አሳይ"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"ተንሳፋፊ የቅድመ እይታ ቃል ከምልክት ጋር አሳይ"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ተቀምጧል"</string>
<string name="label_go_key" msgid="1635148082137219148">"ሂድ"</string>
<string name="label_next_key" msgid="362972844525672568">"በመቀጠል"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"ተመለስ"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"ፍለጋ"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"ነጥብ"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"ቋንቋ ቀይር"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"ቀጣይ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"ቀዳሚ"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"ቅያር ቁልፍ ነቅቷል"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"አቢያት ማድረጊያ ነቅቷል"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ቅያር ተሰናክሏል"</string>
diff --git a/java/res/values-ar/strings-appname.xml b/java/res/values-ar/strings-appname.xml
new file mode 100644
index 000000000..3d81e5d4b
--- /dev/null
+++ b/java/res/values-ar/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"لوحة مفاتيح Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"التدقيق الإملائي في Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"إعدادات لوحة مفاتيح Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"إعدادات التدقيق الإملائي"</string>
+</resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 5678c40c8..29d046e76 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"لوحة مفاتيح Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"لوحة مفاتيح Android ‏(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"إعدادات لوحة مفاتيح Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"التدقيق الإملائي في Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"التدقيق الإملائي في Android‏ (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"إعدادات التدقيق الإملائي"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"يستخدم المدقق الإملائي إدخالات من قائمة جهات الاتصال"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند ضغط مفتاح"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"افتراضي"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"اقتراح أسماء جهات الاتصال"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"تمكين عمليات إعادة التصحيح"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"تعيين اقتراحات لعمليات إعادة التصحيح"</string>
<string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"القواميس الإضافية"</string>
<string name="main_dictionary" msgid="4798763781818361168">"القاموس الرئيسي"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"معتدل"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"حاد"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"شديد الصرامة"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"اقتراحات الكلمات التالية"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"استخدام الكلمة السابقة لتحسين الاقتراحات"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"تنبؤ الكلمات التالية"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"استخدام الكلمة السابقة أيضًا للتنبؤ"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"اقتراحات الكلمات التالية"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"استنادًا إلى الكلمة السابقة"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"إدخال الإيماءة"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"يمكنك إدخال كلمة من خلال تتبع أحرف كلمة ما"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"عرض مسار الإيماءة"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"عرض كلمة الإيماءة"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"عرض كلمة معاينة متحركة مع الإيماءة"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : تم الحفظ"</string>
<string name="label_go_key" msgid="1635148082137219148">"تنفيذ"</string>
<string name="label_next_key" msgid="362972844525672568">"التالي"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"رجوع"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"بحث"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"نقطة"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"تبديل اللغة"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"التالي"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"السابق"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"تم تمكين Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"تم تمكين Caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"تم تعطيل Shift"</string>
diff --git a/java/res/values-be/strings-appname.xml b/java/res/values-be/strings-appname.xml
new file mode 100644
index 000000000..e0aadfa3c
--- /dev/null
+++ b/java/res/values-be/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавіятура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Iнструмент праверкi правапiсу для Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Налады клавіятуры Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налады праверкі арфаграфіі"</string>
+</resources>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 4d14565b9..30217fb32 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавіятура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіятура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Налады клавіятуры Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Iнструмент праверкi правапiсу для Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Iнструмент праверкi правапiсу для Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налады праверкі арфаграфіі"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Модуль праверкі правапісу выкарыстоўвае запісы са спісу кантактаў"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібрацыя пры націску клавіш"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Прапан. імёны кантактаў"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Выкарыстоўваць імёны са спісу кантактаў для прапаноў і выпраўл."</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Уключыць карэкцiроўкі"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаць прапановы для карэкцiроўкі"</string>
<string name="auto_cap" msgid="1719746674854628252">"Аўтаматычна рабіць вялікія літары"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Дадатковыя слоўнікі"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Асноўны слоўнік"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Сціплы"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агрэсіўны"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Вельмі агрэсіўны"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Падказкi для наступнага слова"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Выкарыстаць папярэдняе слова, каб палепшыць прапановы"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Падказка наступнага слова"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Выкарыстанне папярэдняга слова для падказак"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Падказкi для наступнага слова"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На аснове папярэдняга слова"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Уваход жэстам"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Увядзiце слова, адсочваючы лiтары"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Паказаць след жэста"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Паказаць слова жэста"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Паказаць плаваючы прагляд слова з жэстам"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Захаваныя"</string>
<string name="label_go_key" msgid="1635148082137219148">"Пачаць"</string>
<string name="label_next_key" msgid="362972844525672568">"Далей"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Увод"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Кропка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пераключыць мову"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далей"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift уключаны"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock уключаны"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift адключаны"</string>
diff --git a/java/res/values-bg/strings-appname.xml b/java/res/values-bg/strings-appname.xml
new file mode 100644
index 000000000..49e301d32
--- /dev/null
+++ b/java/res/values-bg/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавиатура на Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Програма за правописна проверка за Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Настройки на клавиатурата на Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Настройки за проверка на правописа"</string>
+</resources>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 106a91806..dc25205ce 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура на Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура на Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Настройки на клавиатурата на Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Програма за правописна проверка за Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Програма за правописна проверка за Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройки за проверка на правописа"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"За проверка на правописа се ползват записи от списъка с контакти"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложения за контакти"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Повторни поправки: Актив."</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаване на предложения за повторни поправки"</string>
<string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Добавени речници"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основен речник"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Много агресивно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Предложения за следващата дума"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Използване на предишната дума за подобряване на предложенията"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Предвиждане на следващата дума"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Използване на предишната дума и за предвиждане"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Предложения за следващата дума"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Въз основа на предишната дума"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Въвеждане чрез жест"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Въвеждане на дума чрез проследяване на буквите й"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Следа на жестовете: Показване"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Показване на дума при жестове"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показване на дума с плаваща визуализация при жест"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Запазено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Старт"</string>
<string name="label_next_key" msgid="362972844525672568">"Напред"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Търсене"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Смяна на езика"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Следващ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Предишен"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"„Shift“ е активиран"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"„Caps Lock“ е активиран"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"„Shift“ е деактивиран"</string>
diff --git a/java/res/values-ca/strings-appname.xml b/java/res/values-ca/strings-appname.xml
new file mode 100644
index 000000000..add5c3f2f
--- /dev/null
+++ b/java/res/values-ca/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclat Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortogràfic d\'Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configuració del teclat d\'Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configuració de la correcció ortogràfica"</string>
+</resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index e3adbcff4..57b55d310 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclat Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclat d\'Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configuració del teclat d\'Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortogràfic d\'Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortogràfic d\'Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuració de la correcció ortogràfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortogràfic utilitza entrades de la llista de cont."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms contactes"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de Contactes per a suggeriments i correccions"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activa la capacitat de tornar a corregir"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Estableix suggeriments per tornar a corregir"</string>
<string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionaris complementaris"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionari principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Molt agressiu"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggeriments de paraula següent"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilitza la paraula anterior per millorar els suggeriments"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicció de paraula següent"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilitza també la paraula anterior per a la predicció"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Suggeriments de paraula següent"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"En funció de la paraula anterior"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Dibuixa les lletres d\'una paraula per escriure-la"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra el recorregut del gest"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostra paraules en fer gestos"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostra la paraula de visualització prèvia flotant en fer gestos"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: desada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Vés"</string>
<string name="label_next_key" msgid="362972844525672568">"Següent"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Retorn"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Canvia l\'idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Següent"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maj activat"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloq Maj activat"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maj desactivat"</string>
diff --git a/java/res/values-cs/strings-appname.xml b/java/res/values-cs/strings-appname.xml
new file mode 100644
index 000000000..0eeac88b4
--- /dev/null
+++ b/java/res/values-cs/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klávesnice Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavení klávesnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavení kontroly pravopisu"</string>
+</resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 90d840cd8..930cf13db 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnice Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavení kontroly pravopisu"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhovat jména kontaktů"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Povolit opětovné opravy"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavit návrhy pro opětovné opravy"</string>
<string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplňkové slovníky"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hlavní slovník"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mírné"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivní"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Velmi agresivní"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Návrh dalšího slova"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Použít předchozí slovo ke zlepšení návrhů"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Odhad dalšího slova"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použít předchozí slovo také pro odhad"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Návrhy dalšího slova"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základě předchozího slova"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Zadávání gesty"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Napište slovo zadáním jeho písmen."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovat stopu gesta"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Zobrazovat slovo gesta"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zobrazovat plovoucí náhled slova gesta"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Uloženo"</string>
<string name="label_go_key" msgid="1635148082137219148">"Přejít"</string>
<string name="label_next_key" msgid="362972844525672568">"Další"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"vyhledávací tlačítko"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Tečka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Přepnout jazyk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Další"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Předchozí"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Klávesa Shift je aktivní"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Klávesa Caps Lock je aktivní"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Klávesa Shift je neaktivní"</string>
diff --git a/java/res/values-da/strings-appname.xml b/java/res/values-da/strings-appname.xml
new file mode 100644
index 000000000..faef5824b
--- /dev/null
+++ b/java/res/values-da/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontrol"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Indstillinger for Android-tastatur"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Indstillinger for stavekontrol"</string>
+</resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 50b0b0a9f..f04c43d16 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-tastatur-indstillinger"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontrol"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontrol (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Indstillinger for stavekontrol"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå kontaktnavne op"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruger poster fra listen over kontaktpersoner"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå navne på kontakter"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktivér fornyet rettelse"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angiv forslag til fornyet rettelse"</string>
<string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tillægsordbøger"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hovedordbog"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Meget aggressiv"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til næste ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Brug forrige ord til at forbedre forslag"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Forudsigelse af næste ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Brug også tidligere ord til forudsigelse"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Forslag til næste ord"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baseret på tidligere ord"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Berøringsindtastning"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Indtast et ord ved at skrive bogstaverne for et ord med fingeren"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis spor af berøring"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Vis berøringsord"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vis flydende eksempelord under berøring"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string>
<string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
<string name="label_next_key" msgid="362972844525672568">"Næste"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tilbage"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Søg"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punktum"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Skift sprog"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Næste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift er aktiveret"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock er aktiveret"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift er deaktiveret"</string>
diff --git a/java/res/values-de/strings-appname.xml b/java/res/values-de/strings-appname.xml
new file mode 100644
index 000000000..fc5fb8902
--- /dev/null
+++ b/java/res/values-de/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-Tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-Rechtschreibprüfung"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-Tastatureinstellungen"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Einstellungen für Rechtschreibprüfung"</string>
+</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 216aac5d9..59d8e7357 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-Tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-Rechtschreibprüfung"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-Rechtschreibprüfung (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Einstellungen für Rechtschreibprüfung"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung verwendet Einträge aus Ihrer Kontaktliste."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Bei Tastendruck vibrieren"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Korrekturen aktivieren"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Vorschläge für Korrekturen festlegen"</string>
<string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Erweiterte Wörterbücher"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mäßig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Stark"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sehr stark"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Vorschläge für nächstes Wort"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Zur Verbesserung von Vorschlägen vorheriges Wort verwenden"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Vervollständigung für nächstes Wort"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Vorheriges Wort auch für Vervollständigung verwenden"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Vorschläge für nächstes Wort"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Auf Grundlage des vorherigen Wortes"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Bewegungseingabe"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Wort durch Nachzeichnen der Buchstaben eingeben"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Spur der Bewegung anzeigen"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Wort bei Bewegung anzeigen"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vorgeschlagenes Wort bei Bewegung anzeigen"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string>
<string name="label_go_key" msgid="1635148082137219148">"Los"</string>
<string name="label_next_key" msgid="362972844525672568">"Weiter"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Eingabe"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Suchen"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Aufzählungspunkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Sprache wechseln"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nächste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorherige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Umschalttaste aktiviert"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Feststelltaste aktiviert"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Umschalttaste deaktiviert"</string>
diff --git a/java/res/values-el/strings-appname.xml b/java/res/values-el/strings-appname.xml
new file mode 100644
index 000000000..a199655f2
--- /dev/null
+++ b/java/res/values-el/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Πληκτρολόγιο Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Ορθογραφικός έλεγχος Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Ρυθμίσεις πληκτρολογίου Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ρυθμίσεις ορθογραφικού ελέγχου"</string>
+</resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 486346a09..2803ed258 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Πληκτρολόγιο Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Ορθογραφικός έλεγχος Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ρυθμίσεις ορθογραφικού ελέγχου"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Ο ορθογρ. έλεγχος χρησιμοπ. καταχωρίσεις από τη λίστα επαφών σας"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Προεπιλογή"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Πρόταση ονομάτων επαφών"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ενεργ. επανάλ. διορθώσεων"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ορισμός προτάσεων για επαναλήψεις διορθώσεων"</string>
<string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Πρόσθετα λεξικά"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Κύριο λεξικό"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Μέτρια"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Υψηλή"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Πολύ επιθετική"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Προτάσεις επόμενων λέξεων"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Χρήση προηγούμενης λέξης για τη βελτίωση προτάσεων"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Πρόβλεψη επόμενης λέξης"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Χρησιμοποιήστε, επίσης, την προηγούμενη λέξη για πρόβλεψη"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Προτάσεις επόμενων λέξεων"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Βάσει προηγούμενης λέξης"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Καταχώριση κίνησης"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Καταχώριση μιας λέξης με εντοπισμό των γραμμάτων μιας λέξης"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Εμφάνιση διαδρομής χειρονομίας"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Εμφάνιση λέξης χειρονομίας"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Εμφάνιση κινούμενης προεπισκόπησης λέξης με χειρονομία"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string>
<string name="label_go_key" msgid="1635148082137219148">"Μετ."</string>
<string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Πλήκτρο Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Αναζήτηση"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Κουκκίδα"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Αλλαγή γλώσσας"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Επόμενο"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Προηγούμενο"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Το Shift ενεργοποιημένο"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Το Caps lock είναι ενεργοποιημένο"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Το Shift είναι απενεργοποιημένο"</string>
diff --git a/java/res/values-en-rGB/strings-appname.xml b/java/res/values-en-rGB/strings-appname.xml
new file mode 100644
index 000000000..ad9e782b0
--- /dev/null
+++ b/java/res/values-en-rGB/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android keyboard"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android spell checker"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android keyboard settings"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Spell checking settings"</string>
+</resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 502007666..4d960d7d6 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android keyboard settings"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android spell checker"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android spell checker (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Spellchecking settings"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on key-press"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Enable recorrections"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Set suggestions for recorrections"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Main dictionary"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressive"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Very aggressive"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Next word suggestions"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Use previous word to improve suggestion"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Next word prediction"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use previous word also for prediction"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Next word suggestions"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Based on previous word"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Gesture input"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Input a word by tracing the letters of a word"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Show gesture trail"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Show gesture word"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Show floating preview word with gesture"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string>
<string name="label_go_key" msgid="1635148082137219148">"Go"</string>
<string name="label_next_key" msgid="362972844525672568">"Next"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Search"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Switch language"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Next"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Previous"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift enabled"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock enabled"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift disabled"</string>
diff --git a/java/res/values-en/whitelist.xml b/java/res/values-en/whitelist.xml
deleted file mode 100644
index 262017916..000000000
--- a/java/res/values-en/whitelist.xml
+++ /dev/null
@@ -1,411 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--
- An entry of the whitelist word should be:
- 1. (int)frequency
- 2. (String)before
- 3. (String)after
- -->
- <string-array name="wordlist_whitelist" translatable="false">
-
- <item>255</item>
- <item>ill</item>
- <item>I\'ll</item>
-
- <!-- TODO: Trim down more entries by removing ones that get auto-corrected by the
- Android keyboard's own typing error correction algorithms. -->
-
- <item>255</item>
- <item>acomodate</item>
- <item>accommodate</item>
-
- <item>255</item>
- <item>aint</item>
- <item>ain\'t</item>
-
- <item>255</item>
- <item>alot</item>
- <item>a lot</item>
-
- <item>255</item>
- <item>andteh</item>
- <item>and the</item>
-
- <item>255</item>
- <item>arent</item>
- <item>aren\'t</item>
-
- <item>255</item>
- <item>bot</item>
- <item>not</item>
-
- <item>255</item>
- <item>bern</item>
- <item>been</item>
-
- <item>255</item>
- <item>bot</item>
- <item>not</item>
-
- <item>255</item>
- <item>bur</item>
- <item>but</item>
-
- <item>255</item>
- <item>cam</item>
- <item>can</item>
-
- <item>255</item>
- <item>cant</item>
- <item>can\'t</item>
-
- <item>255</item>
- <item>dame</item>
- <item>same</item>
-
- <item>255</item>
- <item>didint</item>
- <item>didn\'t</item>
-
- <item>255</item>
- <item>dormer</item>
- <item>former</item>
-
- <item>255</item>
- <item>dud</item>
- <item>did</item>
-
- <item>255</item>
- <item>fay</item>
- <item>day</item>
-
- <item>255</item>
- <item>fife</item>
- <item>five</item>
-
- <item>255</item>
- <item>foo</item>
- <item>for</item>
-
- <item>255</item>
- <item>fora</item>
- <item>for a</item>
-
- <item>255</item>
- <item>galled</item>
- <item>called</item>
-
- <item>255</item>
- <item>goo</item>
- <item>too</item>
-
- <item>255</item>
- <item>hed</item>
- <item>he\'d</item>
-
- <item>255</item>
- <item>hel</item>
- <item>he\'ll</item>
-
- <item>255</item>
- <item>heres</item>
- <item>here\'s</item>
-
- <item>255</item>
- <item>hew</item>
- <item>new</item>
-
- <item>255</item>
- <item>hoe</item>
- <item>how</item>
-
- <item>255</item>
- <item>hoes</item>
- <item>how\'s</item>
-
- <item>255</item>
- <item>howd</item>
- <item>how\'d</item>
-
- <item>255</item>
- <item>howll</item>
- <item>how\'ll</item>
-
- <item>255</item>
- <item>hows</item>
- <item>how\'s</item>
-
- <item>255</item>
- <item>howve</item>
- <item>how\'ve</item>
-
- <item>255</item>
- <item>hum</item>
- <item>him</item>
-
- <item>255</item>
- <item>i</item>
- <item>I</item>
-
- <item>255</item>
- <item>ifs</item>
- <item>its</item>
-
- <item>255</item>
- <item>il</item>
- <item>I\'ll</item>
-
- <item>255</item>
- <item>im</item>
- <item>I\'m</item>
-
- <item>255</item>
- <item>inteh</item>
- <item>in the</item>
-
- <item>255</item>
- <item>itd</item>
- <item>it\'d</item>
-
- <item>255</item>
- <item>itsa</item>
- <item>it\'s a</item>
-
- <item>255</item>
- <item>lets</item>
- <item>let\'s</item>
-
- <item>255</item>
- <item>maam</item>
- <item>ma\'am</item>
-
- <item>255</item>
- <item>manu</item>
- <item>many</item>
-
- <item>255</item>
- <item>mare</item>
- <item>made</item>
-
- <item>255</item>
- <item>mew</item>
- <item>new</item>
-
- <item>255</item>
- <item>mire</item>
- <item>more</item>
-
- <item>255</item>
- <item>moat</item>
- <item>most</item>
-
- <item>255</item>
- <item>mot</item>
- <item>not</item>
-
- <item>255</item>
- <item>mote</item>
- <item>note</item>
-
- <item>255</item>
- <item>motes</item>
- <item>notes</item>
-
- <item>255</item>
- <item>mow</item>
- <item>now</item>
-
- <item>255</item>
- <item>namer</item>
- <item>named</item>
-
- <item>255</item>
- <item>nave</item>
- <item>have</item>
-
- <item>255</item>
- <item>nee</item>
- <item>new</item>
-
- <item>255</item>
- <item>nigh</item>
- <item>high</item>
-
- <item>255</item>
- <item>nit</item>
- <item>not</item>
-
- <item>255</item>
- <item>oft</item>
- <item>off</item>
-
- <item>255</item>
- <item>os</item>
- <item>is</item>
-
- <item>255</item>
- <item>pater</item>
- <item>later</item>
-
- <item>255</item>
- <item>rook</item>
- <item>took</item>
-
- <item>255</item>
- <item>shel</item>
- <item>she\'ll</item>
-
- <item>255</item>
- <item>shouldent</item>
- <item>shouldn\'t</item>
-
- <item>255</item>
- <item>sill</item>
- <item>will</item>
-
- <item>255</item>
- <item>sown</item>
- <item>down</item>
-
- <item>255</item>
- <item>thatd</item>
- <item>that\'d</item>
-
- <item>255</item>
- <item>tine</item>
- <item>time</item>
-
- <item>255</item>
- <item>thong</item>
- <item>thing</item>
-
- <item>255</item>
- <item>tome</item>
- <item>time</item>
-
- <!-- through additional proximity, 'uf' becomes 'of'. 'o' is not next to 'u' so anyone
- typing 'uf' probably meant 'if', but 'of' is much more common and should be left
- higher than 'if', hence the need for this entry. -->
- <item>255</item>
- <item>uf</item>
- <item>if</item>
-
- <!-- 'un' becomes 'UN' because of perfect match ; even if we remove 'UN', then 'un'
- will become 'on' for the same reason as above. So list this here. -->
- <item>255</item>
- <item>un</item>
- <item>in</item>
-
- <!-- does it really make any sense to have the following here? -->
- <item>255</item>
- <item>UnitedStates</item>
- <item>United States</item>
-
- <item>255</item>
- <item>unitedstates</item>
- <item>United States</item>
-
- <item>255</item>
- <item>visavis</item>
- <item>vis-a-vis</item>
-
- <item>255</item>
- <item>wierd</item>
- <item>weird</item>
-
- <item>255</item>
- <item>wel</item>
- <item>we\'ll</item>
-
- <item>255</item>
- <item>wer</item>
- <item>we\'re</item>
-
- <item>255</item>
- <item>whatd</item>
- <item>what\'d</item>
-
- <item>255</item>
- <item>whatm</item>
- <item>what\'m</item>
-
- <item>255</item>
- <item>whatre</item>
- <item>what\'re</item>
-
- <item>255</item>
- <item>whats</item>
- <item>what\'s</item>
-
- <item>255</item>
- <item>whens</item>
- <item>when\'s</item>
-
- <item>255</item>
- <item>whered</item>
- <item>where\'d</item>
-
- <item>255</item>
- <item>wherell</item>
- <item>where\'ll</item>
-
- <item>255</item>
- <item>wheres</item>
- <item>where\'s</item>
-
- <item>255</item>
- <item>wholl</item>
- <item>who\'ll</item>
-
- <item>255</item>
- <item>whove</item>
- <item>who\'ve</item>
-
- <item>255</item>
- <item>whyd</item>
- <item>why\'d</item>
-
- <item>255</item>
- <item>whyll</item>
- <item>why\'ll</item>
-
- <item>255</item>
- <item>whys</item>
- <item>why\'s</item>
-
- <item>255</item>
- <item>whyve</item>
- <item>why\'ve</item>
-
- <item>255</item>
- <item>wont</item>
- <item>won\'t</item>
-
- <item>255</item>
- <item>yall</item>
- <item>y\'all</item>
-
- <item>255</item>
- <item>youd</item>
- <item>you\'d</item>
-
- </string-array>
-</resources>
diff --git a/java/res/values-es-rUS/strings-appname.xml b/java/res/values-es-rUS/strings-appname.xml
new file mode 100644
index 000000000..5f08afba4
--- /dev/null
+++ b/java/res/values-es-rUS/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado de Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configuración de teclado de Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configuración del corrector ortográfico"</string>
+</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 34ad0a420..d467a0bc2 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado de Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortográfico de Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuración del corrector ortográfico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortográfico usa entradas de tu lista de contactos."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nombres de contacto"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activar correcciones"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para realizar correcciones"</string>
<string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerencias para la palabra siguiente"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar la palabra anterior para mejorar las sugerencias"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicción de la palabra siguiente"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usar la palabra anterior también para predicción."</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugerencias de palabra siguiente"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Ingresa una palabra trazando sus letras."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido de gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palabra de gesto"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar palabra de vista previa flotante al realizar un gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Siguiente"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Volver"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Se activó el modo Mayúscula."</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Se activó el bloqueo de mayúsculas."</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Se desactivó el modo Mayúscula"</string>
diff --git a/java/res/values-es/strings-appname.xml b/java/res/values-es/strings-appname.xml
new file mode 100644
index 000000000..cce9a176d
--- /dev/null
+++ b/java/res/values-es/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado de Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Ajustes del teclado de Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ajustes del corrector ortográfico"</string>
+</resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 3c60aa6e2..802864547 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector de Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector de Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ajustes del corrector ortográfico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Añadir nombres de tu lista de contactos al corrector"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activar nuevas correcciones"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para nuevas correcciones"</string>
<string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Parcial"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresiva"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerir siguiente palabra"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palabra anterior para mejorar las sugerencias"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predecir siguiente palabra"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar también la palabra anterior para realizar la predicción"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugerir siguiente palabra"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Según la palabra anterior"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Entrada de gestos"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Trazar las letras de una palabra para introducirla"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido del gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palabra del gesto"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar la palabra de vista previa flotante al realizar un gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Sig."</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tecla Intro"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Mayúsculas habilitadas"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloqueo de mayúsculas habilitado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Mayúsculas inhabilitadas"</string>
diff --git a/java/res/values-et/strings-appname.xml b/java/res/values-et/strings-appname.xml
new file mode 100644
index 000000000..181d597f9
--- /dev/null
+++ b/java/res/values-et/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidi klaviatuur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidi õigekirjakontroll"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Androidi klaviatuuri seaded"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Õigekirjakontrolli seaded"</string>
+</resources>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 4a592c36b..152c0300b 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androidi klaviatuur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-klaviatuur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Androidi klaviatuuriseaded"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidi õigekirjakontroll"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidi õigekirjakontroll (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Õigekirjakontrolli seaded"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Õigekirjakontroll kasutab teie kontaktisikute loendi sissekandeid"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreeri klahvivajutusel"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Soovita kontaktkirjeid"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Kasuta soovitusteks ja parandusteks nimesid kontaktiloendist"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Uute paranduste lubamine"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Soovituste seadmine uute paranduste jaoks"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automaatne suurtähtede kasutamine"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Pistiksõnaraamatud"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Peamine sõnaraamat"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mõõdukas"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiivne"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Väga agressiivne"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Järgmise sõna soovitused"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Kasuta soovituste täiustamiseks eelmist sõna"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Järgmise sõna ennustus"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Kasuta ennustuseks ka eelmist sõna"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Järgmise sõna soovitused"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Eelmise sõna põhjal"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Liigutusega sisest."</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Sisestage sõna, kirjutades sõna tähed sõrmega"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Näita liigutuse jälge"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Näita liigutuse sõna"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Ujuva sõna eelvaate näitamine koos liigutusega"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : salvestatud"</string>
<string name="label_go_key" msgid="1635148082137219148">"Mine"</string>
<string name="label_next_key" msgid="362972844525672568">"Edasi"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tagasi"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Otsing"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keele vahetamine"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Järgmine"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Eelmine"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tõstuklahv on lubatud"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Suurtähelukk on lubatud"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tõstuklahv on keelatud"</string>
diff --git a/java/res/values-fa/strings-appname.xml b/java/res/values-fa/strings-appname.xml
new file mode 100644
index 000000000..ba2a76ff1
--- /dev/null
+++ b/java/res/values-fa/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"صفحه‌کلید Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"غلط‌گیر املای Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"تنظیمات صفحه‌کلید Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"تنظیمات غلط‌‌ گیر املا"</string>
+</resources>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index bc0e85b77..4b1422984 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -20,51 +20,49 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"صفحه کلید Android"</string>
- <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"صفحه کلید (Android (AOSP"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"تنظیمات صفحه کلید Android"</string>
- <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه های ورودی"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"غلط‌گیر املای Android"</string>
+ <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"صفحه‌کلید (Android (AOSP"</string>
+ <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه‌های ورودی"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"فرمان‌های گزارش‌گیری پژوهش"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"غلط‌گیر املای Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"تنظیمات غلط گیری املایی"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string>
- <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلط‌گیر املا از ورودی‌های لیست مخاطبین شما استفاده میکند"</string>
+ <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلط‌گیر املا از ورودی‌های لیست مخاطبین شما استفاده می‌کند"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string>
<string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string>
<string name="popup_on_keypress" msgid="123894815723512944">"بازشدن با فشار کلید"</string>
<string name="general_category" msgid="1859088467017573195">"کلی"</string>
<string name="correction_category" msgid="2236750915056607613">"تصحیح متن"</string>
- <string name="misc_category" msgid="6894192814868233453">"سایر گزینه ها"</string>
+ <string name="misc_category" msgid="6894192814868233453">"سایر گزینه‌ها"</string>
<string name="advanced_settings" msgid="362895144495591463">"تنظیمات پیشرفته"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"گزینه‌هایی برای حرفه‌ای‌ها"</string>
- <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"تغییر به دیگر روشهای ورودی"</string>
- <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودیهای زبان را نیز پوشش می‌دهد"</string>
+ <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"تغییر به دیگر روش‌های ورودی"</string>
+ <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودی‌های زبان را نیز پوشش می‌دهد"</string>
<string name="suppress_language_switch_key" msgid="8003788410354806368">"کلید تغییر زبان را فشار دهید"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
- <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش فرض"</string>
- <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نام های مخاطب"</string>
+ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش‌فرض"</string>
+ <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نام‌های مخاطب"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"فعال کردن تصحیح مجدد"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"تنظیم پیشنهادات برای تصحیح مجدد"</string>
- <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string>
+ <string name="auto_cap" msgid="1719746674854628252">"بزرگ‌کردن خودکار حروف"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"فرهنگ‌های لغت افزودنی"</string>
<string name="main_dictionary" msgid="4798763781818361168">"فرهنگ‌ لغت اصلی"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"نمایش پیشنهادات تصحیح"</string>
- <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه های پیشنهادی در حین تایپ"</string>
+ <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه‌های پیشنهادی در حین تایپ"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
<string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"نمایش در حالت عمودی"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"همیشه پنهان شود"</string>
<string name="auto_correction" msgid="4979925752001319458">"تصحیح خودکار"</string>
- <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده اند تصحیح می کنند"</string>
+ <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده‌اند تصحیح می‌کنند"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"خاموش"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"متوسط"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"فعال"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"بسیار پرخاشگرانه"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"پیشنهادات کلمه بعدی"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"برای بهبود پیشنهاد از کلمه قبلی استفاده شود"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"پیش‌بینی کلمه بعدی"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"استفاده از کلمه قبلی برای پیش بینی"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"پیشنهادات کلمه بعدی"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"بر اساس کلمه قبلی"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"ورودی اشاره"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"با دنبال کردن حروف یک کلمه، کلمه را وارد کنید"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"نمایش نسخه آزمایشی حرکت"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"نمایش کلمه در طول ورودی حرکتی"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"نمایش کلمه پیش‌نمایش متحرک با حرکت"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ذخیره شد"</string>
<string name="label_go_key" msgid="1635148082137219148">"برو"</string>
<string name="label_next_key" msgid="362972844525672568">"بعدی"</string>
@@ -99,6 +97,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"جستجو"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"نقطه"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"تغییر زبان"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"بعدی"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"قبلی"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift فعال است"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock فعال شد"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift غیرفعال است"</string>
@@ -107,26 +108,26 @@
<string name="spoken_description_mode_phone" msgid="6520207943132026264">"حالت تلفن"</string>
<string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"حالت نمادهای تلفن"</string>
<string name="voice_input" msgid="3583258583521397548">"کلید ورودی صدا"</string>
- <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"در صفحه کلید اصلی"</string>
- <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"در صفحه کلید نمادها"</string>
+ <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"در صفحه‌کلید اصلی"</string>
+ <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"در صفحه‌کلید نمادها"</string>
<string name="voice_input_modes_off" msgid="3745699748218082014">"خاموش"</string>
- <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"میکروفن در صفحه کلید اصلی"</string>
- <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحه کلید نمادها"</string>
+ <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"میکروفن در صفحه‌کلید اصلی"</string>
+ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحه‌کلید نمادها"</string>
<string name="voice_input_modes_summary_off" msgid="63875609591897607">"ورودی صدا غیرفعال است"</string>
- <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش های ورودی"</string>
- <string name="language_selection_title" msgid="1651299598555326750">"زبان های ورودی"</string>
+ <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش‌های ورودی"</string>
+ <string name="language_selection_title" msgid="1651299598555326750">"زبان‌های ورودی"</string>
<string name="select_language" msgid="3693815588777926848">"زبان‌های ورودی"</string>
<string name="hint_add_to_dictionary" msgid="573678656946085380">"برای ذخیره دوباره لمس کنید"</string>
<string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string>
<string name="prefs_enable_log" msgid="6620424505072963557">"فعال کردن بازخورد کاربر"</string>
- <string name="prefs_description_log" msgid="5827825607258246003">"با ارسال خودکار آمارهای کاربرد و گزارش های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید."</string>
- <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه کلید"</string>
- <string name="subtype_en_GB" msgid="88170601942311355">"انگیسی (UK)"</string>
- <string name="subtype_en_US" msgid="6160452336634534239">"انگیسی (US)"</string>
+ <string name="prefs_description_log" msgid="5827825607258246003">"با ارسال خودکار آمارهای کاربرد و گزارش‌های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید."</string>
+ <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه‌کلید"</string>
+ <string name="subtype_en_GB" msgid="88170601942311355">"انگلیسی (بریتانیا)"</string>
+ <string name="subtype_en_US" msgid="6160452336634534239">"انگلیسی (امریکا)"</string>
<string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"انگلیسی (انگلستان) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_en_US" msgid="1362581347576714579">"انگلیسی (ایالات متحده) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_no_language" msgid="141420857808801746">"زبانی موجود نیست"</string>
- <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"هیچ کدام از زبانها (QWERTY)"</string>
+ <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"بدون زبان (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="1177848172397202890">"هیچکدام از زبان‌ها (QWERTZ)"</string>
<string name="subtype_no_language_azerty" msgid="8721460968141187394">"هیچکدام از زبان‌ها (AZERTY)"</string>
<string name="subtype_no_language_dvorak" msgid="3122976737669823935">"هیچکدام از زبان‌ها (Dvorak)"</string>
diff --git a/java/res/values-fi/strings-appname.xml b/java/res/values-fi/strings-appname.xml
new file mode 100644
index 000000000..b2e23d552
--- /dev/null
+++ b/java/res/values-fi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-näppäimistö"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-oikoluku"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-näppäimistön asetukset"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Oikolukuasetukset"</string>
+</resources>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 97002edfe..28eeceddd 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-näppäimistö"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-näppäimistö (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-näppäimistön asetukset"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-oikoluku"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-oikoluku (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Oikoluvun asetukset"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae kontaktien nimiä"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Oikeinkirjoituksen tarkistus käyttää kontaktiluettelosi tietoja."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ehdota yhteystietojen nimiä"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ota korjaukset käyttöön"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Aseta korjausehdotuksia"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Lisäsanakirjat"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pääsanakirja"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Osittainen"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Täysi"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Hyvin aggressiivinen"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Seuraavan sanan ehdotukset"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Paranna ehdotuksia aiempien sanojen avulla"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Seuraavan sanan ennakointi"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Käytä edellistä sanaa myös ennakointiin"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Seuraavan sanan ehdotukset"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Perustuu edelliseen sanan"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Eleiden syöttö"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Syötä sana piirtämällä kirjaimet sormella"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Näytä eleen jälki"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Näytä elesanat"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Näytä eleen yhteydessä kelluva esikatselusana"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Tallennettu"</string>
<string name="label_go_key" msgid="1635148082137219148">"Siirry"</string>
<string name="label_next_key" msgid="362972844525672568">"Seur."</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Haku"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Piste"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Vaihda kieli"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seuraava"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Edellinen"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Vaihto päällä"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock päällä"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Vaihto pois käytöstä"</string>
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml
index 8cf2516a6..5288bd7d1 100644
--- a/java/res/values-fr/donottranslate.xml
+++ b/java/res/values-fr/donottranslate.xml
@@ -25,5 +25,7 @@
<!-- Symbols that should promote magic spaces into real space -->
<string name="phantom_space_promoting_symbols">;:!?([*&amp;@{&lt;&gt;+=|</string>
<!-- Symbols that do NOT separate words -->
- <string name="symbols_excluded_from_word_separators">\'</string>
+ <!-- Note that this is identical to the default value, but since the above ones are different
+ and those variables only make sense together, this is kept here for readability. -->
+ <string name="symbols_excluded_from_word_separators">\'-</string>
</resources>
diff --git a/java/res/values-fr/strings-appname.xml b/java/res/values-fr/strings-appname.xml
new file mode 100644
index 000000000..8e2a6e088
--- /dev/null
+++ b/java/res/values-fr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Clavier Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Correcteur orthographique Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Paramètres du clavier Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Paramètres du correcteur orthographique"</string>
+</resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 285d2226c..a8556e870 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Clavier Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Correcteur orthographique Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Correcteur orthographique Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Paramètre du correcteur orthographique"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activer la recorrection"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Définir des suggestions de recorrection"</string>
<string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Simple"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Proactive"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Très exigeante"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggestions pour le mot suivant"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Améliorer les suggestions grâce au mot précédent"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Prédiction du mot suivant"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utiliser le mot précédent pour la prédiction"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Suggestions pour le mot suivant"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Suggestions basées sur le mot précédent"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Saisie gestuelle"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Saisir un mot en traçant ses lettres avec le doigt"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Afficher le tracé du geste"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Afficher un mot lors du geste"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Afficher un aperçu flottant du mot lors de la saisie gestuelle"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Suiv."</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Entrée"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Point"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Changer de langue"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Touche suivante"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Touche précédente"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Touche Maj activée"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Verrouillage des majuscules activé"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Touche Maj désactivée"</string>
diff --git a/java/res/values-hi/strings-appname.xml b/java/res/values-hi/strings-appname.xml
new file mode 100644
index 000000000..02283af9a
--- /dev/null
+++ b/java/res/values-hi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android कीबोर्ड"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android वर्तनी परीक्षक"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android कीबोर्ड सेटिंग"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"वर्तनी जांच सेटिंग"</string>
+</resources>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 2b1808460..57e9ff451 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android कीबोर्ड"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android कीबोर्ड (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android कीबोर्ड सेटिंग"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्‍प"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android वर्तनी परीक्षक"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android वर्तनी परीक्षक (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"वर्तनी जांच सेटिंग"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"वर्तनी परीक्षक आपकी संपर्क सूची की प्रविष्टियों का उपयोग करता है"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"कुंजी दबाने पर कंपन करता है"</string>
@@ -45,15 +42,13 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"संपर्क नाम सुझाएं"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव और सुधार के लिए संपर्क से नामों का उपयोग करें"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"पुन: सुधार सक्षम करें"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"पुन: सुधार के लि‍ए सुझाव सेट करें"</string>
<string name="auto_cap" msgid="1719746674854628252">"स्‍वत: अक्षर बड़े करना"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन डिक्शनरी"</string>
<string name="main_dictionary" msgid="4798763781818361168">"मुख्‍य डिक्‍शनरी"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझाव दिखाएं"</string>
- <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"लिखते समय सुझाए गए शब्‍द प्रदर्शित करें"</string>
+ <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"लिखते समय सुझाए गए शब्‍द दिखाएं"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"हमेशा दिखाएं"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"र्पोट्रेट मोड पर प्रदर्शित करें"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"र्पोट्रेट मोड पर दिखाएं"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"हमेशा छुपाएं"</string>
<string name="auto_correction" msgid="4979925752001319458">"स्‍वत: सुधार"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar और विराम चिह्न गलत लिखे गए शब्‍दों को स्‍वचालित रूप से ठीक करते हैं"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"साधारण"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"तीव्र"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"बहुत तीव्र"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"अगला शब्‍द सुझाव"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"सुझावों को बेहतर बनाने के लिए पिछले शब्‍द का उपयोग करें"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"अगला शब्‍द पूर्वानुमान"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"पूर्वानुमान के लिए पिछले शब्द का उपयोग करें"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"अगले शब्द सुझाव"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"पिछले शब्द के आधार पर"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"जेस्‍चर इनपुट"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"किसी शब्द के अक्षरों को ट्रेस करके कोई शब्द इनपुट करें"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"जेस्चर ट्रेल दिखाएं"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"जेस्चर शब्द दिखाएं"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"जेस्चर के साथ फ़्लोटिंग पूर्वावलोकन शब्द दिखाएं"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: सहेजा गया"</string>
<string name="label_go_key" msgid="1635148082137219148">"जाएं"</string>
<string name="label_next_key" msgid="362972844525672568">"अगला"</string>
@@ -74,7 +72,7 @@
<string name="label_to_alpha_key" msgid="4793983863798817523">"कखग"</string>
<string name="label_to_symbol_key" msgid="8516904117128967293">"?१२३"</string>
<string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"१२३"</string>
- <string name="label_pause_key" msgid="181098308428035340">"रोकें"</string>
+ <string name="label_pause_key" msgid="181098308428035340">"पॉज़ करें"</string>
<string name="label_wait_key" msgid="6402152600878093134">"प्रतीक्षा करें"</string>
<string name="spoken_use_headphones" msgid="896961781287283493">"ज़ोर से बोली गई पासवर्ड कुंजियां सुनने के लिए हेडसेट प्‍लग इन करें."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"वर्तमान पाठ %s है"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"रिटर्न"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"खोजें"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"बिंदु"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"भाषा स्विच करें"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"अगला"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"पिछला"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift सक्षम किया गया"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock सक्षम किया गया"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift अक्षम किया गया"</string>
@@ -115,7 +116,7 @@
<string name="hint_add_to_dictionary" msgid="573678656946085380">"सहेजने के लिए पुन: स्‍पर्श करें"</string>
<string name="has_dictionary" msgid="6071847973466625007">"शब्‍दकोश उपलब्‍ध है"</string>
<string name="prefs_enable_log" msgid="6620424505072963557">"उपयोगकर्ता फ़ीडबैक सक्षम करें"</string>
- <string name="prefs_description_log" msgid="5827825607258246003">"उपयोग के आंकड़े और क्रैश रिपोर्ट Google को स्वचालित रूप से भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें."</string>
+ <string name="prefs_description_log" msgid="5827825607258246003">"उपयोग के आंकड़े और क्रैश रिपोर्ट Google को अपने आप भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें."</string>
<string name="keyboard_layout" msgid="8451164783510487501">"कीबोर्ड थीम"</string>
<string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेज़ी (यूके)"</string>
<string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेज़ी (यूएस)"</string>
diff --git a/java/res/values-hr/strings-appname.xml b/java/res/values-hr/strings-appname.xml
new file mode 100644
index 000000000..69fa2e9a1
--- /dev/null
+++ b/java/res/values-hr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidova tipkovnica"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidova provjera pravopisa"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Postavke Androidove tipkovnice"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Postavke provjere pravopisa"</string>
+</resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index a59d4698f..cb208804e 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android tipkovnica"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tipkovnica (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Postavke tipkovnice za Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidova provjera pravopisa"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidova provjera pravopisa (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Postavke provjere pravopisa"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Provjera pravopisa upotrebljava unose iz vašeg popisa kontakata"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlaži imena kontakata"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Omogući ponovne ispravke"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Postavite prijedloge za ponovne ispravke"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Rječnici-dodaci"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Glavni rječnik"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Skromno"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivno"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Vrlo agresivno"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Prijedlozi za sljedeću riječ"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Upotrijebi prethodnu riječ radi poboljšanja prijedloga"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predviđanje sljedeće riječi"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Upotrijebite prethodnu riječ i za predviđanje"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Prijedlozi za sljedeću riječ"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na temelju prethodne riječi"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Unos pokretom"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Unos riječi ispisivanjem slova riječi"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži trag pokreta"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Prikaži riječ pokreta"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Prikaži lebdeći pregled riječi uz pokret"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Spremljeno"</string>
<string name="label_go_key" msgid="1635148082137219148">"Idi"</string>
<string name="label_next_key" msgid="362972844525672568">"Dalje"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pretraživanje"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Točka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Promijeni jezik"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Sljedeće"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Prethodno"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Omogućena tipka Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Omogućeno pisanje velikih slova"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Onemogućena tipka Shift"</string>
diff --git a/java/res/values-hu/strings-appname.xml b/java/res/values-hu/strings-appname.xml
new file mode 100644
index 000000000..ad511cfbc
--- /dev/null
+++ b/java/res/values-hu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-billentyűzet"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidos helyesírás-ellenőrző"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-billentyűzet beállításai"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"A helyesírás-ellenőrzés beállításai"</string>
+</resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 0eac1a95b..62d1dd187 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-billentyűzet"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-billentyűzet (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android billentyűzetbeállítások"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidos helyesírás-ellenőrző"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidos helyesírás-ellenőrző (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Helyesírás-ellenőrzés beállításai"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"A helyesírás-ellenőrző használja a névjegyek bejegyzéseit"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés billentyű megnyomása esetén"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Javasolt névjegyek"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Újbóli javítás engedélyezése"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Javaslatok beállítása az újbóli javításokhoz"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Bővítmények: szótárak"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Fő szótár"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mérsékelt"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresszív"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nagyon agresszív"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Következő szóra vonatkozó javaslatok"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Javaslatok fejlesztése az előző szó használatával"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Következő szó előrejelzése"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Az előző szó használata a prediktív bevitelhez is"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Következő szóra vonatkozó javaslatok"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Az előző szó alapján"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Kézi bevitel"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Szó beírása a betűk megrajzolásával"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mozdulat irányának mutatása"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mozdulatot leíró szó mutatása"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mozdulatot leíró szó mutatása lebegő előnézetben"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : mentve"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ugrás"</string>
<string name="label_next_key" msgid="362972844525672568">"Tovább"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Keresés"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pont"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Nyelvek felcserélése"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Következő"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Előző"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift bekapcsolva"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock bekapcsolva"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift kikapcsolva"</string>
diff --git a/java/res/values-in/strings-appname.xml b/java/res/values-in/strings-appname.xml
new file mode 100644
index 000000000..283d69247
--- /dev/null
+++ b/java/res/values-in/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Keyboard Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Pemeriksa ejaan Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Setelan keyboard Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Setelan pemeriksa ejaan"</string>
+</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index e0e92b5bd..922991053 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Keyboard Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Keyboard Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Setelan keyboard Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Pemeriksa ejaan Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pemeriksa ejaan Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setelan pemeriksaan ejaan"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pemeriksa ejaan menggunakan entri dari daftar kontak Anda"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sarankan nama Kontak"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kontak untuk saran dan koreksi"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktifkan koreksi ulang"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setel saran untuk koreksi ulang"</string>
<string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus pengaya"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Saran kata berikutnya"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan kata sebelumnya untuk meningkatkan saran"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Prediksi kata berikutnya"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan kata sebelumnya juga untuk prediksi"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Saran kata berikutnya"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan kata sebelumnya"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Masukan isyarat"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Masukkan kata dengan melacak huruf dari sebuah kata"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Tampilkan jalur isyarat"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Tampilkan kata isyarat"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Tampilkan kata pratinjau melayang dengan isyarat"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Telah disimpan"</string>
<string name="label_go_key" msgid="1635148082137219148">"Buka"</string>
<string name="label_next_key" msgid="362972844525672568">"Berikutnya"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Kembali"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Telusuri"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Ganti bahasa"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Berikutnya"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift diaktifkan"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock diaktifkan"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift dinonaktifkan"</string>
@@ -139,7 +140,7 @@
<string name="enable" msgid="5031294444630523247">"Aktifkan"</string>
<string name="not_now" msgid="6172462888202790482">"Nanti saja"</string>
<string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sudah ada gaya masukan yang sama: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
- <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus studi daya guna"</string>
+ <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode studi daya guna"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Setelan durasi getaran saat tombol ditekan"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Setelan volume suara saat tombol ditekan"</string>
</resources>
diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml
new file mode 100644
index 000000000..8d5b00766
--- /dev/null
+++ b/java/res/values-is/strings.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3551821800439659812) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (4979925752001319458) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (5809665643352206540) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3253961591626441019) -->
+ <skip />
+ <!-- no translation found for gesture_input (3310827802759290774) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (7019742443455085809) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"Áfram"</string>
+ <string name="label_next_key" msgid="362972844525672568">"Næsta"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"Fyrra"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"Lokið"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"Senda"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-it/strings-appname.xml b/java/res/values-it/strings-appname.xml
new file mode 100644
index 000000000..b84896b9d
--- /dev/null
+++ b/java/res/values-it/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tastiera Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Controllo ortografico Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Impostazioni tastiera Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Impostazioni di controllo ortografico"</string>
+</resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index a54958c05..d529b8aad 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastiera Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Controllo ortografico Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Controllo ortografico Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Impostazioni di controllo ortografico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"La funzione di controllo ortografico usa voci dell\'elenco contatti"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggerisci nomi di contatti"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Attiva nuove correzioni"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Imposta suggerimenti per nuove correzioni"</string>
<string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dizionari aggiuntivi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dizionario principale"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Media"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Massima"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Massima"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggerimenti parola successiva"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usa parola precedente per migliorare suggerimenti"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsione parola successiva"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usa anche la parola precedente per la previsione"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Suggerimenti parola successiva"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"In base alla parola precedente"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Inserimento con gesti"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Inserisci una parola tracciandone le lettere"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra traccia con gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostra parola con gesto"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostra parola di anteprima floating con gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string>
<string name="label_go_key" msgid="1635148082137219148">"Vai"</string>
<string name="label_next_key" msgid="362972844525672568">"Avanti"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Invio"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pallino"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambia lingua"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Successivo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Precedente"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maiuscolo attivo"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Blocco maiuscole attivo"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maiuscolo disattivato"</string>
diff --git a/java/res/values-iw/strings-appname.xml b/java/res/values-iw/strings-appname.xml
new file mode 100644
index 000000000..f3f4b674c
--- /dev/null
+++ b/java/res/values-iw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"מקלדת Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"בודק האיות של Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"הגדרות מקלדת Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"הגדרות בדיקת איות"</string>
+</resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 323cac198..5c10716ce 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"מקלדת Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"מקלדת Android ‏(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"הגדרות מקלדת של Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"בודק האיות של Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"בודק האיות של Android ‏(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"הגדרות בדיקת איות"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"בודק האיות משתמש בערכים מרשימת אנשי הקשר שלך"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט בלחיצה על מקשים"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ברירת מחדל"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"הצע שמות של אנשי קשר"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מרשימת אנשי הקשר עבור הצעות ותיקונים"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"הפוך תיקונים חוזרים לפעילים"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"הגדר הצעות עבור תיקונים חוזרים"</string>
<string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"הוספת מילונים"</string>
<string name="main_dictionary" msgid="4798763781818361168">"מילון ראשי"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"מצומצם"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"מחמיר"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"מחמיר מאוד"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"הצעות המילה הבאה"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"השתמש במילה הקודמת כדי לשפר את ההצעות"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"חיזוי המילה הבאה"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"השתמש במילה הקודמת גם עבור חיזוי"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"הצעות המילה הבאה"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"בהתבסס על המילה הקודמת"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"קלט מחווה"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"הזן מילה על ידי החלקת האצבע מאות לאות"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"הצג שובל מחווה"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"הצג מילת מחווה"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"הצג תצוגה מקדימה צפה של המילה בזמן המחווה"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : נשמרה"</string>
<string name="label_go_key" msgid="1635148082137219148">"בצע"</string>
<string name="label_next_key" msgid="362972844525672568">"הבא"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"חזור"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"חיפוש"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"נקודה"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"החלף שפה"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"הבא"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"הקודם"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift מופעל"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock מופעל"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift מושבת"</string>
diff --git a/java/res/values-ja/strings-appname.xml b/java/res/values-ja/strings-appname.xml
new file mode 100644
index 000000000..16c1c05c6
--- /dev/null
+++ b/java/res/values-ja/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidキーボード"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidスペルチェッカー"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Androidキーボードの設定"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"スペルチェックの設定"</string>
+</resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 3a865166f..fae12ab4d 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androidキーボード(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidスペルチェッカー"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidスペルチェッカー(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"スペルチェックの設定"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"スペルチェッカーでは連絡先リストのエントリを使用します"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"候補の連絡先名を表示"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"再修正を有効にする"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"再修正の候補を挿入する"</string>
<string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"アドオン辞書"</string>
<string name="main_dictionary" msgid="4798763781818361168">"メイン辞書"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"中"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"強"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"最も強い"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"次の入力候補"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"直前の単語から入力候補を予測します"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"次の入力候補を予測"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"前の語句も予測に使用"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"次の入力候補"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"前の語句に基づいた入力候補表示"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"ジェスチャー入力"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"単語の文字をトレースして単語を入力"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"ジェスチャートレイルを表示"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"ジェスチャーワードを表示"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"ジェスチャーでプレビューワードをフローティング表示できます"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string>
<string name="label_go_key" msgid="1635148082137219148">"実行"</string>
<string name="label_next_key" msgid="362972844525672568">"次へ"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"検索"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"中点"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"言語を切り替え"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"次へ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"前へ"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift有効"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock有効"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift解除"</string>
diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml
new file mode 100644
index 000000000..fcb666d57
--- /dev/null
+++ b/java/res/values-ka/strings.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3551821800439659812) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (4979925752001319458) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (5809665643352206540) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3253961591626441019) -->
+ <skip />
+ <!-- no translation found for gesture_input (3310827802759290774) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (7019742443455085809) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"გადასვლა"</string>
+ <string name="label_next_key" msgid="362972844525672568">"შემდეგი"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"წინა"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"შესრულებულია"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"გაგზავნა"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-ko/strings-appname.xml b/java/res/values-ko/strings-appname.xml
new file mode 100644
index 000000000..3d7db6136
--- /dev/null
+++ b/java/res/values-ko/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 키보드"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 맞춤법 검사기"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 키보드 설정"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"맞춤법 검사 설정"</string>
+</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 536f06dbd..9049034c3 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 키보드"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 키보드(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 설정"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 맞춤법 검사기"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 맞춤법 검사기(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"맞춤법 검사 설정"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"맞춤법 검사기가 주소록의 항목을 사용합니다."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"주소록 이름 활용"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"재수정 가능 설정"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"재수정 추천어 사전 활성화"</string>
<string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"사전 추가"</string>
<string name="main_dictionary" msgid="4798763781818361168">"기본 사전"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"약"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"중"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"강"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"다음 추천 검색어"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"이전 단어를 사용하여 추천 검색어 개선"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"다음 예상 검색어"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"이전 단어를 사용하여 예상 검색어 표시"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"다음 검색어 추천"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"이전 단어에 기반한 추천"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"동작 입력"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"한번에 문자를 그려서 단어 입력"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"동작 흔적 표시"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"동작 단어 표시"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"동작에 따라 단어 미리보기 표시"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string>
<string name="label_go_key" msgid="1635148082137219148">"이동"</string>
<string name="label_next_key" msgid="362972844525672568">"다음"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"리턴 키"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"검색"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"점"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"언어 전환"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"다음"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"이전"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 사용"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock 사용"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 사용중지"</string>
diff --git a/java/res/values-lt/strings-appname.xml b/java/res/values-lt/strings-appname.xml
new file mode 100644
index 000000000..668d27531
--- /dev/null
+++ b/java/res/values-lt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"„Android“ klaviatūra"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"„Android“ rašybos tikrinimo programa"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"„Android“ klaviatūros nustatymai"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Rašybos tikrinimo nustatymai"</string>
+</resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index a0b8fc87d..f3b900382 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"„Android“ klaviatūra"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"„Android“ klaviatūra (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"„Android“ klaviatūros nustatymai"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"„Android“ rašybos tikrinimo programa"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"„Android“ rašybos tikrinimo programa (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Rašybos tikrinimo nustatymai"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rašybos tikrinimo progr. naudoja įrašus, esančius kontaktų sąraše"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Siūlyti kontaktų vardus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Įdiegti pakartotinius pataisymus"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nustatyti pakartotinio pataisymo pasiūlymus"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildomi žodynai"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pagrindinis žodynas"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Vidutinis"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Atkaklus"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Labai agresyviai"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Kito žodžio pasiūlymai"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Naudoti ankstesnį žodį pasiūlymams patobulinti"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Kito žodžio numatymas"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Numatant naudoti ir ankstesnį žodį"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Kito žodžio pasiūlymai"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pagal ankstesnį žodį"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Įvestis gestais"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Įveskite žodį brėždami jo raides"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Rodyti gestų kelią"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Rodyti gesto žodį"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Rodyti kintantį peržiūros žodį gestu"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: išsaugota"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pradėti"</string>
<string name="label_next_key" msgid="362972844525672568">"Kitas"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Grįžti"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Ieškoti"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Taškas"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keisti kalbą"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Kitas"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Ankstesnis"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Įgalintas antrasis lygis"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Įgalintos didžiosios raidės"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Antrasis lygis išjungtas"</string>
diff --git a/java/res/values-lv/strings-appname.xml b/java/res/values-lv/strings-appname.xml
new file mode 100644
index 000000000..e5657a237
--- /dev/null
+++ b/java/res/values-lv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android tastatūra"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android pareizrakstības pārbaudītājs"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android tastatūras iestatījumi"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Pareizrakstības pārbaudes iestatījumi"</string>
+</resources>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 93727a8ba..55be05218 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android tastatūra"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tastatūra (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android tastatūras iestatījumi"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android pareizrakstības pārbaudītājs"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android pareizrakstības pārbaudītājs (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Pareizrakstības pārbaudes iestatījumi"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pareizrakst. pārbaudītājs lieto ierakstus no kontaktp. saraksta."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ieteikt kontaktp. vārdus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Iespējot atk. labojumus"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Iestatīt atkārtotu labojumu ieteikumus"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildinājumu vārdnīcas"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Galvenā vārdnīca"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mērena"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresīva"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Ļoti radikāla"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Nākamā vārda ieteikumi"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Ieteikumu uzlabošanai izmantot iepriekšējo vārdu"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Nākamā vārda prognozēšana"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Izmantot iepriekšējo vārdu arī prognozēm"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Nākamie vārdu ieteikumi"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Pamatojoties uz iepriekšējo vārdu"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Ievade ar žestu"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Ievadiet vārdu, norādot tā burtus."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Rādīt žesta pēdas"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Rādīt žesta vārdu"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Rādīt peldošo priekšskatījuma vārdu ar žestu"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: saglabāts"</string>
<string name="label_go_key" msgid="1635148082137219148">"Sākt"</string>
<string name="label_next_key" msgid="362972844525672568">"Tālāk"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Ievadīšanas taustiņš"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Meklēt"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkts"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mainīt valodu"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nākamā"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Iepriekšējā"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pārslēgšanas režīms iespējots"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Burtslēgs iespējots"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Pārslēgšanas režīms atspējots"</string>
diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml
new file mode 100644
index 000000000..7f293e4dc
--- /dev/null
+++ b/java/res/values-mk/strings.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3551821800439659812) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (4979925752001319458) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (5809665643352206540) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3253961591626441019) -->
+ <skip />
+ <!-- no translation found for gesture_input (3310827802759290774) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (7019742443455085809) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"Оди"</string>
+ <string name="label_next_key" msgid="362972844525672568">"Следно"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"Претходно"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"Испрати"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-ms/strings-appname.xml b/java/res/values-ms/strings-appname.xml
new file mode 100644
index 000000000..73b553751
--- /dev/null
+++ b/java/res/values-ms/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Papan kekunci Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Penyemak ejaan Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Tetapan papan kekunci Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Tetapan penyemakan ejaan"</string>
+</resources>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index a07c44f2c..4f0f19bfc 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Papan kekunci Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Papan kekunci Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Tetapan papan kekunci Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Penyemak ejaan Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Penyemak ejaan Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Tetapan penyemakan ejaan"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Penyemak ejaan menggunakan entri dari senarai kenalan anda"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Dayakan pembetulan semula"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Tetapkan cadangan untuk pembetulan semula"</string>
<string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus tambahan"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Cadangan perkataan seterusnya"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan perkataan sebelumnya untuk memperbaik cadangan"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Ramalan perkataan seterusnya"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan juga perkataan sebelumnya untuk ramalan"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Cadangan perkataan seterusnya"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Berdasarkan perkataan sebelumnya"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Input gerak isyarat"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Masukkan perkataan dengan menyurih huruf perkataan itu."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Tunjukkan jejak gerak isyarat"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Tunjukkan perkataan gerak isyarat"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Tunjukkan perkataan pratonton terapung dengan gerak isyarat"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pergi"</string>
<string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Carian"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Tukar bahasa"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seterusnya"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kunci anjak didayakan"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kunci huruf besar didayakan"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kunci anjak dilumpuhkan"</string>
diff --git a/java/res/values-nb/strings-appname.xml b/java/res/values-nb/strings-appname.xml
new file mode 100644
index 000000000..56c1c3c71
--- /dev/null
+++ b/java/res/values-nb/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontroll"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Innstillinger for Android-tastatur"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Innstillinger for stavekontroll"</string>
+</resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 75c1295a9..f603dfaa1 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontroll"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontroll (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Innstillinger for stavekontroll"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruker oppføringer fra kontaktlisten din"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå kontaktnavn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktiver korrigeringer"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angi forslag for korrigeringer"</string>
<string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilleggsordbøker"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hovedordliste"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Omfattende"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veldig aggressiv"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til rettelser av neste ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Bruk forrige ord til å forbedre forslagene"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Forslag til neste ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Bruk forrige ord også for forslag"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Forslag til neste ord"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Basert på det forrige ordet"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Bevegelseskommandoer"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Angi et ord ved å skrive bokstavene på skjermen med fingeren"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis bevegelsesspor"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Vis bevegelsesord"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Vis flytende forhåndsvisningsord under bevegelser"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string>
<string name="label_go_key" msgid="1635148082137219148">"Utfør"</string>
<string name="label_next_key" msgid="362972844525672568">"Neste"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Søk"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Prikk"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Bytt språk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Neste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift er aktivert"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock er aktivert"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift er deaktivert"</string>
diff --git a/java/res/values-nl/strings-appname.xml b/java/res/values-nl/strings-appname.xml
new file mode 100644
index 000000000..ee288efbb
--- /dev/null
+++ b/java/res/values-nl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-toetsenbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Spellingcontrole van Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Instellingen voor Android-toetsenbord"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Instellingen voor spellingcontrole"</string>
+</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index a7d499bd0..33816f870 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-toetsenbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Spellingcontrole van Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Spellingcontrole van Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Instellingen voor spellingcontrole"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"De spellingcontrole gebruikt items uit uw contactenlijst"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Contactnamen suggereren"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Verbeteringen inschakelen"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Suggesties instellen voor verbeteringen"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Woordenboeken toevoegen"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Algemeen woordenboek"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Normaal"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressief"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zeer agressief"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Volgende woordsuggesties"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Vorig woord gebruiken om suggesties te verbeteren"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Volgende woordvoorspelling"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Het voorgaande woord ook voor voorspelling gebruiken"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Suggesties voor volgend woord"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Op basis van het vorige woord"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Invoer met gebaren"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Voer een woord in door de letters van een woord te volgen"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Gebarenspoor weergeven"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Woord met gebaar weergeven"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zwevend voorbeeldwoord met gebaar weergeven"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string>
<string name="label_go_key" msgid="1635148082137219148">"Start"</string>
<string name="label_next_key" msgid="362972844525672568">"Verder"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Zoeken"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Stip"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Taal wijzigen"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ingeschakeld"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock ingeschakeld"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift uitgeschakeld"</string>
diff --git a/java/res/values-pl/strings-appname.xml b/java/res/values-pl/strings-appname.xml
new file mode 100644
index 000000000..e460644a3
--- /dev/null
+++ b/java/res/values-pl/strings-appname.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klawiatura Android"</string>
+ <!-- no translation found for spell_checker_service_name (6268342166872202903) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (7470027018752707691) -->
+ <skip />
+ <!-- no translation found for android_spell_checker_settings (8397842018475560441) -->
+ <skip />
+</resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index bf27782b6..7e15566ac 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klawiatura Androida (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Słownik Androida"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Sprawdzanie pisowni na Androidzie (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ustawienia sprawdzania pisowni"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Sprawdzanie pisowni bierze pod uwagę wpisy z listy kontaktów."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proponuj osoby z kontaktów"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Włącz poprawki"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ustaw sugestie poprawek"</string>
<string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatkowe słowniki"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Słownik główny"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Umiarkowana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresywna"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Bardzo agresywna"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestie kolejnych słów"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Używaj poprzedniego wyrazu, by polepszyć sugestie"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Przewidywanie następnego wyrazu"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Przewiduj również na podstawie poprzedniego słowa"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugestie kolejnych słów"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podstawie poprzedniego słowa"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Wprowadzanie gestem"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Podaj słowo, pisząc litery palcem"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Pokazuj ślad gestu"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Podpowiadaj przy pisaniu gestem"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Pokazuj pływający podgląd słowa podczas pisania gestem"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Dalej"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Szukaj"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Przełącz język"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Dalej"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Wstecz"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift włączony"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock włączony"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift wyłączony"</string>
diff --git a/java/res/values-pt-rPT/strings-appname.xml b/java/res/values-pt-rPT/strings-appname.xml
new file mode 100644
index 000000000..1b88acb69
--- /dev/null
+++ b/java/res/values-pt-rPT/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado do Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Verificador ortográfico do Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Definições de teclado do Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Definições da verificação ortográfica"</string>
+</resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index ccb042e47..c2cb4a873 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificador ortográfico do Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificador ortográfico do Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Definições da verificação ortográfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico utiliza entradas da sua lista de contactos"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de Contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ativar correções"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para correções"</string>
<string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários extras"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiva"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões da palavra seguinte"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizar palavra anterior para melhorar sugestões"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar a palavra anterior também para predição"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugestões da palavra seguinte"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Introd. por gestos"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra, desenhando as letras de uma palavra"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra do gesto"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar visualização flutuante da palavra com gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mudar de idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seguinte"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string>
diff --git a/java/res/values-pt/strings-appname.xml b/java/res/values-pt/strings-appname.xml
new file mode 100644
index 000000000..d78786d63
--- /dev/null
+++ b/java/res/values-pt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado do Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corretor ortográfico do Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configurações de teclado do Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configurações de verificação ortográfica"</string>
+</resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 7d64759d8..b4830675b 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corretor ortográfico do Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corretor ortográfico do Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configurações de verificação ortográfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico usa entradas de sua lista de contatos"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de contato"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ativar recorreções"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para recorreções"</string>
<string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários complementares"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressivo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões p/ palavra seguinte"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palavra anterior para melhorar as sugestões"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use também a palavra anterior para prever"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugestões para a palavra seguinte"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Com base na palavra anterior"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Entrada por gesto"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Introduza uma palavra traçando suas letras"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Mostrar palavra do gesto"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Mostrar visualização flutuante da palavra com gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Voltar"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Alterar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Próximo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 18741f816..25d58732d 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -20,18 +20,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastatura Android"</string>
<!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
<skip />
- <string name="english_ime_settings" msgid="6661589557206947774">"Parameters da la tastatura Android"</string>
<!-- no translation found for english_ime_input_options (3909945612939668554) -->
<skip />
- <!-- no translation found for spell_checker_service_name (7338064335159755926) -->
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
<skip />
<!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
<skip />
- <!-- no translation found for android_spell_checker_settings (5822324635435443689) -->
- <skip />
<!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
<skip />
<!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
@@ -65,10 +61,6 @@
<skip />
<!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
<skip />
- <!-- no translation found for enable_span_insert (7204653105667167620) -->
- <skip />
- <!-- no translation found for enable_span_insert_summary (2947317657871394467) -->
- <skip />
<string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string>
<!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
<skip />
@@ -96,13 +88,19 @@
<skip />
<!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
<skip />
- <!-- no translation found for bigram_suggestion (8169311444438922902) -->
+ <!-- no translation found for bigram_prediction (5809665643352206540) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3253961591626441019) -->
+ <skip />
+ <!-- no translation found for gesture_input (3310827802759290774) -->
<skip />
- <!-- no translation found for bigram_suggestion_summary (6635527607242625713) -->
+ <!-- no translation found for gesture_input_summary (7019742443455085809) -->
<skip />
- <!-- no translation found for bigram_prediction (3216364899483135294) -->
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
<skip />
- <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+ <!-- no translation found for gesture_floating_preview_text (6859416520117939680) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (3333754126434989709) -->
<skip />
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Memorisà"</string>
<string name="label_go_key" msgid="1635148082137219148">"Dai"</string>
@@ -159,6 +157,12 @@
<skip />
<!-- no translation found for spoken_description_dot (40711082435231673) -->
<skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
<!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
<skip />
<!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
diff --git a/java/res/values-ro/strings-appname.xml b/java/res/values-ro/strings-appname.xml
new file mode 100644
index 000000000..dfa642204
--- /dev/null
+++ b/java/res/values-ro/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tastatură Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Verificator ortografic Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Setările tastaturii Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Setările de verificare ortografică"</string>
+</resources>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 0c4d8bcfb..e47ed63f4 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastatură Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastatură Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Setările tastaturii Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificator ortografic Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificator ortografic Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setări de verificare ortografică"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Verificatorul ortografic utilizează intrări din lista de contacte"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugeraţi nume din Agendă"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activaţi rectificările"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setaţi sugestii pentru rectificări"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicţionare suplimentare"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicţionar principal"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderată"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivă"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Foarte exigentă"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestii pentru cuvântul următor"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestiile"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicţia cuvântului următor"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Se utilizează şi cuvântul precedent pentru predicţii"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sugestii pentru cuvântul următor"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Bazate pe cuvântul precedent"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Utilizaţi gesturi"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Introduceţi un cuvânt desenând literele acestuia"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Se afişează urma gestului"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Sugestie cuvinte la gesturi"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Se afişează previzualizarea cuvântului flotant la gesturi"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Înainte"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Căutaţi"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punct"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Schimbaţi limba"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Înainte"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Înapoi"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tasta Shift a fost activată"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Tasta Caps Lock a fost activată"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tasta Shift a fost dezactivată"</string>
diff --git a/java/res/values-ru/strings-appname.xml b/java/res/values-ru/strings-appname.xml
new file mode 100644
index 000000000..5db1d0bc9
--- /dev/null
+++ b/java/res/values-ru/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавиатура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Проверка правописания Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Настройки клавиатуры Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Настройки проверки правописания"</string>
+</resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index f76cc8eaf..f780650ab 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Клавиатура Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Проверка правописания Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Проверка правописания Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройка проверки правописания"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Обращаться к списку контактов при проверке правописания"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Подсказывать имена"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказывать исправления на основе имен из списка контактов"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Автоисправление"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показывать варианты исправления"</string>
<string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Дополнительные словари"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основной словарь"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умеренное"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активное"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Очень активно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Следующие варианты"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Использовать предыдущее слово, чтобы исправить предложенные варианты"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Следующая подсказка"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Использовать предыдущее слово для прогнозирования"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Подсказка для следующего слова"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Подсказки, основанные на предыдущих словах"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Ввод жестами"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Перемещайте палец от буквы к букве, чтобы составить слово"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Рисовать линию"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Подсказывать слово"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показывать подсказку во время ввода"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: сохранено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Поиск"</string>
<string name="label_next_key" msgid="362972844525672568">"Далее"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Клавиша \"Ввод\""</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Поиск"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Сменить язык"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далее"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Верхний регистр включен"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock включен"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Верхний регистр отключен"</string>
diff --git a/java/res/values-sk/strings-appname.xml b/java/res/values-sk/strings-appname.xml
new file mode 100644
index 000000000..5b5590000
--- /dev/null
+++ b/java/res/values-sk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klávesnica Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavenia klávesnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavenia kontroly pravopisu"</string>
+</resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 44eecdc74..4aa0c5ce8 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klávesnica Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnica Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavenia klávesnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavenia kontroly pravopisu"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používa záznamy z vášho zoznamu kontaktov"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhnúť mená kontaktov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Povoliť opätovné opravy"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastaviť návrhy pre opätovné opravy"</string>
<string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplnkové slovníky"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hlavný slovník"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mierne"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresívne"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veľmi agresívne"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Návrhy ďalšieho slova"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Na zlepšenie návrhov použiť predchádzajúce slovo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Odhad ďalšieho slova"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použiť predchádzajúce slovo aj pre predpoveď"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Návrhy ďalšieho slova"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na základe predchádzajúceho slova"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Zadávanie gestami"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Napíšte slovo zadaním jeho písmen"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovať stopu gesta"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Zobrazovať slovo gesta"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Zobrazovať plávajúcu ukážku slova gesta"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uložené"</string>
<string name="label_go_key" msgid="1635148082137219148">"Hľadať"</string>
<string name="label_next_key" msgid="362972844525672568">"Ďalej"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Hľadať"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Bodka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Prepnúť jazyk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Ďalšie"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Predchádzajúce"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kláves Shift je povolený"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kláves Caps Lock je povolený"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kláves Shift je zakázaný"</string>
diff --git a/java/res/values-sl/strings-appname.xml b/java/res/values-sl/strings-appname.xml
new file mode 100644
index 000000000..fd303d8dd
--- /dev/null
+++ b/java/res/values-sl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tipkovnica Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Črkovalnik za Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavitve tipkovnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavitve preverjanja črkovanja"</string>
+</resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index ce7dc70bb..d21e585c3 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tipkovnica Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tipkovnica Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavitve tipkovnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Črkovalnik za Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Črkovalnik za Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavitve preverjanja črkovanja"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Črkovalnik uporablja vnose s seznama stikov"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlagaj imena stikov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Omogoči vnovične popravke"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavitev predlogov za vnovične popravke"</string>
<string name="auto_cap" msgid="1719746674854628252">"Samod. velike začetnice"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatni slovarji"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Glavni slovar"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Zmerno"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Strogo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zelo strogo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Predlogi naslednje besede"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Predloge izboljšaj s prejšnjo besedo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predvidevanje naslednje besede"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Uporabi prejšnjo besedo tudi za predvidevanje"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Predlogi za naslednjo besedo"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Na podlagi prejšnje besede"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Vnos s potezo"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Vnašanje besede z drsenjem po zaslonu od črke do črke"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži pot poteze"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Pokaži besedo za potezo"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Pokaži plavajoči predogled besede za potezo"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string>
<string name="label_next_key" msgid="362972844525672568">"Naprej"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Vračalka"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Iskanje"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pika"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Preklop jezika"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Naprej"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Nazaj"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Način »Shift« je omogočen"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Način »Caps Lock« je omogočen"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Način »Shift« je onemogočen"</string>
diff --git a/java/res/values-sr/strings-appname.xml b/java/res/values-sr/strings-appname.xml
new file mode 100644
index 000000000..449fe551a
--- /dev/null
+++ b/java/res/values-sr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android тастатура"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android провера правописа"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Подешавања Android тастатуре"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Подешавања провере правописа"</string>
+</resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 6d2899b47..3b209b305 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android тастатура"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android тастатура (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Подешавања Android тастатуре"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android провера правописа"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android провера правописа (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Подешавања провере правописа"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Контролор правописа користи уносе са листе контаката"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложи имена контаката"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Омогући поновне исправке"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Подешавање предлога за поновне исправке"</string>
<string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Помоћни речници"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Главни речник"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Веома агресивно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Предлози за следећу реч"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Користи претходну реч за побољшање предлога"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Предвиђање следеће речи"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Користи претходну реч и за предвиђање"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Предлози за следећу реч"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основу претходне речи"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Унос покретом"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Унесите реч исписивањем слова речи"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Прикажи траг покрета"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Прикажи реч уз покрет"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Приказ плутајућег прегледа речи уз покрет"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Сачувано"</string>
<string name="label_go_key" msgid="1635148082137219148">"Иди"</string>
<string name="label_next_key" msgid="362972844525672568">"Следеће"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Претражи"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Тачка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пребаци језик"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Следеће"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Претходно"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift је омогућен"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock је омогућен"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift је онемогућен"</string>
diff --git a/java/res/values-sv/strings-appname.xml b/java/res/values-sv/strings-appname.xml
new file mode 100644
index 000000000..9b4a7dbd1
--- /dev/null
+++ b/java/res/values-sv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androids tangentbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Stavningskontroll i Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Inställningar för Androids tangentbord"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Inställningar för stavningskontroll"</string>
+</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 75e80d42d..71ba6204d 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androids tangentbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Stavningskontroll i Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Stavningskontroll i Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Inställningar för stavningskontroll"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"I stavningskontrollen används poster från kontaktlistan"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Föreslå kontaktnamn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktivera omkorrigeringar"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ställ in förslag för omkorrigeringar"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilläggsordlistor"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Huvudordlistan"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Måttlig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Mycket aggressivt"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Föreslå nästa ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Förbättra förslagen med föregående ord"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Förutspå nästa ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Använd även föregående ord för att ge förslag"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Föreslå nästa ord"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Baserat på föregående ord"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Gestinmatning"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Skriv ett ord för hand genom att rita bokstäverna i ordet"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Visa spår efter rörelse"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Visa svävande ord för rörelse"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Visa förhandsgranskning av svävande ord med rörelse"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string>
<string name="label_go_key" msgid="1635148082137219148">"Kör"</string>
<string name="label_next_key" msgid="362972844525672568">"Nästa"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Retur"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Sök"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Byt språk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nästa"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Föregående"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift är aktiverat"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock är aktiverat"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift är inaktiverat"</string>
diff --git a/java/res/values-sw/strings-appname.xml b/java/res/values-sw/strings-appname.xml
new file mode 100644
index 000000000..51de0a6b8
--- /dev/null
+++ b/java/res/values-sw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Kibodi ya Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kikagua tahajia cha Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Mipangilio ya kibodi ya Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Mipangilio ya kukagua tahajia"</string>
+</resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index da51cb3b9..ec960bcd4 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Kibodi ya Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Kicharazio cha Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Mipangilio ya kibodi ya Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kikagua tahajia cha Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kikagua tahajia cha Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mipangilio ya kukagua sarufi"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya wasiliani"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kikagua tahajia hutumia ingizo kutoka kwa orodha yako ya anwani"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Chaguo-msingi"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Pendekeza majini ya Anwani"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Tumia majina kutoka kwa Anwani kwa mapendekezo na marekebisho"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Wezesha masahihisho mapya"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Weka mapendekezo kwa ajili ya kusahihisha upya"</string>
<string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Nyongeza za kamusi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamusi kuu"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ya wastani"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Ya hima"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Changamfu zaidi"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Mapendekezo ya neno lifuatalo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Tumia neno la awali ili kuboresha mapendekezo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Utabiri wa neno lifuatalo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Tumia neno la awali pia kwa udadisi"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Mapendekezo ya neno lifuatalo"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Kulingana na neno la awali"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Ingizo la ishara"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Ingiza neno kwa kufuatilia herufi za neno"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Onyesha njia ya ishara"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Onyesha neno la ishara"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Onyesha neno hakiki linaloelea lililo na ishara"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Imehifadhiwa"</string>
<string name="label_go_key" msgid="1635148082137219148">"Nenda"</string>
<string name="label_next_key" msgid="362972844525672568">"Ifuatayo"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Rudi"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Tafuta"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Nukta"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Badili lugha"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Inayofuata"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Iliyotangulia"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift imewezeshwa"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock imewezeshwa"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift imelemazwa"</string>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 2f35d9ae5..e296623b2 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -19,6 +19,8 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET7} -->
+ <integer name="config_device_form_factor">1</integer>
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
@@ -32,8 +34,9 @@
<string name="config_default_keyboard_theme_index" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
+ <dimen name="config_key_hysteresis_distance">40.0dp</dimen>
<bool name="config_sliding_key_input_enabled">false</bool>
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
false -->
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 5fcaeeb41..346fa9979 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -19,6 +19,8 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET10} -->
+ <integer name="config_device_form_factor">2</integer>
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
@@ -30,7 +32,7 @@
<string name="config_default_keyboard_theme_index" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
<bool name="config_sliding_key_input_enabled">false</bool>
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
diff --git a/java/res/values-th/strings-appname.xml b/java/res/values-th/strings-appname.xml
new file mode 100644
index 000000000..7fc7e3e43
--- /dev/null
+++ b/java/res/values-th/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"แป้นพิมพ์แอนดรอยด์"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"การตรวจสอบการสะกดของแอนดรอยด์"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"การตั้งค่าแป้นพิมพ์แอนดรอยด์"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"การตั้งค่าการตรวจสอบการสะกด"</string>
+</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index da20d6607..4b8b45c0b 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"แป้นพิมพ์ Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"การตั้งค่าแป้นพิมพ์ Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"แอนดรอยด์ตรวจสอบการสะกด"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"แอนดรอยด์ตรวจสอบการสะกด (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"การตั้งค่าการตรวจสอบการสะกด"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"เครื่องมือตรวจการสะกดใช้รายการจากรายชื่อติดต่อของคุณ"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"แนะนำชื่อผู้ติดต่อ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"เปิดใช้งานการแก้ไขซ้ำ"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"ตั้งค่าคำแนะนำสำหรับการแก้ไขซ้ำ"</string>
<string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"พจนานุกรม Add-On"</string>
<string name="main_dictionary" msgid="4798763781818361168">"พจนานุกรมหลัก"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ปานกลาง"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"เข้มงวด"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"เข้มงวดมาก"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"คำแนะนำสำหรับคำถัดไป"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ใช้คำก่อนหน้านี้เพื่อปรับปรุงคำแนะนำ"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"การคาดคะเนคำถัดไป"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"ใช้คำก่อนหน้านี้สำหรับการคาดคะเน"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"คำแนะนำสำหรับคำถัดไป"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"ตามคำก่อนหน้า"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"ป้อนท่าทางสัมผัส"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"ป้อนคำโดยลากนิ้วเป็นรูปตัวอักษรที่อยู่ในคำ โดยไม่ยกนิ้วขึ้น"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"แสดงรอยทางเดินของท่าทางสัมผัส"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"แสดงคำแนะนำท่าทางสัมผัส"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"แสดงตัวอย่างคำแนะนำพร้อมด้วยท่าทางสัมผัส"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string>
<string name="label_go_key" msgid="1635148082137219148">"ไป"</string>
<string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"ค้นหา"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"เครื่องหมายจุด"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"เปลี่ยนภาษา"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"ถัดไป"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"ก่อนหน้า"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"เปิดใช้งาน Shift แล้ว"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"เปิดใช้งาน Caps Lock แล้ว"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ปิดใช้งาน Shift แล้ว"</string>
diff --git a/java/res/values-tl/strings-appname.xml b/java/res/values-tl/strings-appname.xml
new file mode 100644
index 000000000..fd2b3f55b
--- /dev/null
+++ b/java/res/values-tl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Keyboard ng Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Spell checker ng Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Mga setting ng keyboard ng Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Mga setting ng pag-spell check"</string>
+</resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 7d365b2dc..a2b46a66b 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Mga setting ng Android keyboard"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Pang-check ng pagbabaybay ng Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pang-check ng pagbabaybay ng Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mga setting ng pang-check ng pagbabaybay"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Gumagamit pang-check pagbabaybay entry sa iyong listahan contact"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Mungkahi pangalan Contact"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Paganahin ang mga muling pagtatama"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Magtakda ng mga suhestyon para sa mga muling pagtatama"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Mga diksyunaryo na add-on"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pangunahing diksyunaryo"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresibo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Napaka-agresibo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Mga paghuhula sa susunod na salita"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gamitin ang naunang salita para mapahusay ang mga suhestiyon"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Paghuhula sa susunod na salita"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gamitin ang nakaraang salita para din sa hula"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Mga suhestiyon sa susunod na salita"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Batay sa nakaraang salita"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Pagpasok ng galaw"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Magpasok ng salita sa gamit ang pagsulat sa mga titik ng salita"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Ipakita ang trail ng galaw"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Ipakita ang salita ng galaw"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Ipakita ang lumulutang na preview ng salita na may galaw"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string>
<string name="label_go_key" msgid="1635148082137219148">"Punta"</string>
<string name="label_next_key" msgid="362972844525672568">"Susunod"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Bumalik"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Paghahanap"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Tuldok"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Magpalit ng wika"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Susunod"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Nakaraan"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pinagana ang shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Pinagana ang caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Hindi pinagana ang shift"</string>
diff --git a/java/res/values-tr/strings-appname.xml b/java/res/values-tr/strings-appname.xml
new file mode 100644
index 000000000..f5e36d2e8
--- /dev/null
+++ b/java/res/values-tr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android klavyesi"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android yazım denetleyici"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android klavye ayarları"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Yazım denetimi ayarları"</string>
+</resources>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index b2f73912e..f9eb59831 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android klavye (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android yazım denetleyici"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android yazım denetleyici (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Yazım denetimi ayarları"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Yazım denetleyici, kişi listenizdeki girişleri kullanır"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kişi Adları öner"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Düzeltmeleri etkinleştir"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Yeniden düzeltmeler için önerileri ayarla"</string>
<string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük fark yap"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Ek sözlükler"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Ana sözlük"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ölçülü"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Çok geniş ölçekte"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sonraki kelime önerileri"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Önerileri geliştirmek için önceki kelimeyi kullan"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Sonraki kelime tahmini"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Önceki kelimeyi de tahmin için kullan"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Sonraki kelime önerileri"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Önceki kelimeye dayanarak"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Hareket girişi"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Bir kelimeyi harflerini izleyerek girin"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Hareket izini göster"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Hareketle yazılan kelimeyi göster"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Hareketle kayan önizleme kelimesini göster"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string>
<string name="label_go_key" msgid="1635148082137219148">"Git"</string>
<string name="label_next_key" msgid="362972844525672568">"İleri"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Ara"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Nokta"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Dili değiştir"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Sonraki"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Önceki"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Üst karakter etkin"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Büyük harf kilidi etkin"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Üst karakter devre dışı"</string>
diff --git a/java/res/values-uk/strings-appname.xml b/java/res/values-uk/strings-appname.xml
new file mode 100644
index 000000000..fdbb89fd9
--- /dev/null
+++ b/java/res/values-uk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавіатура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Засіб перевірки орфографії Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Налаштування клавіатури Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налаштування перевірки орфографії"</string>
+</resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index e994e1a15..d62f42082 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавіатура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіатура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Налашт-ня клавіат. Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Засіб перевірки орфографії Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Засіб перевірки орфографії Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налаштування перевірки орфографії"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Програма перевірки правопису використ. записи зі списку контактів"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр. при натисканні клавіш"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Пропон. імена контактів"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Увімкнути виправлення"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показувати варіанти автовиправлень"</string>
<string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Додані словники"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основний словник"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Помірне"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активне"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Дуже активне"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Пропозиції наступного слова"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Використати попереднє слово для покращення пропозицій"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Передбачення наступного слова"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Використовувати попереднє слово також як передбачений запит"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Пропозиції наступного слова"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"На основі попереднього слова"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Введення жестами"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Введіть слово, малюючи літери, з яких воно складається"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Показувати слід жестів"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Показувати слово для жесту"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Показувати спливаючий перегляд слова під час жесту"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : збережено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Іти"</string>
<string name="label_next_key" msgid="362972844525672568">"Далі"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Клавіша Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Крапка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Змінити мову"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далі"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift увімкнено"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock увімкнено"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift вимкнено"</string>
diff --git a/java/res/values-vi/strings-appname.xml b/java/res/values-vi/strings-appname.xml
new file mode 100644
index 000000000..6e32d0370
--- /dev/null
+++ b/java/res/values-vi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Bàn phím Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Trình kiểm tra chính tả Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Cài đặt bàn phím Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Cài đặt kiểm tra chính tả"</string>
+</resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 753af1840..13f51ad77 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Bàn phím Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Bàn phím Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Cài đặt bàn phím Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Trình kiểm tra chính tả Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Trình kiểm tra chính tả Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Cài đặt kiểm tra chính tả"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Trình kiểm tra chính tả sử dụng các mục nhập từ danh sách liên hệ của bạn"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Đề xuất tên liên hệ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Bật sửa đổi lại"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Đặt đề xuất cho các sửa đổi lại"</string>
<string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Thêm từ điển"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Từ điển chính"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Đơn giản"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Linh hoạt"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Rất linh hoạt"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Đề xuất từ tiếp theo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sử dụng từ trước đó để cải tiến đề xuất"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Dự đoán từ tiếp theo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Cũng sử dụng từ trước đó để dự đoán"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Đề xuất từ tiếp theo"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Dựa trên từ trước đó"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Nhập bằng cử chỉ"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Nhập từ bằng cách lần theo các chữ cái của từ"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Hiển thị vệt cử chỉ"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Hiển thị từ theo cử chỉ"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Hiển thị từ xem trước nổi bằng cử chỉ"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Đã lưu"</string>
<string name="label_go_key" msgid="1635148082137219148">"Tìm"</string>
<string name="label_next_key" msgid="362972844525672568">"Tiếp theo"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Quay lại"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Tìm kiếm"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Dấu chấm"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Chuyển ngôn ngữ"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Tiếp theo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Trước"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Đã bật Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Đã bật Caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Đã tắt Shift"</string>
diff --git a/java/res/values-zh-rCN/strings-appname.xml b/java/res/values-zh-rCN/strings-appname.xml
new file mode 100644
index 000000000..f5e12fd5e
--- /dev/null
+++ b/java/res/values-zh-rCN/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 键盘"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 拼写检查工具"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 键盘设置"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"拼写检查设置"</string>
+</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index efde54103..d8fec34ea 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 键盘"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 键盘 (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘设置"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼写检查工具"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼写检查工具 (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼写检查设置"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼写检查工具会使用您的联系人列表中的条目"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"按键振动"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"联系人姓名建议"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用联系人中的姓名提供建议和更正"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"允许再次更正"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"设置建议以用于再次更正"</string>
<string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"附加词典"</string>
<string name="main_dictionary" msgid="4798763781818361168">"主词典"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"小改"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"大改"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"改动极大"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"下一字词建议"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"使用上一字词改进建议"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"下一字词预测"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"结合前一个字词进行预测"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"下一字词建议"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"根据上一个字词提供建议"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"手势输入"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"连笔书写输入字词"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"显示手势轨迹"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"显示手势文字"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"手势划动输入时显示漂浮预览文字"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
<string name="label_go_key" msgid="1635148082137219148">"开始"</string>
<string name="label_next_key" msgid="362972844525672568">"下一步"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"搜索"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"点"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"切换语言"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"下一个"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"上一个"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 模式已启用"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大写锁定已启用"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 模式已停用"</string>
diff --git a/java/res/values-zh-rTW/strings-appname.xml b/java/res/values-zh-rTW/strings-appname.xml
new file mode 100644
index 000000000..8cc663826
--- /dev/null
+++ b/java/res/values-zh-rTW/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 鍵盤"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 拼字檢查"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 鍵盤設定"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"拼字檢查設定"</string>
+</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 51df022aa..120ae330f 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 鍵盤"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 鍵盤 (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤設定"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼字檢查"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼字檢查 (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼字檢查設定"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人清單項目"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"根據「聯絡人」名稱提供建議與修正"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"啟用重新更正"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"設定建議供重新更正"</string>
<string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"外掛字典"</string>
<string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"更正範圍小"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"更正範圍大"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"更正範圍極大"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"下一個字詞建議"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"根據前一個字詞找出更適合的建議"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"下一個字詞預測"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"同樣使用前一個字詞進行預測"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"下一個字詞建議"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"根據上一個字詞產生"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"手勢輸入"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"以手指寫出字詞中字母的方式來輸入字詞"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"顯示手勢軌跡"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"顯示手勢字詞"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"使用手勢輸入時顯示浮動預覽字詞"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
<string name="label_go_key" msgid="1635148082137219148">"開始"</string>
<string name="label_next_key" msgid="362972844525672568">"繼續"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"點"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"切換語言"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"下一步"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"上一步"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 鍵已啟用"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大寫鎖定已啟用"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 鍵已停用"</string>
diff --git a/java/res/values-zu/strings-appname.xml b/java/res/values-zu/strings-appname.xml
new file mode 100644
index 000000000..a0fb51716
--- /dev/null
+++ b/java/res/values-zu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Ikhibhodi ye-Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Isihloli sokupela se-Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Izilungiselelo zekhibhodi ye-Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Izilungiselelo zokuhlola ukupela"</string>
+</resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index d3f80e42a..2b2a5872c 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Ikhibhodi ye-Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Ikhibhodi ye-Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Izilungiselelo zekhibhodi ye-Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Isihloli sokupela se-Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Isihloli sokupela se-Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Izilungiselelo zokuhlola ukupela"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Isihloli sokupela sisebenzisa okungenayo kusuka kuhlu lalabo oxhumana nabo"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string>
@@ -45,8 +42,6 @@
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Okuzenzakalelayo"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sikisela amagama Othintana nabo"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Amagama abasebenzisi kusuka Kothintana nabo bokusikisela nokulungisa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Vumela ukulungiswa kabusha"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setha iziphakamiso zokulungisa kabusha"</string>
<string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Faka izichazamazwi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Isichazamazwi sakho ngqangi"</string>
@@ -61,10 +56,13 @@
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Thobekile"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Bukhali"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nobudlova kakhulu"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Iziphakamiso zegama elilandelayo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sebenzisa igama elandulele ukuthuthukisa iziphakamiso"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Ukuqagela kwegama elilandelayo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Sebenzisa igama langaphambilini ukuze uqagele"</string>
+ <string name="bigram_prediction" msgid="5809665643352206540">"Iziphakamiso zegama elilandelayo"</string>
+ <string name="bigram_prediction_summary" msgid="3253961591626441019">"Ngokususela egameni langaphambilini"</string>
+ <string name="gesture_input" msgid="3310827802759290774">"Okokufaka kokuthinta"</string>
+ <string name="gesture_input_summary" msgid="7019742443455085809">"Faka igama ngokulandela ngomkhondo izinhlamvu zegama"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Bonisa i-trail yokuthinta"</string>
+ <string name="gesture_floating_preview_text" msgid="6859416520117939680">"Bonisa igama lokuthinta"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="3333754126434989709">"Bonisa igama lokuhlola kuqala elintantayo nokuthinta"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kulondoloziwe"</string>
<string name="label_go_key" msgid="1635148082137219148">"Iya"</string>
<string name="label_next_key" msgid="362972844525672568">"Okulandelayo"</string>
@@ -95,6 +93,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Buyisela"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Sesha"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Icashazi"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Shintsha ulimi"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Okulandelayo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Okwangaphambilini"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"U-Shift uvunyelwe"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Ofeleba bavunyelwe"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"U-Shift uvimbelwe"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a18371fc9..cced45d9f 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -18,13 +18,10 @@
<declare-styleable name="KeyboardTheme">
<!-- Keyboard style -->
<attr name="keyboardStyle" format="reference" />
- <!-- TODO: Get rid of latinKeyboardStyle -->
- <!-- LatinKeyboard style -->
- <attr name="latinKeyboardStyle" format="reference" />
<!-- KeyboardView style -->
<attr name="keyboardViewStyle" format="reference" />
- <!-- LatinKeyboardView style -->
- <attr name="latinKeyboardViewStyle" format="reference" />
+ <!-- MainKeyboardView style -->
+ <attr name="mainKeyboardViewStyle" format="reference" />
<!-- MoreKeysKeyboard style -->
<attr name="moreKeysKeyboardStyle" format="reference" />
<!-- MoreKeysKeyboardView style -->
@@ -32,7 +29,7 @@
<attr name="moreKeysKeyboardPanelStyle" format="reference" />
<!-- Suggestions strip style -->
<attr name="suggestionsStripBackgroundStyle" format="reference" />
- <attr name="suggestionsViewStyle" format="reference" />
+ <attr name="suggestionStripViewStyle" format="reference" />
<attr name="moreSuggestionsViewStyle" format="reference" />
<attr name="suggestionBackgroundStyle" format="reference" />
<attr name="suggestionPreviewBackgroundStyle" format="reference" />
@@ -121,9 +118,30 @@
<enum name="italic" value="2" />
<enum name="boldItalic" value="3" />
</attr>
+
+ <!-- Attributes for PreviewPlacerView -->
+ <attr name="gestureFloatingPreviewTextSize" format="dimension" />
+ <attr name="gestureFloatingPreviewTextColor" format="color" />
+ <attr name="gestureFloatingPreviewTextOffset" format="dimension" />
+ <attr name="gestureFloatingPreviewTextShadingColor" format="color" />
+ <attr name="gestureFloatingPreviewTextShadingBorder" format="dimension" />
+ <attr name="gestureFloatingPreviewTextShadowColor" format="color" />
+ <attr name="gestureFloatingPreviewTextShadowBorder" format="dimension" />
+ <attr name="gestureFloatingPreviewTextConnectorColor" format="color" />
+ <attr name="gestureFloatingPreviewTextConnectorWidth" format="dimension" />
+ <!-- Delay after gesture input and gesture floating preview text dismissing in millisecond -->
+ <attr name="gestureFloatingPreviewTextLingerTimeout" format="integer" />
+ <!-- Delay after gesture trail starts fading out in millisecond. -->
+ <attr name="gesturePreviewTrailFadeoutStartDelay" format="integer" />
+ <!-- Duration while gesture preview trail is fading out in millisecond. -->
+ <attr name="gesturePreviewTrailFadeoutDuration" format="integer" />
+ <!-- Interval of updating gesture preview trail in millisecond. -->
+ <attr name="gesturePreviewTrailUpdateInterval" format="integer" />
+ <attr name="gesturePreviewTrailColor" format="color" />
+ <attr name="gesturePreviewTrailWidth" format="dimension" />
</declare-styleable>
- <declare-styleable name="LatinKeyboardView">
+ <declare-styleable name="MainKeyboardView">
<attr name="autoCorrectionSpacebarLedEnabled" format="boolean" />
<attr name="autoCorrectionSpacebarLedIcon" format="reference" />
<!-- Size of the text for spacebar language label, in the proportion of key height. -->
@@ -158,9 +176,9 @@
<attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" />
</declare-styleable>
- <declare-styleable name="SuggestionsView">
+ <declare-styleable name="SuggestionStripView">
<attr name="suggestionStripOption" format="integer">
- <!-- This should be aligned with SuggestionsViewParams.AUTO_CORRECT_* and etc. -->
+ <!-- This should be aligned with SuggestionStripViewParams.AUTO_CORRECT_* and etc. -->
<flag name="autoCorrectBold" value="0x01" />
<flag name="autoCorrectUnderline" value="0x02" />
<flag name="validTypedWordBold" value="0x04" />
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 8d3319dae..8477df054 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -19,16 +19,15 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_PHONE} -->
+ <integer name="config_device_form_factor">0</integer>
<bool name="config_use_fullscreen_mode">false</bool>
<bool name="config_enable_show_voice_key_option">true</bool>
<bool name="config_enable_show_popup_on_keypress_option">true</bool>
- <bool name="config_enable_next_word_suggestions_option">true</bool>
- <bool name="config_enable_usability_study_mode_option">false</bool>
+ <!-- TODO: Disable the following configuration for production. -->
+ <bool name="config_enable_usability_study_mode_option">true</bool>
<!-- Whether or not Popup on key press is enabled by default -->
<bool name="config_default_popup_preview">true</bool>
- <!-- Default value for next word suggestion: while showing suggestions for a word should we weigh
- in the previous word? -->
- <bool name="config_default_next_word_suggestions">true</bool>
<!-- Default value for next word prediction: after entering a word and a space only, should we look
at input history to suggest a hopefully helpful suggestions for the next word? -->
<bool name="config_default_next_word_prediction">true</bool>
@@ -50,8 +49,12 @@
Configuration for KeyboardView
-->
<integer name="config_key_preview_linger_timeout">70</integer>
+ <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer>
+ <integer name="config_gesture_preview_trail_fadeout_start_delay">100</integer>
+ <integer name="config_gesture_preview_trail_fadeout_duration">1000</integer>
+ <integer name="config_gesture_preview_trail_update_interval">20</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
<dimen name="config_key_hysteresis_distance">8.0dp</dimen>
<integer name="config_touch_noise_threshold_time">40</integer>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 925eb55fa..c59bad302 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -93,4 +93,12 @@
<dimen name="more_suggestions_hint_text_size">27dp</dimen>
<integer name="suggestions_count_in_strip">3</integer>
<integer name="center_suggestion_percentile">36</integer>
+
+ <!-- Gesture preview parameters -->
+ <dimen name="gesture_preview_trail_width">2.5dp</dimen>
+ <dimen name="gesture_floating_preview_text_size">35dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">75dp</dimen>
+ <dimen name="gesture_floating_preview_text_shadow_border">17.5dp</dimen>
+ <dimen name="gesture_floating_preview_text_shading_border">7.5dp</dimen>
+ <dimen name="gesture_floating_preview_text_connector_width">1.0dp</dimen>
</resources>
diff --git a/java/res/values-it/donottranslate.xml b/java/res/values/gesture-input.xml
index 58e94361b..235616fbe 100644
--- a/java/res/values-it/donottranslate.xml
+++ b/java/res/values/gesture-input.xml
@@ -2,7 +2,7 @@
<!--
/*
**
-** Copyright 2009, 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.
@@ -17,7 +17,6 @@
** limitations under the License.
*/
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Symbols that do NOT separate words -->
- <string name="symbols_excluded_from_word_separators"></string>
+<resources>
+ <bool name="config_gesture_input_enabled_by_build_config">false</bool>
</resources>
diff --git a/java/res/values/strings-appname.xml b/java/res/values/strings-appname.xml
new file mode 100644
index 000000000..19aaa2513
--- /dev/null
+++ b/java/res/values/strings-appname.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- Title for Latin Keyboard -->
+ <string name="english_ime_name">Android keyboard</string>
+
+ <!-- Name of Android spell checker service -->
+ <string name="spell_checker_service_name">Android spell checker</string>
+
+ <!-- Title for Latin keyboard settings activity / dialog -->
+ <string name="english_ime_settings">Android keyboard settings</string>
+
+ <!-- Title for the spell checking service settings screen -->
+ <string name="android_spell_checker_settings">Spell checking settings</string>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index d51d3789a..35cbcf3c4 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -18,23 +18,16 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Title for Latin keyboard -->
- <string name="english_ime_name">Android keyboard</string>
<!-- Application name for opensource Android keyboard. AOSP(Android Open Source Project) should not be translated. -->
<string name="aosp_android_keyboard_ime_name">Android keyboard (AOSP)</string>
- <!-- Title for Latin keyboard settings activity / dialog -->
- <string name="english_ime_settings">Android keyboard settings</string>
<!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] -->
<string name="english_ime_input_options">Input options</string>
+ <!-- Title for Latin keyboard research log dialog, which contains special commands for users that contribute data for research. [CHAR LIMIT=33] -->
+ <string name="english_ime_research_log">Research Log Commands</string>
- <!-- Name of Android spell checker service -->
- <string name="spell_checker_service_name">Android spell checker</string>
<!-- Name of Android spell checker service. AOSP(Android Open Source Project) should not be translated. -->
<string name="aosp_spell_checker_service_name">Android spell checker (AOSP)</string>
- <!-- Title for the spell checking service settings screen -->
- <string name="android_spell_checker_settings">Spell checking settings</string>
-
<!-- Title for the spell checker option to turn on/off contact names lookup [CHAR LIMIT=25] -->
<string name="use_contacts_for_spellchecking_option_title">Look up contact names</string>
@@ -83,11 +76,6 @@
<!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] -->
<string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string>
- <!-- Option name for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=25] -->
- <string name="enable_span_insert">Enable recorrections</string>
- <!-- Option summary for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=65] -->
- <string name="enable_span_insert_summary">Set suggestions for recorrections</string>
-
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Auto-capitalization</string>
@@ -118,14 +106,21 @@
<!-- Option to suggest auto correction suggestions very aggressively. Auto-corrects to a word which has even large edit distance from typed word. [CHAR LIMIT=20] -->
<string name="auto_correction_threshold_mode_very_aggeressive">Very aggressive</string>
- <!-- Option to enable next word correction -->
- <string name="bigram_suggestion">Next word suggestions</string>
- <!-- Option to enable next word suggestion. This uses the previous word in an attempt to improve the suggestions quality -->
- <string name="bigram_suggestion_summary">Use previous word to improve suggestions</string>
- <!-- Option to enable using next word prediction -->
- <string name="bigram_prediction">Next word prediction</string>
- <!-- Description for "next word prediction" option. This displays suggestions even when there is no input, based on the previous word. -->
- <string name="bigram_prediction_summary">Use previous word also for prediction</string>
+ <!-- Option to enable using next word suggestions. After the user types a space, with this option on, the keyboard will try to predict the next word. -->
+ <string name="bigram_prediction">Next word suggestions</string>
+ <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. -->
+ <string name="bigram_prediction_summary">Based on previous word</string>
+
+ <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]-->
+ <string name="gesture_input">Gesture input</string>
+ <!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]-->
+ <string name="gesture_input_summary">Input a word by tracing the letters of a word</string>
+ <!-- Option to enable gesture trail preview. The user can see a trail of the gesture during gesture input. [CHAR LIMIT=30]-->
+ <string name="gesture_preview_trail">Show gesture trail</string>
+ <!-- Option to enable gesture floating text preview. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=30]-->
+ <string name="gesture_floating_preview_text">Show gesture word</string>
+ <!-- Description for "gesture_floating_preview_text" option. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=65]-->
+ <string name="gesture_floating_preview_text_summary">Show floating preview word with gesture</string>
<!-- Indicates that a word has been added to the dictionary -->
<string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
@@ -192,6 +187,12 @@
<string name="spoken_description_search">Search</string>
<!-- Spoken description for the "U+2022" (BULLET) keyboard key. -->
<string name="spoken_description_dot">Dot</string>
+ <!-- Spoken description for the "Switch language" keyboard key. -->
+ <string name="spoken_description_language_switch">Switch language</string>
+ <!-- Spoken description for the "Next" action keyboard key. -->
+ <string name="spoken_description_action_next">Next</string>
+ <!-- Spoken description for the "Previous" action keyboard key. -->
+ <string name="spoken_description_action_previous">Previous</string>
<!-- Spoken feedback after turning "Shift" mode on. -->
<string name="spoken_description_shiftmode_on">Shift enabled</string>
@@ -233,6 +234,65 @@
<!-- Title for input language selection screen -->
<string name="language_selection_title">Input languages</string>
+ <!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_do_not_log_this_session" translatable="false">Suspend logging</string>
+ <!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_enable_session_logging" translatable="false">Enable logging</string>
+ <!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_log_deleting" translatable="false">Deleting session log</string>
+ <!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_logging_suspended" translatable="false">Logging temporarily suspended. To disable permanently, go to Android Keyboard Settings</string>
+ <!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_log_not_deleted" translatable="false">Session log NOT deleted</string>
+ <!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_logging_enabled" translatable="false">Session logging enabled</string>
+
+ <!-- Menu option that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_menu_option" translatable="false">Send feedback</string>
+ <!-- Dialog box title that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_dialog_title" translatable="false">Send feedback</string>
+ <!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <!-- TODO: handle multilingual plurals -->
+ <string name="research_feedback_include_history_label" translatable="false">Include last <xliff:g id="word">%d</xliff:g> words entered</string>
+ <!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_hint" translatable="false">Enter your feedback here.</string>
+ <!-- Dialog button choice to send research feedback [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_send" translatable="false">Send</string>
+ <!-- Dialog button choice to cancel sending research feedback [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_cancel" translatable="false">Cancel</string>
+ <!-- Toast notification to ask user to quit the research feedback dialog to perform this operation [CHAR LIMIT=100] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_please_exit_feedback_form" translatable="false">Please exit the feedback dialog to access the research log menu</string>
+
+ <!-- Title of dialog shown at start informing users about contributing research usage data-->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_splash_title" translatable="false">Usage Participation</string>
+ <!-- Contents of note explaining what data is collected and how. -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_splash_content" translatable="false">Thank you for dogfooding this keyboard.\n\nIf you like it, please help us make it better by sending us usage information. When enabled, the keyboard uploads general statistics, such as how fast you type, and also occasional samples of how you type words.\n\nNo passwords or non-dictionary words are ever automatically uploaded, and words are sampled infrequently enough so that reconstructing the meaning of what you typed is highly unlikely.\n\nYou can disable and reenable logging through the RLog menu by long-pressing on the microphone or settings key.\n</string>
+ <!-- Button label text for opting out of research usage data collection [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_dont_send_usage_info" translatable="false">Do not send\nusage info</string>
+ <!-- Button label text for opting into research usage data collection [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_send_usage_info" translatable="false">Send usage info</string>
+
+ <!-- Name for the research uploading service to be displayed to users. [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_log_uploader_name" translatable="false">Research Uploader Service</string>
+
<!-- Preference for input language selection -->
<string name="select_language">Input languages</string>
@@ -260,6 +320,15 @@
<!-- Description for English (United States) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25]
This should be identical to subtype_en_US aside from the trailing (%s). -->
<string name="subtype_with_layout_en_US">English (US) (<xliff:g id="layout">%s</xliff:g>)</string>
+ <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
+ Description for Serbian Cyrillic keyboard subtype [CHAR LIMIT=25]
+ <string name="subtype_serbian_cyrillic">Serbian (Cyrillic)</string>
+ Description for Serbian Latin keyboard subtype [CHAR LIMIT=25]
+ <string name="subtype_serbian_latin">Serbian (Latin)</string>
+ Description for Serbian Latin keyboard subtype with explicit keyboard layout [CHAR LIMIT=25]
+ This should be identical to subtype_serbian_latin aside from the trailing (%s).
+ <string name="subtype_with_layout_sr-Latn">Serbian (Latin) (<xliff:g id="layout">%s</xliff:g>)</string>
+ -->
<!-- Description for language agnostic keyboard subtype [CHAR LIMIT=25] -->
<string name="subtype_no_language">No language</string>
<!-- Description for language agnostic QWERTY keyboard subtype [CHAR LIMIT=25] -->
@@ -289,7 +358,7 @@
<string name="subtype_locale">Language</string>
<!-- Title of the spinner for choosing a keyboard layout of custom style in the settings dialog [CHAR LIMIT=15] -->
<string name="keyboard_layout_set">Layout</string>
- <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=64] -->
+ <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=130] -->
<string name="custom_input_style_note_message">"Your custom input style needs to be enabled before you start using it. Do you want to enable it now?"</string>
<!-- Title of the button to enable a custom input style entry in the settings dialog [CHAR LIMIT=15] -->
<string name="enable">Enable</string>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index e9b0470ea..e5e7fed80 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Theme "Basic" -->
<style name="Keyboard">
<!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
@@ -67,7 +67,23 @@
<item name="shadowColor">#BB000000</item>
<item name="shadowRadius">2.75</item>
<item name="backgroundDimAlpha">128</item>
- <!-- Common attributes of LatinKeyboardView -->
+ <!-- android:color/holo_blue_light=#FF33B5E5 -->
+ <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item>
+ <item name="gestureFloatingPreviewTextColor">@android:color/white</item>
+ <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item>
+ <item name="gestureFloatingPreviewTextShadingColor">@android:color/holo_blue_light</item>
+ <item name="gestureFloatingPreviewTextShadingBorder">@dimen/gesture_floating_preview_text_shading_border</item>
+ <item name="gestureFloatingPreviewTextShadowColor">#FF252525</item>
+ <item name="gestureFloatingPreviewTextShadowBorder">@dimen/gesture_floating_preview_text_shadow_border</item>
+ <item name="gestureFloatingPreviewTextConnectorColor">@android:color/white</item>
+ <item name="gestureFloatingPreviewTextConnectorWidth">@dimen/gesture_floating_preview_text_connector_width</item>
+ <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
+ <item name="gesturePreviewTrailFadeoutStartDelay">@integer/config_gesture_preview_trail_fadeout_start_delay</item>
+ <item name="gesturePreviewTrailFadeoutDuration">@integer/config_gesture_preview_trail_fadeout_duration</item>
+ <item name="gesturePreviewTrailUpdateInterval">@integer/config_gesture_preview_trail_update_interval</item>
+ <item name="gesturePreviewTrailColor">@android:color/holo_blue_light</item>
+ <item name="gesturePreviewTrailWidth">@dimen/gesture_preview_trail_width</item>
+ <!-- Common attributes of MainKeyboardView -->
<item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
<item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item>
<item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item>
@@ -84,7 +100,7 @@
<item name="altCodeKeyWhileTypingFadeinAnimator">@anim/alt_code_key_while_typing_fadein</item>
</style>
<style
- name="LatinKeyboardView"
+ name="MainKeyboardView"
parent="KeyboardView">
<item name="autoCorrectionSpacebarLedEnabled">true</item>
<item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
@@ -114,7 +130,7 @@
<item name="android:background">@drawable/keyboard_suggest_strip</item>
</style>
<style
- name="SuggestionsViewStyle"
+ name="SuggestionStripViewStyle"
parent="SuggestionsStripBackgroundStyle"
>
<item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
@@ -155,7 +171,7 @@
<item name="keyBackground">@drawable/btn_keyboard_key3</item>
</style>
<style
- name="LatinKeyboardView.HighContrast"
+ name="MainKeyboardView.HighContrast"
parent="KeyboardView.HighContrast"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -190,7 +206,7 @@
<item name="shadowColor">#FFFFFFFF</item>
</style>
<style
- name="LatinKeyboardView.Stone"
+ name="MainKeyboardView.Stone"
parent="KeyboardView.Stone"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -230,7 +246,7 @@
<item name="keyTextStyle">bold</item>
</style>
<style
- name="LatinKeyboardView.Stone.Bold"
+ name="MainKeyboardView.Stone.Bold"
parent="KeyboardView.Stone.Bold"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -259,7 +275,7 @@
<item name="keyTextStyle">bold</item>
</style>
<style
- name="LatinKeyboardView.Gingerbread"
+ name="MainKeyboardView.Gingerbread"
parent="KeyboardView.Gingerbread"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -316,7 +332,7 @@
<item name="shadowRadius">0.0</item>
</style>
<style
- name="LatinKeyboardView.IceCreamSandwich"
+ name="MainKeyboardView.IceCreamSandwich"
parent="KeyboardView.IceCreamSandwich"
>
<item name="autoCorrectionSpacebarLedEnabled">false</item>
@@ -348,7 +364,7 @@
<item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
</style>
<style
- name="SuggestionsViewStyle.IceCreamSandwich"
+ name="SuggestionStripViewStyle.IceCreamSandwich"
parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"
>
<item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index 19df42ce1..b3ea05045 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard.HighContrast</item>
<item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.HighContrast</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.HighContrast</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index 5d477206d..ff6a70a08 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard</item>
<item name="keyboardViewStyle">@style/KeyboardView</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index a13979818..0ce0b8a9b 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard.Gingerbread</item>
<item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Gingerbread</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Gingerbread</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Gingerbread</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Gingerbread</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index e6fd4f451..8df58c594 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
<item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
<item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.IceCreamSandwich</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.IceCreamSandwich</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.IceCreamSandwich</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.IceCreamSandwich</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle.IceCreamSandwich</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle.IceCreamSandwich</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle.IceCreamSandwich</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle.IceCreamSandwich</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle.IceCreamSandwich</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index 47de99e47..355a97f7b 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
<item name="keyboardStyle">@style/Keyboard.Stone.Bold</item>
<item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone.Bold</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone.Bold</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index a0b39e3e6..23437f780 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
<item name="keyboardStyle">@style/Keyboard.Stone</item>
<item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/whitelist.xml b/java/res/values/urls.xml
index d4ecbfaa4..a8e9ad7d3 100644
--- a/java/res/values/whitelist.xml
+++ b/java/res/values/urls.xml
@@ -2,7 +2,7 @@
<!--
/*
**
-** Copyright 2011, 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.
@@ -17,13 +17,6 @@
** limitations under the License.
*/
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--
- An entry of the whitelist word should be:
- 1. (int)frequency
- 2. (String)before
- 3. (String)after
- -->
- <string-array name="wordlist_whitelist">
- </string-array>
+<resources>
+ <string name="research_logger_upload_url" translatable="false"></string>
</resources>
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index a1b2eb475..bf2e76a6b 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -133,6 +133,17 @@
latin:keyIconPreview="!icon/tab_key_preview"
latin:backgroundType="functional" />
</case>
+ <case
+ latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+ latin:navigateNext="true"
+ >
+ <key-style
+ latin:styleName="tabKeyStyle"
+ latin:code="!code/key_action_next"
+ latin:keyIcon="!icon/tab_key"
+ latin:keyIconPreview="!icon/tab_key_preview"
+ latin:backgroundType="functional" />
+ </case>
<default>
<key-style
latin:styleName="tabKeyStyle"
diff --git a/java/res/xml-sw600dp/rowkeys_symbols3.xml b/java/res/xml-sw600dp/rowkeys_symbols3.xml
index 4bfa0d730..30fba3812 100644
--- a/java/res/xml-sw600dp/rowkeys_symbols3.xml
+++ b/java/res/xml-sw600dp/rowkeys_symbols3.xml
@@ -49,7 +49,7 @@
<Key
latin:keyLabel="." />
<Key
- latin:keyLabel="!text/keylabel_for_symbols_exclamation"
+ latin:keyLabel="!"
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
<Key
latin:keyLabel="!text/keylabel_for_symbols_question"
diff --git a/java/res/xml-sw768dp/key_space.xml b/java/res/xml-sw768dp/key_space.xml
index 8968f080a..58e71d807 100644
--- a/java/res/xml-sw768dp/key_space.xml
+++ b/java/res/xml-sw768dp/key_space.xml
@@ -24,15 +24,36 @@
<switch>
<case
latin:languageCode="fa"
+ latin:languageSwitchKeyEnabled="true"
+ >
+ <Key
+ latin:keyStyle="languageSwitchKeyStyle" />
+ <Key
+ latin:keyStyle="spaceKeyStyle"
+ latin:keyWidth="24.141%p" />
+ <Key
+ latin:keyStyle="zwnjKeyStyle" />
+ </case>
+ <case
+ latin:languageCode="fa"
+ latin:languageSwitchKeyEnabled="false"
>
<Key
latin:keyStyle="spaceKeyStyle"
latin:keyWidth="32.188%p" />
- <!-- U+200C: "" ZERO WIDTH NON-JOINER
- U+200D: "" ZERO WIDTH JOINER -->
<Key
latin:keyStyle="zwnjKeyStyle" />
</case>
+ <case
+ latin:languageSwitchKeyEnabled="true"
+ >
+ <Key
+ latin:keyStyle="languageSwitchKeyStyle" />
+ <Key
+ latin:keyStyle="spaceKeyStyle"
+ latin:keyWidth="32.188%p" />
+ </case>
+ <!-- languageSwitchKeyEnabled="false" -->
<default>
<Key
latin:keyStyle="spaceKeyStyle"
diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
index 40082ac35..537e76800 100644
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ b/java/res/xml-sw768dp/key_styles_common.xml
@@ -76,7 +76,7 @@
<key-style
latin:styleName="spaceKeyStyle"
latin:code="!code/key_space"
- latin:keyActionFlags="noKeyPreview" />
+ latin:keyActionFlags="noKeyPreview|enableLongPress" />
<!-- U+200C: ZERO WIDTH NON-JOINER
U+200D: ZERO WIDTH JOINER -->
<key-style
@@ -100,6 +100,12 @@
latin:keyActionFlags="noKeyPreview"
latin:backgroundType="functional" />
<key-style
+ latin:styleName="languageSwitchKeyStyle"
+ latin:code="!code/key_language_switch"
+ latin:keyIcon="!icon/language_switch_key"
+ latin:keyActionFlags="noKeyPreview|altCodeWhileTyping|enableLongPress"
+ latin:altCode="!code/key_space" />
+ <key-style
latin:styleName="settingsKeyStyle"
latin:code="!code/key_settings"
latin:keyIcon="!icon/settings_key"
@@ -117,6 +123,17 @@
latin:keyLabelFlags="fontNormal|preserveCase"
latin:backgroundType="functional" />
</case>
+ <case
+ latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+ latin:navigateNext="true"
+ >
+ <key-style
+ latin:styleName="tabKeyStyle"
+ latin:code="!code/key_action_next"
+ latin:keyLabel="!text/label_tab_key"
+ latin:keyLabelFlags="fontNormal|preserveCase"
+ latin:backgroundType="functional" />
+ </case>
<default>
<key-style
latin:styleName="tabKeyStyle"
diff --git a/java/res/xml-sw768dp/rows_east_slavic.xml b/java/res/xml-sw768dp/rows_east_slavic.xml
index 0316c76f6..a4287f162 100644
--- a/java/res/xml-sw768dp/rows_east_slavic.xml
+++ b/java/res/xml-sw768dp/rows_east_slavic.xml
@@ -33,9 +33,8 @@
<include
latin:keyboardLayout="@xml/rowkeys_east_slavic1"
latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
- <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN -->
<Key
- latin:keyLabel="&#x044A;" />
+ latin:keyLabel="!text/keylabel_for_east_slavic_row1_12" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 622da2120..162119dab 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -22,23 +22,8 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<!-- Base key style for the key which may have settings or tab key as popup key. -->
- <switch>
- <case
- latin:clobberSettingsKey="true"
- >
- <key-style
- latin:styleName="f1MoreKeysStyle"
- latin:backgroundType="functional" />
- </case>
- <!-- clobberSettingsKey="false" -->
- <default>
- <key-style
- latin:styleName="f1MoreKeysStyle"
- latin:keyLabelFlags="hasPopupHint"
- latin:moreKeys="!text/settings_as_more_key"
- latin:backgroundType="functional" />
- </default>
- </switch>
+ <include
+ latin:keyboardLayout="@xml/key_styles_f1" />
<!-- Functional key styles -->
<switch>
<case
diff --git a/java/res/xml/key_styles_f1.xml b/java/res/xml/key_styles_f1.xml
new file mode 100644
index 000000000..8dfc3cb84
--- /dev/null
+++ b/java/res/xml/key_styles_f1.xml
@@ -0,0 +1,43 @@
+<?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"
+>
+ <!-- Base key style for the key which may have settings or tab key as popup key. -->
+ <!-- Kept as a separate file for cleaner overriding by an overlay. -->
+ <switch>
+ <case
+ latin:clobberSettingsKey="true"
+ >
+ <key-style
+ latin:styleName="f1MoreKeysStyle"
+ latin:backgroundType="functional" />
+ </case>
+ <!-- clobberSettingsKey="false" -->
+ <default>
+ <key-style
+ latin:styleName="f1MoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:moreKeys="!text/settings_as_more_key"
+ latin:backgroundType="functional" />
+ </default>
+ </switch>
+</merge>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 7f8a23a0e..acdf7645f 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -22,10 +22,12 @@
<!-- Supported subtypes
keyboard_locale: script_name/keyboard_layout_set[:keyboard_locale]
+ af: Afrikaans/qwerty
ar: Arabic/arabic
be: Belarusian/east_slavic
bg: Bulgarian/bulgarian
bg: Bulgarian/bulgarian_bds
+ ca: Catalan/spanish
cs: Czech/qwertz
da: Danish/nordic
de: German/qwertz
@@ -41,14 +43,16 @@
hi: Hindi/hindi
hr: Croatian/qwertz
hu: Hungarian/qwertz
+ in: Indonesian/qwerty # "id" is official language code of Indonesian.
is: Icelandic/qwerty
it: Italian/qwerty
- iw: Hebrew/hebrew
+ iw: Hebrew/hebrew # "he" is official language code of Hebrew.
ka: Georgian/georgian
ky: Kyrgyz/east_slavic
lt: Lithuanian/qwerty
lv: Latvian/qwerty
mk: Macedonian/south_slavic
+ ms: Malay/qwerty
nb: Norwegian Bokmål/nordic
nl: Dutch/qwerty
pl: Polish/qwerty
@@ -59,11 +63,15 @@
sk: Slovak/qwerty
sl: Slovenian/qwerty
sr: Serbian/south_slavic
+ (sr-Latn: Serbian/qwerty) # not yet implemented.
sv: Swedish/nordic
+ sw: Swahili/qwerty
th: Thai/thai
+ tl: Tagalog/spanish
tr: Turkish/qwerty
uk: Ukrainian/east_slavic
vi: Vietnamese/qwerty
+ zu: Zulu/qwerty
zz: QWERTY/qwerty
-->
<!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
@@ -86,6 +94,12 @@
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="af"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:imeSubtypeLocale="ar"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
@@ -110,6 +124,12 @@
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="ca"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
@@ -186,6 +206,13 @@
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
+ <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. -->
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="in"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="is"
@@ -237,6 +264,12 @@
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="ms"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
@@ -295,6 +328,20 @@
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/>
+ <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_serbian_cyrillic"
+ android:imeSubtypeLocale="sr"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_serbian_latin"
+ android:imeSubtypeLocale="sr-Latn"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:imeSubtypeLocale="sv"
@@ -303,12 +350,24 @@
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="sw"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:imeSubtypeLocale="th"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=thai"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="tl"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
@@ -326,6 +385,12 @@
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:imeSubtypeLocale="zu"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_no_language_qwerty"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 137981949..ef6be3eed 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -81,6 +81,18 @@
android:title="@string/misc_category"
android:key="misc_settings">
<CheckBoxPreference
+ android:key="next_word_prediction"
+ android:title="@string/bigram_prediction"
+ android:summary="@string/bigram_prediction_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
+ <CheckBoxPreference
+ android:key="gesture_input"
+ android:title="@string/gesture_input"
+ android:summary="@string/gesture_input_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
+ <CheckBoxPreference
android:key="usability_study_mode"
android:title="@string/prefs_usability_study_mode"
android:persistent="true"
@@ -90,6 +102,12 @@
android:title="@string/advanced_settings"
android:summary="@string/advanced_settings_summary">
<CheckBoxPreference
+ android:key="pref_key_use_contacts_dict"
+ android:title="@string/use_contacts_dict"
+ android:summary="@string/use_contacts_dict_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
+ <CheckBoxPreference
android:key="pref_suppress_language_switch_key"
android:title="@string/suppress_language_switch_key"
android:persistent="true"
@@ -108,36 +126,23 @@
<ListPreference
android:key="pref_key_preview_popup_dismiss_delay"
android:title="@string/key_preview_popup_dismiss_delay" />
- <CheckBoxPreference
- android:key="pref_key_use_contacts_dict"
- android:title="@string/use_contacts_dict"
- android:summary="@string/use_contacts_dict_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="next_word_suggestion"
- android:title="@string/bigram_suggestion"
- android:summary="@string/bigram_suggestion_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="next_word_prediction"
- android:title="@string/bigram_prediction"
- android:summary="@string/bigram_prediction_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="enable_span_insert"
- android:title="@string/enable_span_insert"
- android:summary="@string/enable_span_insert_summary"
- android:persistent="true"
- android:defaultValue="true" />
<PreferenceScreen
android:key="pref_vibration_duration_settings"
android:title="@string/prefs_keypress_vibration_duration_settings"/>
<PreferenceScreen
android:key="pref_keypress_sound_volume"
android:title="@string/prefs_keypress_sound_volume_settings" />
+ <CheckBoxPreference
+ android:key="pref_gesture_preview_trail"
+ android:title="@string/gesture_preview_trail"
+ android:persistent="true"
+ android:defaultValue="true" />
+ <CheckBoxPreference
+ android:key="pref_gesture_floating_preview_text"
+ android:title="@string/gesture_floating_preview_text"
+ android:summary="@string/gesture_floating_preview_text_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/java/res/xml/rowkeys_east_slavic1.xml b/java/res/xml/rowkeys_east_slavic1.xml
index 00cb6a973..c1b43bd36 100644
--- a/java/res/xml/rowkeys_east_slavic1.xml
+++ b/java/res/xml/rowkeys_east_slavic1.xml
@@ -47,7 +47,7 @@
latin:keyLabel="&#x0435;"
latin:keyHintLabel="5"
latin:additionalMoreKeys="5"
- latin:moreKeys="!text/more_keys_for_cyrillic_ye" />
+ latin:moreKeys="!text/more_keys_for_cyrillic_ie" />
<!-- U+043D: "н" CYRILLIC SMALL LETTER EN -->
<Key
latin:keyLabel="&#x043D;"
@@ -58,7 +58,8 @@
<Key
latin:keyLabel="&#x0433;"
latin:keyHintLabel="7"
- latin:additionalMoreKeys="7" />
+ latin:additionalMoreKeys="7"
+ latin:moreKeys="!text/more_keys_for_cyrillic_ghe" />
<!-- U+0448: "ш" CYRILLIC SMALL LETTER SHA -->
<Key
latin:keyLabel="&#x0448;"
@@ -75,6 +76,5 @@
latin:additionalMoreKeys="0" />
<!-- U+0445: "х" CYRILLIC SMALL LETTER HA -->
<Key
- latin:keyLabel="&#x0445;"
- latin:moreKeys="!text/more_keys_for_cyrillic_ha" />
+ latin:keyLabel="&#x0445;" />
</merge>
diff --git a/java/res/xml/rowkeys_east_slavic2.xml b/java/res/xml/rowkeys_east_slavic2.xml
index c635af2d9..9743727c1 100644
--- a/java/res/xml/rowkeys_east_slavic2.xml
+++ b/java/res/xml/rowkeys_east_slavic2.xml
@@ -52,7 +52,6 @@
<!-- U+0436: "ж" CYRILLIC SMALL LETTER ZHE -->
<Key
latin:keyLabel="&#x0436;" />
- <!-- U+044D: "э" CYRILLIC SMALL LETTER E -->
<Key
- latin:keyLabel="&#x044D;" />
+ latin:keyLabel="!text/keylabel_for_east_slavic_row2_11" />
</merge>
diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml
index c89716bc7..7722ca9ae 100644
--- a/java/res/xml/rowkeys_symbols3.xml
+++ b/java/res/xml/rowkeys_symbols3.xml
@@ -22,7 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<Key
- latin:keyLabel="!text/keylabel_for_symbols_exclamation"
+ latin:keyLabel="!"
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
<switch>
<case
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index 70e38fdb0..039c77b9c 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -35,6 +35,7 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.latin.CollectionUtils;
/**
* Exposes a virtual view sub-tree for {@link KeyboardView} and generates
@@ -55,7 +56,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat
private final AccessibilityUtils mAccessibilityUtils;
/** A map of integer IDs to {@link Key}s. */
- private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>();
+ private final SparseArray<Key> mVirtualViewIdToKey = CollectionUtils.newSparseArray();
/** Temporary rect used to calculate in-screen bounds. */
private final Rect mTempBoundsInScreen = new Rect();
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 616b1c6d7..58d3022c9 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -19,10 +19,15 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
+import android.os.Build;
import android.os.SystemClock;
import android.provider.Settings;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
@@ -138,9 +143,10 @@ public class AccessibilityUtils {
* Sends the specified text to the {@link AccessibilityManager} to be
* spoken.
*
- * @param text the text to speak
+ * @param view The source view.
+ * @param text The text to speak.
*/
- public void speak(CharSequence text) {
+ public void announceForAccessibility(View view, CharSequence text) {
if (!mAccessibilityManager.isEnabled()) {
Log.e(TAG, "Attempted to speak when accessibility was disabled!");
return;
@@ -149,8 +155,7 @@ public class AccessibilityUtils {
// The following is a hack to avoid using the heavy-weight TextToSpeech
// class. Instead, we're just forcing a fake AccessibilityEvent into
// the screen reader to make it speak.
- final AccessibilityEvent event = AccessibilityEvent
- .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
event.setPackageName(PACKAGE);
event.setClassName(CLASS);
@@ -158,20 +163,34 @@ public class AccessibilityUtils {
event.setEnabled(true);
event.getText().add(text);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ // Platforms starting at SDK 16 should use announce events.
+ if (Build.VERSION.SDK_INT >= 16) {
+ event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
+ } else {
+ event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
+ final ViewParent viewParent = view.getParent();
+ if ((viewParent == null) || !(viewParent instanceof ViewGroup)) {
+ Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility");
+ return;
+ }
+
+ viewParent.requestSendAccessibilityEvent(view, event);
}
/**
* Handles speaking the "connect a headset to hear passwords" notification
* when connecting to a password field.
*
+ * @param view The source view.
* @param editorInfo The input connection's editor info attribute.
* @param restarting Whether the connection is being restarted.
*/
- public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInputViewInternal(View view, EditorInfo editorInfo, boolean restarting) {
if (shouldObscureInput(editorInfo)) {
final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
- speak(text);
+ announceForAccessibility(view, text);
}
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index f6376d5f4..2fff73154 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -29,7 +29,7 @@ import android.view.ViewConfiguration;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
@@ -37,7 +37,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
private InputMethodService mInputMethod;
- private LatinKeyboardView mView;
+ private MainKeyboardView mView;
private AccessibilityEntityProvider mAccessibilityNodeProvider;
private Key mLastHoverKey = null;
@@ -70,7 +70,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
*
* @param view The view to wrap.
*/
- public void setView(LatinKeyboardView view) {
+ public void setView(MainKeyboardView view) {
if (view == null) {
// Ignore null views.
return;
@@ -250,7 +250,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
text = context.getText(R.string.spoken_description_shiftmode_off);
}
- AccessibilityUtils.getInstance().speak(text);
+ AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
}
/**
@@ -290,6 +290,6 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
}
final String text = context.getString(resId);
- AccessibilityUtils.getInstance().speak(text);
+ AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
}
}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 23acb8b74..5c45448a5 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -19,11 +19,13 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
@@ -37,10 +39,10 @@ public class KeyCodeDescriptionMapper {
private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
// Map of key labels to spoken description resource IDs
- private final HashMap<CharSequence, Integer> mKeyLabelMap;
+ private final HashMap<CharSequence, Integer> mKeyLabelMap = CollectionUtils.newHashMap();
- // Map of key codes to spoken description resource IDs
- private final HashMap<Integer, Integer> mKeyCodeMap;
+ // Sparse array of spoken description resource IDs indexed by key codes
+ private final SparseIntArray mKeyCodeMap;
public static void init() {
sInstance.initInternal();
@@ -51,18 +53,15 @@ public class KeyCodeDescriptionMapper {
}
private KeyCodeDescriptionMapper() {
- mKeyLabelMap = new HashMap<CharSequence, Integer>();
- mKeyCodeMap = new HashMap<Integer, Integer>();
+ mKeyCodeMap = new SparseIntArray();
}
private void initInternal() {
// Manual label substitutions for key labels with no string resource
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
- // Symbols that most TTS engines can't speak
- mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
-
// Special non-character codes defined in Keyboard
+ mKeyCodeMap.put(Keyboard.CODE_SPACE, R.string.spoken_description_space);
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings);
@@ -70,6 +69,9 @@ public class KeyCodeDescriptionMapper {
mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
+ mKeyCodeMap.put(Keyboard.CODE_LANGUAGE_SWITCH, R.string.spoken_description_language_switch);
+ mKeyCodeMap.put(Keyboard.CODE_ACTION_NEXT, R.string.spoken_description_action_next);
+ mKeyCodeMap.put(Keyboard.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous);
}
/**
@@ -273,7 +275,7 @@ public class KeyCodeDescriptionMapper {
return context.getString(OBSCURED_KEY_RES_ID);
}
- if (mKeyCodeMap.containsKey(code)) {
+ if (mKeyCodeMap.indexOfKey(code) >= 0) {
return context.getString(mKeyCodeMap.get(code));
} else if (isDefinedNonCtrl) {
return Character.toString((char) code);
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
new file mode 100644
index 000000000..0befa7a66
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.compat;
+
+import android.inputmethodservice.InputMethodService;
+
+import java.lang.reflect.Method;
+
+public class InputMethodServiceCompatUtils {
+ private static final Method METHOD_enableHardwareAcceleration =
+ CompatUtils.getMethod(InputMethodService.class, "enableHardwareAcceleration");
+
+ private InputMethodServiceCompatUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static boolean enableHardwareAcceleration(InputMethodService ims) {
+ return (Boolean)CompatUtils.invoke(ims, false, METHOD_enableHardwareAcceleration);
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index a0f48d24c..6ba309fcb 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -16,10 +16,6 @@
package com.android.inputmethod.compat;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
-
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
@@ -27,6 +23,11 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -119,8 +120,7 @@ public class SuggestionSpanUtils {
} else {
spannable = new SpannableString(pickedWord);
}
- final ArrayList<String> suggestionsList = new ArrayList<String>();
- boolean sameAsTyped = false;
+ final ArrayList<String> suggestionsList = CollectionUtils.newArrayList();
for (int i = 0; i < suggestedWords.size(); ++i) {
if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) {
break;
@@ -128,8 +128,6 @@ public class SuggestionSpanUtils {
final CharSequence word = suggestedWords.getWord(i);
if (!TextUtils.equals(pickedWord, word)) {
suggestionsList.add(word.toString());
- } else if (i == 0) {
- sameAsTyped = true;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index e1e1ca9cf..178c9ff05 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -414,8 +414,14 @@ public class Key {
@Override
public String toString() {
- return String.format("%s/%s %d,%d %dx%d %s/%s/%s",
- Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel,
+ final String label;
+ if (StringUtils.codePointCount(mLabel) == 1 && mLabel.codePointAt(0) == mCode) {
+ label = "";
+ } else {
+ label = "/" + mLabel;
+ }
+ return String.format("%s%s %d,%d %dx%d %s/%s/%s",
+ Keyboard.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel,
KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType));
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 13e909c7e..868c8cab5 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -16,16 +16,15 @@
package com.android.inputmethod.keyboard;
+import com.android.inputmethod.latin.Constants;
-public class KeyDetector {
- public static final int NOT_A_CODE = -1;
+public class KeyDetector {
private final int mKeyHysteresisDistanceSquared;
private Keyboard mKeyboard;
private int mCorrectionX;
private int mCorrectionY;
- private boolean mProximityCorrectOn;
/**
* This class handles key detection.
@@ -38,8 +37,9 @@ public class KeyDetector {
}
public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
- if (keyboard == null)
+ if (keyboard == null) {
throw new NullPointerException();
+ }
mCorrectionX = (int)correctionX;
mCorrectionY = (int)correctionY;
mKeyboard = keyboard;
@@ -53,24 +53,18 @@ public class KeyDetector {
return x + mCorrectionX;
}
+ // TODO: Remove vertical correction.
public int getTouchY(int y) {
return y + mCorrectionY;
}
public Keyboard getKeyboard() {
- if (mKeyboard == null)
+ if (mKeyboard == null) {
throw new IllegalStateException("keyboard isn't set");
+ }
return mKeyboard;
}
- public void setProximityCorrectionEnabled(boolean enabled) {
- mProximityCorrectOn = enabled;
- }
-
- public boolean isProximityCorrectionEnabled() {
- return mProximityCorrectOn;
- }
-
public boolean alwaysAllowsSlidingInput() {
return false;
}
@@ -109,7 +103,7 @@ public class KeyDetector {
final StringBuilder sb = new StringBuilder();
boolean addDelimiter = false;
for (final int code : codes) {
- if (code == NOT_A_CODE) break;
+ if (code == Constants.NOT_A_CODE) break;
if (addDelimiter) sb.append(", ");
sb.append(Keyboard.printableCode(code));
addDelimiter = true;
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 21f175d7d..e37868b3f 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -23,6 +23,8 @@ import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.InflateException;
@@ -31,6 +33,7 @@ import com.android.inputmethod.keyboard.internal.KeyStyles;
import com.android.inputmethod.keyboard.internal.KeyboardCodesSet;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import com.android.inputmethod.latin.R;
@@ -44,7 +47,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
@@ -86,10 +88,10 @@ public class Keyboard {
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
- private static final int MINIMUM_LETTER_CODE = CODE_TAB;
/** Special keys code. Must be negative.
- * These should be aligned with values/keycodes.xml
+ * These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
+ * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[]
*/
public static final int CODE_SHIFT = -1;
public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
@@ -101,8 +103,9 @@ public class Keyboard {
public static final int CODE_ACTION_NEXT = -8;
public static final int CODE_ACTION_PREVIOUS = -9;
public static final int CODE_LANGUAGE_SWITCH = -10;
+ public static final int CODE_RESEARCH = -11;
// Code value representing the code is not specified.
- public static final int CODE_UNSPECIFIED = -11;
+ public static final int CODE_UNSPECIFIED = -12;
public final KeyboardId mId;
public final int mThemeId;
@@ -132,7 +135,7 @@ public class Keyboard {
public final Key[] mAltCodeKeysWhileTyping;
public final KeyboardIconsSet mIconsSet;
- private final HashMap<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
+ private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray();
private final ProximityInfo mProximityInfo;
private final boolean mProximityCharsCorrectionEnabled;
@@ -182,23 +185,25 @@ public class Keyboard {
if (code == CODE_UNSPECIFIED) {
return null;
}
- final Integer keyCode = code;
- if (mKeyCache.containsKey(keyCode)) {
- return mKeyCache.get(keyCode);
- }
+ synchronized (mKeyCache) {
+ final int index = mKeyCache.indexOfKey(code);
+ if (index >= 0) {
+ return mKeyCache.valueAt(index);
+ }
- for (final Key key : mKeys) {
- if (key.mCode == code) {
- mKeyCache.put(keyCode, key);
- return key;
+ for (final Key key : mKeys) {
+ if (key.mCode == code) {
+ mKeyCache.put(code, key);
+ return key;
+ }
}
+ mKeyCache.put(code, null);
+ return null;
}
- mKeyCache.put(keyCode, null);
- return null;
}
public boolean hasKey(Key aKey) {
- if (mKeyCache.containsKey(aKey)) {
+ if (mKeyCache.indexOfValue(aKey) >= 0) {
return true;
}
@@ -212,7 +217,12 @@ public class Keyboard {
}
public static boolean isLetterCode(int code) {
- return code >= MINIMUM_LETTER_CODE;
+ return code >= CODE_SPACE;
+ }
+
+ @Override
+ public String toString() {
+ return mId.toString();
}
public static class Params {
@@ -245,9 +255,9 @@ public class Keyboard {
public int GRID_WIDTH;
public int GRID_HEIGHT;
- public final HashSet<Key> mKeys = new HashSet<Key>();
- public final ArrayList<Key> mShiftKeys = new ArrayList<Key>();
- public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<Key>();
+ public final HashSet<Key> mKeys = CollectionUtils.newHashSet();
+ public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList();
+ public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList();
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@@ -274,9 +284,10 @@ public class Keyboard {
public void load(String[] data) {
final int dataLength = data.length;
if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
- if (LatinImeLogger.sDBG)
+ if (LatinImeLogger.sDBG) {
throw new RuntimeException(
"the size of touch position correction data is invalid");
+ }
return;
}
@@ -315,7 +326,7 @@ public class Keyboard {
public boolean isValid() {
return mEnabled && mXs != null && mYs != null && mRadii != null
- && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
+ && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
}
}
@@ -342,8 +353,8 @@ public class Keyboard {
private int mMaxHeightCount = 0;
private int mMaxWidthCount = 0;
- private final HashMap<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
- private final HashMap<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
+ private final SparseIntArray mHeightHistogram = new SparseIntArray();
+ private final SparseIntArray mWidthHistogram = new SparseIntArray();
private void clearHistogram() {
mMostCommonKeyHeight = 0;
@@ -355,22 +366,22 @@ public class Keyboard {
mWidthHistogram.clear();
}
- private static int updateHistogramCounter(HashMap<Integer, Integer> histogram,
- Integer key) {
- final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
+ private static int updateHistogramCounter(SparseIntArray histogram, int key) {
+ final int index = histogram.indexOfKey(key);
+ final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
histogram.put(key, count);
return count;
}
private void updateHistogram(Key key) {
- final Integer height = key.mHeight + key.mVerticalGap;
+ final int height = key.mHeight + key.mVerticalGap;
final int heightCount = updateHistogramCounter(mHeightHistogram, height);
if (heightCount > mMaxHeightCount) {
mMaxHeightCount = heightCount;
mMostCommonKeyHeight = height;
}
- final Integer width = key.mWidth + key.mHorizontalGap;
+ final int width = key.mWidth + key.mHorizontalGap;
final int widthCount = updateHistogramCounter(mWidthHistogram, width);
if (widthCount > mMaxWidthCount) {
mMaxWidthCount = widthCount;
@@ -422,67 +433,67 @@ public class Keyboard {
* This class parses Keyboard XML file and eventually build a Keyboard.
* The Keyboard XML file looks like:
* <pre>
- * &gt;!-- xml/keyboard.xml --&lt;
- * &gt;Keyboard keyboard_attributes*&lt;
- * &gt;!-- Keyboard Content --&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;!-- Row Content --&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;Spacer horizontalGap="32.0dp" /&lt;
- * &gt;include keyboardLayout="@xml/other_keys"&lt;
+ * &lt;!-- xml/keyboard.xml --&gt;
+ * &lt;Keyboard keyboard_attributes*&gt;
+ * &lt;!-- Keyboard Content --&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;!-- Row Content --&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;Spacer horizontalGap="32.0dp" /&gt;
+ * &lt;include keyboardLayout="@xml/other_keys"&gt;
* ...
- * &gt;/Row&lt;
- * &gt;include keyboardLayout="@xml/other_rows"&lt;
+ * &lt;/Row&gt;
+ * &lt;include keyboardLayout="@xml/other_rows"&gt;
* ...
- * &gt;/Keyboard&lt;
+ * &lt;/Keyboard&gt;
* </pre>
- * The XML file which is included in other file must have &gt;merge&lt; as root element,
+ * The XML file which is included in other file must have &lt;merge&gt; as root element,
* such as:
* <pre>
- * &gt;!-- xml/other_keys.xml --&lt;
- * &gt;merge&lt;
- * &gt;Key key_attributes* /&lt;
+ * &lt;!-- xml/other_keys.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Key key_attributes* /&gt;
* ...
- * &gt;/merge&lt;
+ * &lt;/merge&gt;
* </pre>
* and
* <pre>
- * &gt;!-- xml/other_rows.xml --&lt;
- * &gt;merge&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;/Row&lt;
+ * &lt;!-- xml/other_rows.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;/Row&gt;
* ...
- * &gt;/merge&lt;
+ * &lt;/merge&gt;
* </pre>
* You can also use switch-case-default tags to select Rows and Keys.
* <pre>
- * &gt;switch&lt;
- * &gt;case case_attribute*&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/case&lt;
+ * &lt;switch&gt;
+ * &lt;case case_attribute*&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/case&gt;
* ...
- * &gt;default&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/default&lt;
- * &gt;/switch&lt;
+ * &lt;default&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/default&gt;
+ * &lt;/switch&gt;
* </pre>
* You can declare Key style and specify styles within Key tags.
* <pre>
- * &gt;switch&lt;
- * &gt;case mode="email"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * &lt;switch&gt;
+ * &lt;case mode="email"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel=".com"
- * /&lt;
- * &gt;/case&lt;
- * &gt;case mode="url"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;case mode="url"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
* keyLabel="http://"
- * /&lt;
- * &gt;/case&lt;
- * &gt;/switch&lt;
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;/switch&gt;
* ...
- * &gt;Key keyStyle="shift-key" ... /&lt;
+ * &lt;Key keyStyle="shift-key" ... /&gt;
* </pre>
*/
@@ -600,9 +611,6 @@ public class Keyboard {
}
public float getKeyX(TypedArray keyAttr) {
- final int widthType = Builder.getEnumValue(keyAttr,
- R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
-
final int keyboardRightEdge = mParams.mOccupiedWidth
- mParams.mHorizontalEdgesPadding;
if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
@@ -864,10 +872,12 @@ public class Keyboard {
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard);
try {
- if (a.hasValue(R.styleable.Keyboard_horizontalGap))
+ if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
- if (a.hasValue(R.styleable.Keyboard_verticalGap))
+ }
+ if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
+ }
return new Row(mResources, mParams, parser, mCurrentY);
} finally {
a.recycle();
@@ -915,7 +925,9 @@ public class Keyboard {
throws XmlPullParserException, IOException {
if (skip) {
XmlParseUtils.checkEndTag(TAG_KEY, parser);
- if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
+ if (DEBUG) {
+ startEndTag("<%s /> skipped", TAG_KEY);
+ }
} else {
final Key key = new Key(mResources, mParams, row, parser);
if (DEBUG) {
@@ -1093,9 +1105,9 @@ public class Keyboard {
private boolean parseCaseCondition(XmlPullParser parser) {
final KeyboardId id = mParams.mId;
- if (id == null)
+ if (id == null) {
return true;
-
+ }
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Case);
try {
@@ -1199,9 +1211,9 @@ public class Keyboard {
// If <case> does not have "index" attribute, that means this <case> is wild-card for
// the attribute.
final TypedValue v = a.peekValue(index);
- if (v == null)
+ if (v == null) {
return true;
-
+ }
if (isIntegerValue(v)) {
return intValue == a.getInt(index, 0);
} else if (isStringValue(v)) {
@@ -1212,8 +1224,9 @@ public class Keyboard {
private static boolean stringArrayContains(String[] array, String value) {
for (final String elem : array) {
- if (elem.equals(value))
+ if (elem.equals(value)) {
return true;
+ }
}
return false;
}
@@ -1236,16 +1249,18 @@ public class Keyboard {
TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
try {
- if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
+ if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
+ "/> needs styleName attribute", parser);
+ }
if (DEBUG) {
startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
- keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
- skip ? " skipped" : "");
+ keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
+ skip ? " skipped" : "");
}
- if (!skip)
+ if (!skip) {
mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
+ }
} finally {
keyStyleAttr.recycle();
keyAttrs.recycle();
@@ -1266,8 +1281,9 @@ public class Keyboard {
}
private void endRow(Row row) {
- if (mCurrentRow == null)
+ if (mCurrentRow == null) {
throw new InflateException("orphan end row tag");
+ }
if (mRightEdgeKey != null) {
mRightEdgeKey.markAsRightEdge(mParams);
mRightEdgeKey = null;
@@ -1303,8 +1319,9 @@ public class Keyboard {
public static float getDimensionOrFraction(TypedArray a, int index, int base,
float defValue) {
final TypedValue value = a.peekValue(index);
- if (value == null)
+ if (value == null) {
return defValue;
+ }
if (isFractionValue(value)) {
return a.getFraction(index, base, base, defValue);
} else if (isDimensionValue(value)) {
@@ -1315,8 +1332,9 @@ public class Keyboard {
public static int getEnumValue(TypedArray a, int index, int defValue) {
final TypedValue value = a.peekValue(index);
- if (value == null)
+ if (value == null) {
return defValue;
+ }
if (isIntegerValue(value)) {
return a.getInt(index, defValue);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 275aacf36..5c8f78f5e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -16,6 +16,9 @@
package com.android.inputmethod.keyboard;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputPointers;
+
public interface KeyboardActionListener {
/**
@@ -42,20 +45,16 @@ public interface KeyboardActionListener {
*
* @param primaryCode this is the code of the key that was pressed
* @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}.
- * If it's called on insertion from the suggestion strip, it should be
- * {@link #SUGGESTION_STRIP_COORDINATE}.
+ * {@link PointerTracker} or so, the value should be
+ * {@link Constants#NOT_A_COORDINATE}. If it's called on insertion from the
+ * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
* @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}.
- * If it's called on insertion from the suggestion strip, it should be
- * {@link #SUGGESTION_STRIP_COORDINATE}.
+ * {@link PointerTracker} or so, the value should be
+ * {@link Constants#NOT_A_COORDINATE}.If it's called on insertion from the
+ * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
*/
public void onCodeInput(int primaryCode, int x, int y);
- public static final int NOT_A_TOUCH_COORDINATE = -1;
- public static final int SUGGESTION_STRIP_COORDINATE = -2;
- public static final int SPELL_CHECKER_COORDINATE = -3;
-
/**
* Sends a sequence of characters to the listener.
*
@@ -64,6 +63,24 @@ public interface KeyboardActionListener {
public void onTextInput(CharSequence text);
/**
+ * Called when user started batch input.
+ */
+ public void onStartBatchInput();
+
+ /**
+ * Sends the ongoing batch input points data.
+ * @param batchPointers the batch input points representing the user input
+ */
+ public void onUpdateBatchInput(InputPointers batchPointers);
+
+ /**
+ * Sends the final batch input points data.
+ *
+ * @param batchPointers the batch input points representing the user input
+ */
+ public void onEndBatchInput(InputPointers batchPointers);
+
+ /**
* Called when user released a finger outside any key.
*/
public void onCancelInput();
@@ -84,10 +101,24 @@ public interface KeyboardActionListener {
@Override
public void onTextInput(CharSequence text) {}
@Override
+ public void onStartBatchInput() {}
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {}
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {}
+ @Override
public void onCancelInput() {}
@Override
public boolean onCustomRequest(int requestCode) {
return false;
}
+
+ // TODO: Remove this method when the vertical correction is removed.
+ public static boolean isInvalidCoordinate(int coordinate) {
+ // Detect {@link Constants#NOT_A_COORDINATE},
+ // {@link Constants#SUGGESTION_STRIP_COORDINATE}, and
+ // {@link Constants#SPELL_CHECKER_COORDINATE}.
+ return coordinate < 0;
+ }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 233716acf..1e5277345 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -55,10 +55,15 @@ public class KeyboardId {
public static final int ELEMENT_PHONE_SYMBOLS = 8;
public static final int ELEMENT_NUMBER = 9;
+ public static final int FORM_FACTOR_PHONE = 0;
+ public static final int FORM_FACTOR_TABLET7 = 1;
+ public static final int FORM_FACTOR_TABLET10 = 2;
+
private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
public final InputMethodSubtype mSubtype;
public final Locale mLocale;
+ public final int mDeviceFormFactor;
public final int mOrientation;
public final int mWidth;
public final int mMode;
@@ -72,11 +77,12 @@ public class KeyboardId {
private final int mHashCode;
- public KeyboardId(int elementId, InputMethodSubtype subtype, int orientation, int width,
- int mode, EditorInfo editorInfo, boolean clobberSettingsKey, boolean shortcutKeyEnabled,
- boolean hasShortcutKey, boolean languageSwitchKeyEnabled) {
+ public KeyboardId(int elementId, InputMethodSubtype subtype, int deviceFormFactor,
+ int orientation, int width, int mode, EditorInfo editorInfo, boolean clobberSettingsKey,
+ boolean shortcutKeyEnabled, boolean hasShortcutKey, boolean languageSwitchKeyEnabled) {
mSubtype = subtype;
mLocale = SubtypeLocale.getSubtypeLocale(subtype);
+ mDeviceFormFactor = deviceFormFactor;
mOrientation = orientation;
mWidth = width;
mMode = mode;
@@ -94,6 +100,7 @@ public class KeyboardId {
private static int computeHashCode(KeyboardId id) {
return Arrays.hashCode(new Object[] {
+ id.mDeviceFormFactor,
id.mOrientation,
id.mElementId,
id.mMode,
@@ -115,7 +122,8 @@ public class KeyboardId {
private boolean equals(KeyboardId other) {
if (other == this)
return true;
- return other.mOrientation == mOrientation
+ return other.mDeviceFormFactor == mDeviceFormFactor
+ && other.mOrientation == mOrientation
&& other.mElementId == mElementId
&& other.mMode == mMode
&& other.mWidth == mWidth
@@ -137,11 +145,13 @@ public class KeyboardId {
}
public boolean navigateNext() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0;
+ return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0
+ || imeAction() == EditorInfo.IME_ACTION_NEXT;
}
public boolean navigatePrevious() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0;
+ return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0
+ || imeAction() == EditorInfo.IME_ACTION_PREVIOUS;
}
public boolean passwordInput() {
@@ -182,11 +192,11 @@ public class KeyboardId {
@Override
public String toString() {
- return String.format("[%s %s:%s %s%d %s %s %s%s%s%s%s%s%s%s]",
+ return String.format("[%s %s:%s %s-%s:%d %s %s %s%s%s%s%s%s%s%s]",
elementIdToName(mElementId),
mLocale,
mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
- (mOrientation == 1 ? "port" : "land"), mWidth,
+ deviceFormFactor(mDeviceFormFactor), (mOrientation == 1 ? "port" : "land"), mWidth,
modeName(mMode),
imeAction(),
(navigateNext() ? "navigateNext" : ""),
@@ -224,6 +234,15 @@ public class KeyboardId {
}
}
+ public static String deviceFormFactor(int devoceFormFactor) {
+ switch (devoceFormFactor) {
+ case FORM_FACTOR_PHONE: return "phone";
+ case FORM_FACTOR_TABLET7: return "tablet7";
+ case FORM_FACTOR_TABLET10: return "tablet10";
+ default: return null;
+ }
+ }
+
public static String modeName(int mode) {
switch (mode) {
case MODE_TEXT: return "text";
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 8c7246855..76ac3de22 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,12 +29,14 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.InputType;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.InputTypeUtils;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -70,7 +72,7 @@ public class KeyboardLayoutSet {
private final Params mParams;
private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
- new HashMap<KeyboardId, SoftReference<Keyboard>>();
+ CollectionUtils.newHashMap();
private static final KeysCache sKeysCache = new KeysCache();
public static class KeyboardLayoutSetException extends RuntimeException {
@@ -83,11 +85,7 @@ public class KeyboardLayoutSet {
}
public static class KeysCache {
- private final HashMap<Key, Key> mMap;
-
- public KeysCache() {
- mMap = new HashMap<Key, Key>();
- }
+ private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap();
public void clear() {
mMap.clear();
@@ -114,11 +112,12 @@ public class KeyboardLayoutSet {
boolean mNoSettingsKey;
boolean mLanguageSwitchKeyEnabled;
InputMethodSubtype mSubtype;
+ int mDeviceFormFactor;
int mOrientation;
int mWidth;
- // KeyboardLayoutSet element id to element's parameters map.
- final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
- new HashMap<Integer, ElementParams>();
+ // Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
+ final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
+ CollectionUtils.newSparseArray();
static class ElementParams {
int mKeyboardXmlId;
@@ -210,9 +209,10 @@ public class KeyboardLayoutSet {
final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype);
final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage;
final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain);
- return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mOrientation,
- params.mWidth, params.mMode, params.mEditorInfo, params.mNoSettingsKey,
- voiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled);
+ return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor,
+ params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo,
+ params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey,
+ params.mLanguageSwitchKeyEnabled);
}
public static class Builder {
@@ -238,9 +238,11 @@ public class KeyboardLayoutSet {
mPackageName, NO_SETTINGS_KEY, mEditorInfo);
}
- public Builder setScreenGeometry(int orientation, int widthPixels) {
- mParams.mOrientation = orientation;
- mParams.mWidth = widthPixels;
+ public Builder setScreenGeometry(int deviceFormFactor, int orientation, int widthPixels) {
+ final Params params = mParams;
+ params.mDeviceFormFactor = deviceFormFactor;
+ params.mOrientation = orientation;
+ params.mWidth = widthPixels;
return this;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2e4ce199e..fd789f029 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -21,7 +21,6 @@ import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.Log;
import android.view.ContextThemeWrapper;
-import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
@@ -38,7 +37,7 @@ import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SettingsValues;
import com.android.inputmethod.latin.SubtypeSwitcher;
-import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WordComposer;
public class KeyboardSwitcher implements KeyboardState.SwitchActions {
private static final String TAG = KeyboardSwitcher.class.getSimpleName();
@@ -46,24 +45,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
static class KeyboardTheme {
- public final String mName;
public final int mThemeId;
public final int mStyleId;
- public KeyboardTheme(String name, int themeId, int styleId) {
- mName = name;
+ // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
+ // in values/style.xml.
+ public KeyboardTheme(int themeId, int styleId) {
mThemeId = themeId;
mStyleId = styleId;
}
}
private static final KeyboardTheme[] KEYBOARD_THEMES = {
- new KeyboardTheme("Basic", 0, R.style.KeyboardTheme),
- new KeyboardTheme("HighContrast", 1, R.style.KeyboardTheme_HighContrast),
- new KeyboardTheme("Stone", 6, R.style.KeyboardTheme_Stone),
- new KeyboardTheme("Stne.Bold", 7, R.style.KeyboardTheme_Stone_Bold),
- new KeyboardTheme("GingerBread", 8, R.style.KeyboardTheme_Gingerbread),
- new KeyboardTheme("IceCreamSandwich", 5, R.style.KeyboardTheme_IceCreamSandwich),
+ new KeyboardTheme(0, R.style.KeyboardTheme),
+ new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast),
+ new KeyboardTheme(6, R.style.KeyboardTheme_Stone),
+ new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold),
+ new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread),
+ new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
};
private SubtypeSwitcher mSubtypeSwitcher;
@@ -71,7 +70,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
private boolean mForceNonDistinctMultitouch;
private InputView mCurrentInputView;
- private LatinKeyboardView mKeyboardView;
+ private MainKeyboardView mKeyboardView;
private LatinIME mLatinIME;
private Resources mResources;
@@ -137,8 +136,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
mThemeContext, editorInfo);
- builder.setScreenGeometry(mThemeContext.getResources().getConfiguration().orientation,
- mThemeContext.getResources().getDisplayMetrics().widthPixels);
+ final Resources res = mThemeContext.getResources();
+ builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor),
+ res.getConfiguration().orientation, res.getDisplayMetrics().widthPixels);
builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
builder.setOptions(
settingsValues.isVoiceKeyEnabled(editorInfo),
@@ -169,19 +169,20 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
private void setKeyboard(final Keyboard keyboard) {
- final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
- mKeyboardView.setKeyboard(keyboard);
+ final MainKeyboardView keyboardView = mKeyboardView;
+ final Keyboard oldKeyboard = keyboardView.getKeyboard();
+ keyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
- mKeyboardView.setKeyPreviewPopupEnabled(
+ keyboardView.setKeyPreviewPopupEnabled(
SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources),
SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
- mKeyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
- mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+ keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
+ keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
final boolean subtypeChanged = (oldKeyboard == null)
|| !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
keyboard.mId.mLocale);
- mKeyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
+ keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true));
}
@@ -265,7 +266,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startDoubleTapTimer();
@@ -275,7 +276,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelDoubleTapTimer();
@@ -285,7 +286,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public boolean isInDoubleTapTimeout() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
return (keyboardView != null)
? keyboardView.getTimerProxy().isInDoubleTapTimeout() : false;
}
@@ -293,7 +294,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startLongPressTimer(int code) {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startLongPressTimer(code);
@@ -303,7 +304,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelLongPressTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelLongPressTimer();
@@ -343,33 +344,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
- public LatinKeyboardView getKeyboardView() {
+ public MainKeyboardView getMainKeyboardView() {
return mKeyboardView;
}
- public View onCreateInputView() {
+ public View onCreateInputView(boolean isHardwareAcceleratedDrawingEnabled) {
if (mKeyboardView != null) {
mKeyboardView.closing();
}
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- setContextThemeWrapper(mLatinIME, mKeyboardTheme);
- mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
- R.layout.input_view, null);
- tryGC = false;
- } catch (OutOfMemoryError e) {
- Log.w(TAG, "load keyboard failed: " + e);
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e);
- } catch (InflateException e) {
- Log.w(TAG, "load keyboard failed: " + e);
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e);
- }
- }
+ setContextThemeWrapper(mLatinIME, mKeyboardTheme);
+ mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
+ R.layout.input_view, null);
- mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ if (isHardwareAcceleratedDrawingEnabled) {
+ mKeyboardView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
+ }
mKeyboardView.setKeyboardActionListener(mLatinIME);
if (mForceNonDistinctMultitouch) {
mKeyboardView.setDistinctMultitouch(false);
@@ -396,4 +388,16 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
}
+
+ public int getManualCapsMode() {
+ switch (getKeyboard().mId.mElementId) {
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
+ default:
+ return WordComposer.CAPS_MODE_OFF;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 51a0f537f..0a70605d7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -25,24 +25,29 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Region.Op;
+import android.graphics.Region;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
-import java.util.HashMap;
import java.util.HashSet;
/**
@@ -75,6 +80,8 @@ import java.util.HashSet;
* @attr ref R.styleable#KeyboardView_shadowRadius
*/
public class KeyboardView extends View implements PointerTracker.DrawingProxy {
+ private static final String TAG = KeyboardView.class.getSimpleName();
+
// Miscellaneous constants
private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
@@ -94,49 +101,46 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// The maximum key label width in the proportion to the key width.
private static final float MAX_LABEL_RATIO = 0.90f;
- private final static int ALPHA_OPAQUE = 255;
-
// Main keyboard
private Keyboard mKeyboard;
protected final KeyDrawParams mKeyDrawParams;
// Key preview
private final int mKeyPreviewLayoutId;
+ private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
protected final KeyPreviewDrawParams mKeyPreviewDrawParams;
private boolean mShowKeyPreviewPopup = true;
private int mDelayAfterPreview;
- private ViewGroup mPreviewPlacer;
+ private final PreviewPlacerView mPreviewPlacerView;
// Drawing
/** True if the entire keyboard needs to be dimmed. */
private boolean mNeedsToDimEntireKeyboard;
- /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
- private boolean mBufferNeedsUpdate;
/** True if all keys should be drawn */
private boolean mInvalidateAllKeys;
/** The keys that should be drawn */
- private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>();
- /** The region of invalidated keys */
- private final Rect mInvalidatedKeysRect = new Rect();
+ private final HashSet<Key> mInvalidatedKeys = CollectionUtils.newHashSet();
+ /** The working rectangle variable */
+ private final Rect mWorkingRect = new Rect();
/** The keyboard bitmap buffer for faster updates */
- private Bitmap mBuffer;
+ /** The clip region to draw keys */
+ private final Region mClipRegion = new Region();
+ private Bitmap mOffscreenBuffer;
/** The canvas for the above mutable keyboard bitmap */
- private Canvas mCanvas;
+ private Canvas mOffscreenCanvas;
private final Paint mPaint = new Paint();
private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
- // This map caches key label text height in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextHeightCache =
- new HashMap<Integer, Float>();
- // This map caches key label text width in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextWidthCache =
- new HashMap<Integer, Float>();
+ // This sparse array caches key label text height in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray();
+ // This sparse array caches key label text width in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray();
private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
- private static final int MSG_DISMISS_KEY_PREVIEW = 1;
+ private static final int MSG_DISMISS_KEY_PREVIEW = 0;
public DrawingHandler(KeyboardView outerInstance) {
super(outerInstance);
@@ -149,7 +153,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_DISMISS_KEY_PREVIEW:
- tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
+ final TextView previewText = keyboardView.mKeyPreviewTexts.get(tracker.mPointerId);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
break;
}
}
@@ -162,7 +169,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
}
- public void cancelAllDismissKeyPreviews() {
+ private void cancelAllDismissKeyPreviews() {
removeMessages(MSG_DISMISS_KEY_PREVIEW);
}
@@ -253,10 +260,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
public void updateKeyHeight(int keyHeight) {
- if (mKeyLetterRatio >= 0.0f)
+ if (mKeyLetterRatio >= 0.0f) {
mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
- if (mKeyLabelRatio >= 0.0f)
+ }
+ if (mKeyLabelRatio >= 0.0f) {
mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio);
+ }
mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio);
mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio);
mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio);
@@ -266,7 +275,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void blendAlpha(Paint paint) {
final int color = paint.getColor();
- paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE,
+ paint.setARGB((paint.getAlpha() * mAnimAlpha) / Constants.Color.ALPHA_OPAQUE,
Color.red(color), Color.green(color), Color.blue(color));
}
}
@@ -338,13 +347,16 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
public void updateKeyHeight(int keyHeight) {
- mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
- mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
+ if (mPreviewTextRatio >= 0.0f) {
+ mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
+ }
+ if (mKeyLetterRatio >= 0.0f) {
+ mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
+ }
}
private static void setAlpha(Drawable drawable, int alpha) {
- if (drawable == null)
- return;
+ if (drawable == null) return;
drawable.setAlpha(alpha);
}
}
@@ -358,9 +370,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-
mKeyDrawParams = new KeyDrawParams(a);
mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
+ mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
if (mKeyPreviewLayoutId == 0) {
mShowKeyPreviewPopup = false;
@@ -371,8 +383,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0);
a.recycle();
- mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
-
+ mPreviewPlacerView = new PreviewPlacerView(context, attrs);
mPaint.setAntiAlias(true);
}
@@ -428,6 +439,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return mShowKeyPreviewPopup;
}
+ public void setGesturePreviewMode(boolean drawsGesturePreviewTrail,
+ boolean drawsGestureFloatingPreviewText) {
+ mPreviewPlacerView.setGesturePreviewMode(
+ drawsGesturePreviewTrail, drawsGestureFloatingPreviewText);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mKeyboard != null) {
@@ -442,67 +459,119 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mBufferNeedsUpdate || mBuffer == null) {
- mBufferNeedsUpdate = false;
- onBufferDraw();
+ if (canvas.isHardwareAccelerated()) {
+ onDrawKeyboard(canvas);
+ return;
+ }
+
+ final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty();
+ if (bufferNeedsUpdates || mOffscreenBuffer == null) {
+ if (maybeAllocateOffscreenBuffer()) {
+ mInvalidateAllKeys = true;
+ maybeCreateOffscreenCanvas();
+ }
+ onDrawKeyboard(mOffscreenCanvas);
}
- canvas.drawBitmap(mBuffer, 0, 0, null);
+ canvas.drawBitmap(mOffscreenBuffer, 0, 0, null);
}
- private void onBufferDraw() {
+ private boolean maybeAllocateOffscreenBuffer() {
final int width = getWidth();
final int height = getHeight();
- if (width == 0 || height == 0)
- return;
- if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
- if (mBuffer != null)
- mBuffer.recycle();
- mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mInvalidateAllKeys = true;
- if (mCanvas != null) {
- mCanvas.setBitmap(mBuffer);
- } else {
- mCanvas = new Canvas(mBuffer);
- }
+ if (width == 0 || height == 0) {
+ return false;
+ }
+ if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width
+ && mOffscreenBuffer.getHeight() == height) {
+ return false;
+ }
+ freeOffscreenBuffer();
+ mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return true;
+ }
+
+ private void freeOffscreenBuffer() {
+ if (mOffscreenBuffer != null) {
+ mOffscreenBuffer.recycle();
+ mOffscreenBuffer = null;
+ }
+ }
+
+ private void maybeCreateOffscreenCanvas() {
+ // TODO: Stop using the offscreen canvas even when in software rendering
+ if (mOffscreenCanvas != null) {
+ mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+ } else {
+ mOffscreenCanvas = new Canvas(mOffscreenBuffer);
}
+ }
+ private void onDrawKeyboard(final Canvas canvas) {
if (mKeyboard == null) return;
- final Canvas canvas = mCanvas;
+ final int width = getWidth();
+ final int height = getHeight();
final Paint paint = mPaint;
final KeyDrawParams params = mKeyDrawParams;
- if (mInvalidateAllKeys || mInvalidatedKeys.isEmpty()) {
- mInvalidatedKeysRect.set(0, 0, width, height);
- canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE);
+ // Calculate clip region and set.
+ final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty();
+ final boolean isHardwareAccelerated = canvas.isHardwareAccelerated();
+ // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
+ if (drawAllKeys || isHardwareAccelerated) {
+ mClipRegion.set(0, 0, width, height);
+ } else {
+ mClipRegion.setEmpty();
+ for (final Key key : mInvalidatedKeys) {
+ if (mKeyboard.hasKey(key)) {
+ final int x = key.mX + getPaddingLeft();
+ final int y = key.mY + getPaddingTop();
+ mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight);
+ mClipRegion.union(mWorkingRect);
+ }
+ }
+ }
+ if (!isHardwareAccelerated) {
+ canvas.clipRegion(mClipRegion, Region.Op.REPLACE);
+ // Draw keyboard background.
canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
+ final Drawable background = getBackground();
+ if (background != null) {
+ background.draw(canvas);
+ }
+ }
+
+ // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
+ if (drawAllKeys || isHardwareAccelerated) {
// Draw all keys.
for (final Key key : mKeyboard.mKeys) {
onDrawKey(key, canvas, paint, params);
}
- if (mNeedsToDimEntireKeyboard) {
- drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint);
- }
} else {
// Draw invalidated keys.
for (final Key key : mInvalidatedKeys) {
- if (!mKeyboard.hasKey(key)) {
- continue;
- }
- final int x = key.mX + getPaddingLeft();
- final int y = key.mY + getPaddingTop();
- mInvalidatedKeysRect.set(x, y, x + key.mWidth, y + key.mHeight);
- canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE);
- canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- onDrawKey(key, canvas, paint, params);
- if (mNeedsToDimEntireKeyboard) {
- drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint);
+ if (mKeyboard.hasKey(key)) {
+ onDrawKey(key, canvas, paint, params);
}
}
}
+ // Overlay a dark rectangle to dim.
+ if (mNeedsToDimEntireKeyboard) {
+ paint.setColor(Color.BLACK);
+ paint.setAlpha(mBackgroundDimAlpha);
+ // Note: clipRegion() above is in effect if it was called.
+ canvas.drawRect(0, 0, width, height, paint);
+ }
+
+ // ResearchLogging indicator.
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger,
+ // and remove this call.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height);
+ }
+
mInvalidatedKeys.clear();
- mInvalidatedKeysRect.setEmpty();
mInvalidateAllKeys = false;
}
@@ -519,7 +588,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
final int keyDrawY = key.mY + getPaddingTop();
canvas.translate(keyDrawX, keyDrawY);
- params.mAnimAlpha = ALPHA_OPAQUE;
+ params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
if (!key.isSpacer()) {
onDrawKeyBackground(key, canvas, params);
}
@@ -766,7 +835,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final Rect mTextBounds = new Rect();
private float getCharHeight(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextHeightCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -778,7 +847,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
private float getCharWidth(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextWidthCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -827,13 +896,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
canvas.translate(-x, -y);
}
- // Overlay a dark rectangle to dim.
- private static void drawDimRectangle(Canvas canvas, Rect rect, int alpha, Paint paint) {
- paint.setColor(Color.BLACK);
- paint.setAlpha(alpha);
- canvas.drawRect(rect, paint);
- }
-
public Paint newDefaultLabelPaint() {
final Paint paint = new Paint();
paint.setAntiAlias(true);
@@ -844,17 +906,35 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void cancelAllMessages() {
mDrawingHandler.cancelAllMessages();
+ if (mPreviewPlacerView != null) {
+ mPreviewPlacerView.cancelAllMessages();
+ }
}
- // Called by {@link PointerTracker} constructor to create a TextView.
- @Override
- public TextView inflateKeyPreviewText() {
+ private TextView getKeyPreviewText(final int pointerId) {
+ TextView previewText = mKeyPreviewTexts.get(pointerId);
+ if (previewText != null) {
+ return previewText;
+ }
final Context context = getContext();
if (mKeyPreviewLayoutId != 0) {
- return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
+ previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
} else {
- return new TextView(context);
+ previewText = new TextView(context);
+ }
+ mKeyPreviewTexts.put(pointerId, previewText);
+ return previewText;
+ }
+
+ private void dismissAllKeyPreviews() {
+ final int pointerCount = mKeyPreviewTexts.size();
+ for (int id = 0; id < pointerCount; id++) {
+ final TextView previewText = mKeyPreviewTexts.get(id);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
}
+ PointerTracker.setReleasedKeyGraphicsToAllKeys();
}
@Override
@@ -863,21 +943,54 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
private void addKeyPreview(TextView keyPreview) {
- if (mPreviewPlacer == null) {
- mPreviewPlacer = new RelativeLayout(getContext());
- final ViewGroup windowContentView =
- (ViewGroup)getRootView().findViewById(android.R.id.content);
- windowContentView.addView(mPreviewPlacer);
+ locatePreviewPlacerView();
+ mPreviewPlacerView.addView(
+ keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
+ }
+
+ private void locatePreviewPlacerView() {
+ if (mPreviewPlacerView.getParent() != null) {
+ return;
+ }
+ final int[] viewOrigin = new int[2];
+ getLocationInWindow(viewOrigin);
+ mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]);
+ final View rootView = getRootView();
+ if (rootView == null) {
+ Log.w(TAG, "Cannot find root view");
+ return;
+ }
+ final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
+ // Note: It'd be very weird if we get null by android.R.id.content.
+ if (windowContentView == null) {
+ Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
+ } else {
+ windowContentView.addView(mPreviewPlacerView);
}
- mPreviewPlacer.addView(
- keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
+ }
+
+ public void showGestureFloatingPreviewText(String gestureFloatingPreviewText) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.dismissGestureFloatingPreviewText();
}
@Override
+ public void showGestureTrail(PointerTracker tracker) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.invalidatePointer(tracker);
+ }
+
+ @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
+ @Override
public void showKeyPreview(PointerTracker tracker) {
if (!mShowKeyPreviewPopup) return;
- final TextView previewText = tracker.getKeyPreviewText();
+ final TextView previewText = getKeyPreviewText(tracker.mPointerId);
// If the key preview has no parent view yet, add it to the ViewGroup which can place
// key preview absolutely in SoftInputWindow.
if (previewText.getParent() == null) {
@@ -967,7 +1080,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void invalidateAllKeys() {
mInvalidatedKeys.clear();
mInvalidateAllKeys = true;
- mBufferNeedsUpdate = true;
invalidate();
}
@@ -985,13 +1097,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mInvalidatedKeys.add(key);
final int x = key.mX + getPaddingLeft();
final int y = key.mY + getPaddingTop();
- mInvalidatedKeysRect.union(x, y, x + key.mWidth, y + key.mHeight);
- mBufferNeedsUpdate = true;
- invalidate(mInvalidatedKeysRect);
+ invalidate(x, y, x + key.mWidth, y + key.mHeight);
}
public void closing() {
- PointerTracker.dismissAllKeyPreviews();
+ dismissAllKeyPreviews();
cancelAllMessages();
mInvalidateAllKeys = true;
@@ -1012,12 +1122,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
closing();
- if (mPreviewPlacer != null) {
- mPreviewPlacer.removeAllViews();
- }
- if (mBuffer != null) {
- mBuffer.recycle();
- mBuffer = null;
- }
+ mPreviewPlacerView.removeAllViews();
+ freeOffscreenBuffer();
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 383298de9..0cc0b6320 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -43,16 +43,18 @@ import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SubtypeLocale;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.Locale;
import java.util.WeakHashMap;
@@ -64,9 +66,9 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#KeyboardView_verticalCorrection
* @attr ref R.styleable#KeyboardView_popupLayout
*/
-public class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
+public class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
SuddenJumpingTouchEventHandler.ProcessMotionEvent {
- private static final String TAG = LatinKeyboardView.class.getSimpleName();
+ private static final String TAG = MainKeyboardView.class.getSimpleName();
// TODO: Kill process when the usability study mode was changed.
private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
@@ -80,10 +82,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw language name on spacebar.
private final int mLanguageOnSpacebarFinalAlpha;
private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
- private static final int ALPHA_OPAQUE = 255;
private boolean mNeedsToDisplayLanguage;
private boolean mHasMultipleEnabledIMEsOrSubtypes;
- private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
+ private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
private final float mSpacebarTextRatio;
private float mSpacebarTextSize;
private final int mSpacebarTextColor;
@@ -99,7 +100,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw altCodeWhileTyping keys.
private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
- private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE;
+ private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
// More keys keyboard
private PopupWindow mMoreKeysWindow;
@@ -109,7 +110,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
new WeakHashMap<Key, MoreKeysPanel>();
private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
- private final PointerTrackerParams mPointerTrackerParams;
private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
protected KeyDetector mKeyDetector;
@@ -119,29 +119,49 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private final KeyTimerHandler mKeyTimerHandler;
- private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
+ private static class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
implements TimerProxy {
+ private static final int MSG_TYPING_STATE_EXPIRED = 0;
private static final int MSG_REPEAT_KEY = 1;
private static final int MSG_LONGPRESS_KEY = 2;
private static final int MSG_DOUBLE_TAP = 3;
- private static final int MSG_TYPING_STATE_EXPIRED = 4;
- private final KeyTimerParams mParams;
- private boolean mInKeyRepeat;
+ private final int mKeyRepeatStartTimeout;
+ private final int mKeyRepeatInterval;
+ private final int mLongPressKeyTimeout;
+ private final int mLongPressShiftKeyTimeout;
+ private final int mIgnoreAltCodeKeyTimeout;
- public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
+ public KeyTimerHandler(final MainKeyboardView outerInstance,
+ final TypedArray mainKeyboardViewAttr) {
super(outerInstance);
- mParams = params;
+
+ mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
+ mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatInterval, 0);
+ mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
+ mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
+ mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
}
@Override
public void handleMessage(Message msg) {
- final LatinKeyboardView keyboardView = getOuterInstance();
+ final MainKeyboardView keyboardView = getOuterInstance();
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
+ case MSG_TYPING_STATE_EXPIRED:
+ startWhileTypingFadeinAnimation(keyboardView);
+ break;
case MSG_REPEAT_KEY:
- tracker.onRegisterKey(tracker.getKey());
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
+ final Key currentKey = tracker.getKey();
+ if (currentKey != null && currentKey.mCode == msg.arg1) {
+ tracker.onRegisterKey(currentKey);
+ startKeyRepeatTimer(tracker, mKeyRepeatInterval);
+ }
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
@@ -150,30 +170,27 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
}
break;
- case MSG_TYPING_STATE_EXPIRED:
- cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
- keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
- break;
}
}
private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
- sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay);
+ final Key key = tracker.getKey();
+ if (key == null) return;
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
}
@Override
public void startKeyRepeatTimer(PointerTracker tracker) {
- mInKeyRepeat = true;
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout);
+ startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
}
public void cancelKeyRepeatTimer() {
- mInKeyRepeat = false;
removeMessages(MSG_REPEAT_KEY);
}
+ // TODO: Suppress layout changes in key repeat mode
public boolean isInKeyRepeat() {
- return mInKeyRepeat;
+ return hasMessages(MSG_REPEAT_KEY);
}
@Override
@@ -182,7 +199,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final int delay;
switch (code) {
case Keyboard.CODE_SHIFT:
- delay = mParams.mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftKeyTimeout;
break;
default:
delay = 0;
@@ -203,15 +220,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final int delay;
switch (key.mCode) {
case Keyboard.CODE_SHIFT:
- delay = mParams.mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftKeyTimeout;
break;
default:
if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
// We use longer timeout for sliding finger input started from the symbols
// mode key.
- delay = mParams.mLongPressKeyTimeout * 3;
+ delay = mLongPressKeyTimeout * 3;
} else {
- delay = mParams.mLongPressKeyTimeout;
+ delay = mLongPressKeyTimeout;
}
break;
}
@@ -225,7 +242,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
removeMessages(MSG_LONGPRESS_KEY);
}
- public static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
+ private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
final ObjectAnimator animatorToStart) {
float startFraction = 0.0f;
if (animatorToCancel.isStarted()) {
@@ -237,18 +254,39 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
animatorToStart.setCurrentPlayTime(startTime);
}
+ private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
+ }
+
+ private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
+ }
+
@Override
- public void startTypingStateTimer() {
+ public void startTypingStateTimer(Key typedKey) {
+ if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
+ return;
+ }
+
final boolean isTyping = isTypingState();
removeMessages(MSG_TYPING_STATE_EXPIRED);
+ final MainKeyboardView keyboardView = getOuterInstance();
+
+ // When user hits the space or the enter key, just cancel the while-typing timer.
+ final int typedCode = typedKey.mCode;
+ if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) {
+ startWhileTypingFadeinAnimation(keyboardView);
+ return;
+ }
+
sendMessageDelayed(
- obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout);
+ obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
if (isTyping) {
return;
}
- final LatinKeyboardView keyboardView = getOuterInstance();
- cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
- keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
+ startWhileTypingFadeoutAnimation(keyboardView);
}
@Override
@@ -283,99 +321,53 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
- public static class PointerTrackerParams {
- public final boolean mSlidingKeyInputEnabled;
- public final int mTouchNoiseThresholdTime;
- public final float mTouchNoiseThresholdDistance;
-
- public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
-
- private PointerTrackerParams() {
- mSlidingKeyInputEnabled = false;
- mTouchNoiseThresholdTime =0;
- mTouchNoiseThresholdDistance = 0;
- }
-
- public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
- mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
- R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
- mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
- mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
- R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
- }
- }
-
- static class KeyTimerParams {
- public final int mKeyRepeatStartTimeout;
- public final int mKeyRepeatInterval;
- public final int mLongPressKeyTimeout;
- public final int mLongPressShiftKeyTimeout;
- public final int mIgnoreAltCodeKeyTimeout;
-
- public KeyTimerParams(TypedArray latinKeyboardViewAttr) {
- mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
- mKeyRepeatInterval = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
- mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
- mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
- mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0);
- }
+ public MainKeyboardView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.mainKeyboardViewStyle);
}
- public LatinKeyboardView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.latinKeyboardViewStyle);
- }
-
- public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public MainKeyboardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
mHasDistinctMultitouch = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ final Resources res = getResources();
final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
- Utils.getDeviceOverrideValue(context.getResources(),
+ Utils.getDeviceOverrideValue(res,
R.array.phantom_sudden_move_event_device_list, "false"));
PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
+ attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false);
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
mAutoCorrectionSpacebarLedIcon = a.getDrawable(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon);
- mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio,
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
+ mSpacebarTextRatio = a.getFraction(R.styleable.MainKeyboardView_spacebarTextRatio,
1000, 1000, 1) / 1000.0f;
- mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
+ mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
mSpacebarTextShadowColor = a.getColor(
- R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
+ R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
mLanguageOnSpacebarFinalAlpha = a.getInt(
- R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE);
+ R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
+ Constants.Color.ALPHA_OPAQUE);
final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
-
- final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
- mPointerTrackerParams = new PointerTrackerParams(a);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
final float keyHysteresisDistance = a.getDimension(
- R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
+ R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
mKeyDetector = new KeyDetector(keyHysteresisDistance);
- mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
+ mKeyTimerHandler = new KeyTimerHandler(this, a);
mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
- R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ PointerTracker.setParameters(a);
a.recycle();
- PointerTracker.setParameters(mPointerTrackerParams);
-
mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
languageOnSpacebarFadeoutAnimatorResId, this);
mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
@@ -451,8 +443,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
*/
@Override
public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview
- mKeyTimerHandler.cancelKeyTimers();
+ // Remove any pending messages, except dismissing preview and key repeat.
+ mKeyTimerHandler.cancelLongPressTimer();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
@@ -462,11 +454,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
mSpaceIcon = (mSpaceKey != null)
- ? mSpaceKey.getIcon(keyboard.mIconsSet, ALPHA_OPAQUE) : null;
+ ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_setKeyboard(keyboard);
+ ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
}
// This always needs to be set since the accessibility state can
@@ -474,6 +466,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
}
+ // Note that this method is called from a non-UI thread.
+ public void setMainDictionaryAvailability(boolean mainDictionaryAvailable) {
+ PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
+ }
+
+ public void setGestureHandlingEnabledByUser(boolean gestureHandlingEnabledByUser) {
+ PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
+ }
+
/**
* Returns whether the device has distinct multi-touch panel.
* @return true if the device has distinct multi-touch panel.
@@ -486,21 +487,25 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mHasDistinctMultitouch = hasDistinctMultitouch;
}
- /**
- * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
- * codes for adjacent keys. When disabled, only the primary key code will be
- * reported.
- * @param enabled whether or not the proximity correction is enabled
- */
- public void setProximityCorrectionEnabled(boolean enabled) {
- mKeyDetector.setProximityCorrectionEnabled(enabled);
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // Notify the research logger that the keyboard view has been attached. This is needed
+ // to properly show the splash screen, which requires that the window token of the
+ // KeyboardView be non-null.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
+ }
}
- /**
- * Returns true if proximity correction is enabled.
- */
- public boolean isProximityCorrectionEnabled() {
- return mKeyDetector.isProximityCorrectionEnabled();
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Notify the research logger that the keyboard view has been detached. This is needed
+ // to invalidate the reference of {@link MainKeyboardView} to null.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
+ }
}
@Override
@@ -552,7 +557,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
*/
protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_onLongPress();
+ ResearchLogger.mainKeyboardView_onLongPress();
}
final int primaryCode = parentKey.mCode;
if (parentKey.hasEmbeddedMoreKey()) {
@@ -579,9 +584,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
private void invokeCodeInput(int primaryCode) {
- mKeyboardActionListener.onCodeInput(primaryCode,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
+ mKeyboardActionListener.onCodeInput(
+ primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
private void invokeReleaseKey(int primaryCode) {
@@ -703,7 +707,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime, index, id,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id,
x, y);
}
@@ -755,15 +759,18 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final PointerTracker tracker = PointerTracker.getPointerTracker(
pointerId, this);
final int px, py;
+ final MotionEvent motionEvent;
if (mMoreKeysPanel != null
&& tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
px = mMoreKeysPanel.translateX((int)me.getX(i));
py = mMoreKeysPanel.translateY((int)me.getY(i));
+ motionEvent = null;
} else {
px = (int)me.getX(i);
py = (int)me.getY(i);
+ motionEvent = me;
}
- tracker.onMoveEvent(px, py, eventTime);
+ tracker.onMoveEvent(px, py, eventTime, motionEvent);
if (ENABLE_USABILITY_STUDY_LOG) {
final float pointerSize = me.getSize(i);
final float pointerPressure = me.getPressure(i);
@@ -772,7 +779,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
+ pointerSize + "," + pointerPressure);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime,
i, pointerId, px, py);
}
}
@@ -803,20 +810,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return false;
}
- @Override
- public void draw(Canvas c) {
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- super.draw(c);
- tryGC = false;
- } catch (OutOfMemoryError e) {
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e);
- }
- }
- }
-
/**
* Receives hover events from the input framework.
*
@@ -861,7 +854,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mNeedsToDisplayLanguage = false;
} else {
if (subtypeChanged && needsToDisplayLanguage) {
- setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE);
+ setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
if (animator.isStarted()) {
animator.cancel();
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index be7644fb5..e513a1477 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -25,6 +25,8 @@ import android.widget.PopupWindow;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.R;
/**
@@ -49,7 +51,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
public void onCodeInput(int primaryCode, int x, int y) {
// Because a more keys keyboard doesn't need proximity characters correction, we don't
// send touch event coordinates.
- mListener.onCodeInput(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE);
+ mListener.onCodeInput(
+ primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
@Override
@@ -58,6 +61,21 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
}
@Override
+ public void onStartBatchInput() {
+ mListener.onStartBatchInput();
+ }
+
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {
+ mListener.onUpdateBatchInput(batchPointers);
+ }
+
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mListener.onEndBatchInput(batchPointers);
+ }
+
+ @Override
public void onCancelInput() {
mListener.onCancelInput();
}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index babf6ec99..b5b3ef65f 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -16,26 +16,39 @@
package com.android.inputmethod.keyboard;
+import android.content.res.TypedArray;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TextView;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.keyboard.internal.GestureStroke;
+import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewTrail;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.ResearchLogger;
+import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
-public class PointerTracker {
+public class PointerTracker implements PointerTrackerQueue.Element {
private static final String TAG = PointerTracker.class.getSimpleName();
private static final boolean DEBUG_EVENT = false;
private static final boolean DEBUG_MOVE_EVENT = false;
private static final boolean DEBUG_LISTENER = false;
private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
+ /** True if {@link PointerTracker}s should handle gesture events. */
+ private static boolean sShouldHandleGesture = false;
+ private static boolean sMainDictionaryAvailable = false;
+ private static boolean sGestureHandlingEnabledByInputField = false;
+ private static boolean sGestureHandlingEnabledByUser = false;
+
+ private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
+
public interface KeyEventHandler {
/**
* Get KeyDetector object that is used for this PointerTracker.
@@ -65,13 +78,13 @@ public class PointerTracker {
public interface DrawingProxy extends MoreKeysPanel.Controller {
public void invalidateKey(Key key);
- public TextView inflateKeyPreviewText();
public void showKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public void showGestureTrail(PointerTracker tracker);
}
public interface TimerProxy {
- public void startTypingStateTimer();
+ public void startTypingStateTimer(Key typedKey);
public boolean isTypingState();
public void startKeyRepeatTimer(PointerTracker tracker);
public void startLongPressTimer(PointerTracker tracker);
@@ -84,7 +97,7 @@ public class PointerTracker {
public static class Adapter implements TimerProxy {
@Override
- public void startTypingStateTimer() {}
+ public void startTypingStateTimer(Key typedKey) {}
@Override
public boolean isTypingState() { return false; }
@Override
@@ -106,12 +119,41 @@ public class PointerTracker {
}
}
+ static class PointerTrackerParams {
+ public final boolean mSlidingKeyInputEnabled;
+ public final int mTouchNoiseThresholdTime;
+ public final float mTouchNoiseThresholdDistance;
+ public final int mTouchNoiseThresholdDistanceSquared;
+
+ public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
+
+ private PointerTrackerParams() {
+ mSlidingKeyInputEnabled = false;
+ mTouchNoiseThresholdTime = 0;
+ mTouchNoiseThresholdDistance = 0.0f;
+ mTouchNoiseThresholdDistanceSquared = 0;
+ }
+
+ public PointerTrackerParams(TypedArray mainKeyboardViewAttr) {
+ mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
+ R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
+ mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
+ final float touchNouseThresholdDistance = mainKeyboardViewAttr.getDimension(
+ R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
+ mTouchNoiseThresholdDistance = touchNouseThresholdDistance;
+ mTouchNoiseThresholdDistanceSquared =
+ (int)(touchNouseThresholdDistance * touchNouseThresholdDistance);
+ }
+ }
+
// Parameters for pointer handling.
- private static LatinKeyboardView.PointerTrackerParams sParams;
- private static int sTouchNoiseThresholdDistanceSquared;
+ private static PointerTrackerParams sParams;
private static boolean sNeedsPhantomSuddenMoveEventHack;
- private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
+ private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
+ private static final InputPointers sAggregratedPointers = new InputPointers(
+ GestureStroke.DEFAULT_CAPACITY);
private static PointerTrackerQueue sPointerTrackerQueue;
public final int mPointerId;
@@ -123,7 +165,14 @@ public class PointerTracker {
private Keyboard mKeyboard;
private int mKeyQuarterWidthSquared;
- private final TextView mKeyPreviewText;
+
+ private boolean mIsAlphabetKeyboard;
+ private boolean mIsPossibleGesture = false;
+ private boolean mInGesture = false;
+
+ // TODO: Remove these variables
+ private int mLastRecognitionPointSize = 0;
+ private long mLastRecognitionTime = 0;
// The position and time at which first down event occurred.
private long mDownTime;
@@ -148,9 +197,6 @@ public class PointerTracker {
// true if this pointer has been long-pressed and is showing a more keys panel.
private boolean mIsShowingMoreKeysPanel;
- // true if this pointer is repeatable key
- private boolean mIsRepeatableKey;
-
// true if this pointer is in sliding key input
boolean mIsInSlidingKeyInput;
@@ -164,6 +210,8 @@ public class PointerTracker {
private static final KeyboardActionListener EMPTY_LISTENER =
new KeyboardActionListener.Adapter();
+ private final GestureStrokeWithPreviewTrail mGestureStrokeWithPreviewTrail;
+
public static void init(boolean hasDistinctMultitouch,
boolean needsPhantomSuddenMoveEventHack) {
if (hasDistinctMultitouch) {
@@ -172,17 +220,32 @@ public class PointerTracker {
sPointerTrackerQueue = null;
}
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
+ sParams = PointerTrackerParams.DEFAULT;
+ }
- setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
+ public static void setParameters(final TypedArray mainKeyboardViewAttr) {
+ sParams = new PointerTrackerParams(mainKeyboardViewAttr);
}
- public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
- sParams = params;
- sTouchNoiseThresholdDistanceSquared = (int)(
- params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
+ private static void updateGestureHandlingMode() {
+ sShouldHandleGesture = sMainDictionaryAvailable
+ && sGestureHandlingEnabledByInputField
+ && sGestureHandlingEnabledByUser
+ && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
}
- public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
+ // Note that this method is called from a non-UI thread.
+ public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
+ sMainDictionaryAvailable = mainDictionaryAvailable;
+ updateGestureHandlingMode();
+ }
+
+ public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
+ sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
+ updateGestureHandlingMode();
+ }
+
+ public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
final ArrayList<PointerTracker> trackers = sTrackers;
// Create pointer trackers until we can get 'id+1'-th tracker, if needed.
@@ -198,54 +261,92 @@ public class PointerTracker {
return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
}
- public static void setKeyboardActionListener(KeyboardActionListener listener) {
- for (final PointerTracker tracker : sTrackers) {
+ public static void setKeyboardActionListener(final KeyboardActionListener listener) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.mListener = listener;
}
}
- public static void setKeyDetector(KeyDetector keyDetector) {
- for (final PointerTracker tracker : sTrackers) {
+ public static void setKeyDetector(final KeyDetector keyDetector) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.setKeyDetectorInner(keyDetector);
// Mark that keyboard layout has been changed.
tracker.mKeyboardLayoutHasBeenChanged = true;
}
+ final Keyboard keyboard = keyDetector.getKeyboard();
+ sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
+ updateGestureHandlingMode();
}
- public static void dismissAllKeyPreviews() {
- for (final PointerTracker tracker : sTrackers) {
- tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
+ public static void setReleasedKeyGraphicsToAllKeys() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
}
}
- public PointerTracker(int id, KeyEventHandler handler) {
- if (handler == null)
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ private static InputPointers getIncrementalBatchPoints() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStrokeWithPreviewTrail.appendIncrementalBatchPoints(
+ sAggregratedPointers);
+ }
+ return sAggregratedPointers;
+ }
+
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ private static InputPointers getAllBatchPoints() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStrokeWithPreviewTrail.appendAllBatchPoints(sAggregratedPointers);
+ }
+ return sAggregratedPointers;
+ }
+
+ // TODO: To handle multi-touch gestures we may want to move this method to
+ // {@link PointerTrackerQueue}.
+ public static void clearBatchInputPointsOfAllPointerTrackers() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
+ tracker.mGestureStrokeWithPreviewTrail.reset();
+ }
+ sAggregratedPointers.reset();
+ }
+
+ private PointerTracker(final int id, final KeyEventHandler handler) {
+ if (handler == null) {
throw new NullPointerException();
+ }
mPointerId = id;
+ mGestureStrokeWithPreviewTrail = new GestureStrokeWithPreviewTrail(id);
setKeyDetectorInner(handler.getKeyDetector());
mListener = handler.getKeyboardActionListener();
mDrawingProxy = handler.getDrawingProxy();
mTimerProxy = handler.getTimerProxy();
- mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
- }
-
- public TextView getKeyPreviewText() {
- return mKeyPreviewText;
}
// Returns true if keyboard has been changed by this callback.
- private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
+ private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
+ if (mInGesture) {
+ return false;
+ }
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, "onPress : " + KeyDetector.printableCode(key)
+ " ignoreModifier=" + ignoreModifierKey
+ " enabled=" + key.isEnabled());
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(key,
- ignoreModifierKey);
- }
if (ignoreModifierKey) {
return false;
}
@@ -253,9 +354,7 @@ public class PointerTracker {
mListener.onPressKey(key.mCode);
final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
mKeyboardLayoutHasBeenChanged = false;
- if (!key.altCodeWhileTyping() && !key.isModifier()) {
- mTimerProxy.startTypingStateTimer();
- }
+ mTimerProxy.startTypingStateTimer(key);
return keyboardLayoutHasBeenChanged;
}
return false;
@@ -263,7 +362,8 @@ public class PointerTracker {
// Note that we need primaryCode argument because the keyboard may in shifted state and the
// primaryCode is different from {@link Key#mCode}.
- private void callListenerOnCodeInput(Key key, int primaryCode, int x, int y) {
+ private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
+ final int y) {
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
final int code = altersCode ? key.mAltCode : primaryCode;
@@ -292,7 +392,11 @@ public class PointerTracker {
// Note that we need primaryCode argument because the keyboard may in shifted state and the
// primaryCode is different from {@link Key#mCode}.
- private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
+ private void callListenerOnRelease(final Key key, final int primaryCode,
+ final boolean withSliding) {
+ if (mInGesture) {
+ return;
+ }
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
@@ -312,21 +416,32 @@ public class PointerTracker {
}
private void callListenerOnCancelInput() {
- if (DEBUG_LISTENER)
+ if (DEBUG_LISTENER) {
Log.d(TAG, "onCancelInput");
+ }
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_callListenerOnCancelInput();
}
mListener.onCancelInput();
}
- private void setKeyDetectorInner(KeyDetector keyDetector) {
+ private void setKeyDetectorInner(final KeyDetector keyDetector) {
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
+ mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
+ mGestureStrokeWithPreviewTrail.setGestureSampleLength(mKeyboard.mMostCommonKeyWidth);
+ final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
+ if (newKey != mCurrentKey) {
+ if (mDrawingProxy != null) {
+ setReleasedKeyGraphics(mCurrentKey);
+ }
+ // Keep {@link #mCurrentKey} that comes from previous keyboard.
+ }
final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
}
+ @Override
public boolean isInSlidingKeyInput() {
return mIsInSlidingKeyInput;
}
@@ -335,15 +450,16 @@ public class PointerTracker {
return mCurrentKey;
}
+ @Override
public boolean isModifier() {
return mCurrentKey != null && mCurrentKey.isModifier();
}
- public Key getKeyOn(int x, int y) {
+ public Key getKeyOn(final int x, final int y) {
return mKeyDetector.detectHitKey(x, y);
}
- private void setReleasedKeyGraphics(Key key) {
+ private void setReleasedKeyGraphics(final Key key) {
mDrawingProxy.dismissKeyPreview(this);
if (key == null) {
return;
@@ -374,7 +490,7 @@ public class PointerTracker {
}
}
- private void setPressedKeyGraphics(Key key) {
+ private void setPressedKeyGraphics(final Key key) {
if (key == null) {
return;
}
@@ -386,7 +502,7 @@ public class PointerTracker {
return;
}
- if (!key.noKeyPreview()) {
+ if (!key.noKeyPreview() && !mInGesture) {
mDrawingProxy.showKeyPreview(this);
}
updatePressKeyGraphics(key);
@@ -413,16 +529,20 @@ public class PointerTracker {
}
}
- private void updateReleaseKeyGraphics(Key key) {
+ private void updateReleaseKeyGraphics(final Key key) {
key.onReleased();
mDrawingProxy.invalidateKey(key);
}
- private void updatePressKeyGraphics(Key key) {
+ private void updatePressKeyGraphics(final Key key) {
key.onPressed();
mDrawingProxy.invalidateKey(key);
}
+ public GestureStrokeWithPreviewTrail getGestureStrokeWithPreviewTrail() {
+ return mGestureStrokeWithPreviewTrail;
+ }
+
public int getLastX() {
return mLastX;
}
@@ -435,30 +555,76 @@ public class PointerTracker {
return mDownTime;
}
- private Key onDownKey(int x, int y, long eventTime) {
+ private Key onDownKey(final int x, final int y, final long eventTime) {
mDownTime = eventTime;
return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
}
- private Key onMoveKeyInternal(int x, int y) {
+ private Key onMoveKeyInternal(final int x, final int y) {
mLastX = x;
mLastY = y;
return mKeyDetector.detectHitKey(x, y);
}
- private Key onMoveKey(int x, int y) {
+ private Key onMoveKey(final int x, final int y) {
return onMoveKeyInternal(x, y);
}
- private Key onMoveToNewKey(Key newKey, int x, int y) {
+ private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
mCurrentKey = newKey;
mKeyX = x;
mKeyY = y;
return newKey;
}
- public void processMotionEvent(int action, int x, int y, long eventTime,
- KeyEventHandler handler) {
+ private void startBatchInput() {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onStartBatchInput");
+ }
+ mInGesture = true;
+ mListener.onStartBatchInput();
+ }
+
+ private void updateBatchInput(final InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onUpdateBatchInput(batchPoints);
+ }
+
+ private void endBatchInput(final InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onEndBatchInput(batchPoints);
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ }
+
+ private void abortBatchInput() {
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ }
+
+ private void clearBatchInputRecognitionStateOfThisPointerTracker() {
+ mIsPossibleGesture = false;
+ mInGesture = false;
+ mLastRecognitionPointSize = 0;
+ mLastRecognitionTime = 0;
+ }
+
+ private boolean updateBatchInputRecognitionState(final long eventTime, final int size) {
+ if (size > mLastRecognitionPointSize
+ && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) {
+ mLastRecognitionPointSize = size;
+ mLastRecognitionTime = eventTime;
+ return true;
+ }
+ return false;
+ }
+
+ public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
+ final KeyEventHandler handler) {
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
@@ -469,7 +635,7 @@ public class PointerTracker {
onUpEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_MOVE:
- onMoveEvent(x, y, eventTime);
+ onMoveEvent(x, y, eventTime, null);
break;
case MotionEvent.ACTION_CANCEL:
onCancelEvent(x, y, eventTime);
@@ -477,9 +643,11 @@ public class PointerTracker {
}
}
- public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
- if (DEBUG_EVENT)
+ public void onDownEvent(final int x, final int y, final long eventTime,
+ final KeyEventHandler handler) {
+ if (DEBUG_EVENT) {
printTouchEvent("onDownEvent:", x, y, eventTime);
+ }
mDrawingProxy = handler.getDrawingProxy();
mTimerProxy = handler.getTimerProxy();
@@ -491,7 +659,7 @@ public class PointerTracker {
final int dx = x - mLastX;
final int dy = y - mLastY;
final int distanceSquared = (dx * dx + dy * dy);
- if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
+ if (distanceSquared < sParams.mTouchNoiseThresholdDistanceSquared) {
if (DEBUG_MODE)
Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
+ " distance=" + distanceSquared);
@@ -504,8 +672,8 @@ public class PointerTracker {
}
final PointerTrackerQueue queue = sPointerTrackerQueue;
+ final Key key = getKeyOn(x, y);
if (queue != null) {
- final Key key = getKeyOn(x, y);
if (key != null && key.isModifier()) {
// Before processing a down event of modifier key, all pointers already being
// tracked should be released.
@@ -514,9 +682,20 @@ public class PointerTracker {
queue.add(this);
}
onDownEventInternal(x, y, eventTime);
+ if (queue != null && queue.size() == 1) {
+ mIsPossibleGesture = false;
+ // A gesture should start only from the letter key.
+ if (sShouldHandleGesture && mIsAlphabetKeyboard && !mIsShowingMoreKeysPanel
+ && key != null && Keyboard.isLetterCode(key.mCode)) {
+ mIsPossibleGesture = true;
+ // TODO: pointer times should be relative to first down even in entire batch input
+ // instead of resetting to 0 for each new down event.
+ mGestureStrokeWithPreviewTrail.addPoint(x, y, 0, false);
+ }
+ }
}
- private void onDownEventInternal(int x, int y, long eventTime) {
+ private void onDownEventInternal(final int x, final int y, final long eventTime) {
Key key = onDownKey(x, y, eventTime);
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
// from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
@@ -525,7 +704,6 @@ public class PointerTracker {
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
- mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
mIgnoreModifierKey = false;
if (key != null) {
@@ -542,23 +720,69 @@ public class PointerTracker {
}
}
- private void startSlidingKeyInput(Key key) {
+ private void startSlidingKeyInput(final Key key) {
if (!mIsInSlidingKeyInput) {
mIgnoreModifierKey = key.isModifier();
}
mIsInSlidingKeyInput = true;
}
- public void onMoveEvent(int x, int y, long eventTime) {
- if (DEBUG_MOVE_EVENT)
+ private void onGestureMoveEvent(final PointerTracker tracker, final int x, final int y,
+ final long eventTime, final boolean isHistorical, final Key key) {
+ final int gestureTime = (int)(eventTime - tracker.getDownTime());
+ if (sShouldHandleGesture && mIsPossibleGesture) {
+ final GestureStroke stroke = mGestureStrokeWithPreviewTrail;
+ stroke.addPoint(x, y, gestureTime, isHistorical);
+ if (!mInGesture && stroke.isStartOfAGesture()) {
+ startBatchInput();
+ }
+ }
+
+ if (key != null && mInGesture) {
+ final InputPointers batchPoints = getIncrementalBatchPoints();
+ mDrawingProxy.showGestureTrail(this);
+ if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
+ updateBatchInput(batchPoints);
+ }
+ }
+ }
+
+ public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
+ if (DEBUG_MOVE_EVENT) {
printTouchEvent("onMoveEvent:", x, y, eventTime);
- if (mKeyAlreadyProcessed)
+ }
+ if (mKeyAlreadyProcessed) {
return;
+ }
+
+ if (me != null) {
+ // Add historical points to gesture path.
+ final int pointerIndex = me.findPointerIndex(mPointerId);
+ final int historicalSize = me.getHistorySize();
+ for (int h = 0; h < historicalSize; h++) {
+ final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
+ final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
+ final long historicalTime = me.getHistoricalEventTime(h);
+ onGestureMoveEvent(this, historicalX, historicalY, historicalTime,
+ true /* isHistorical */, null);
+ }
+ }
final int lastX = mLastX;
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
Key key = onMoveKey(x, y);
+
+ // Register move event on gesture tracker.
+ onGestureMoveEvent(this, x, y, eventTime, false /* isHistorical */, key);
+ if (mInGesture) {
+ mIgnoreModifierKey = true;
+ mTimerProxy.cancelLongPressTimer();
+ mIsInSlidingKeyInput = true;
+ mCurrentKey = null;
+ setReleasedKeyGraphics(oldKey);
+ }
+
if (key != null) {
if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys.
@@ -598,20 +822,35 @@ public class PointerTracker {
final int dx = x - lastX;
final int dy = y - lastY;
final int lastMoveSquared = dx * dx + dy * dy;
+ // TODO: Should find a way to balance gesture detection and this hack.
if (sNeedsPhantomSuddenMoveEventHack
- && lastMoveSquared >= mKeyQuarterWidthSquared) {
+ && lastMoveSquared >= mKeyQuarterWidthSquared
+ && !mIsPossibleGesture) {
if (DEBUG_MODE) {
Log.w(TAG, String.format("onMoveEvent:"
+ " phantom sudden move event is translated to "
+ "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
}
+ // TODO: This should be moved to outside of this nested if-clause?
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
}
onUpEventInternal();
onDownEventInternal(x, y, eventTime);
} else {
- mKeyAlreadyProcessed = true;
+ // HACK: If there are currently multiple touches, register the key even if
+ // the finger slides off the key. This defends against noise from some
+ // touch panels when there are close multiple touches.
+ // Caveat: When in chording input mode with a modifier key, we don't use
+ // this hack.
+ final PointerTrackerQueue queue = sPointerTrackerQueue;
+ if (queue != null && queue.size() > 1
+ && !queue.hasModifierKeyOlderThan(this)) {
+ onUpEventInternal();
+ }
+ if (!mIsPossibleGesture) {
+ mKeyAlreadyProcessed = true;
+ }
setReleasedKeyGraphics(oldKey);
}
}
@@ -627,24 +866,29 @@ public class PointerTracker {
if (mIsAllowedSlidingKeyInput) {
onMoveToNewKey(key, x, y);
} else {
- mKeyAlreadyProcessed = true;
+ if (!mIsPossibleGesture) {
+ mKeyAlreadyProcessed = true;
+ }
}
}
}
}
- public void onUpEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
+ public void onUpEvent(final int x, final int y, final long eventTime) {
+ if (DEBUG_EVENT) {
printTouchEvent("onUpEvent :", x, y, eventTime);
+ }
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
- if (mCurrentKey != null && mCurrentKey.isModifier()) {
- // Before processing an up event of modifier key, all pointers already being
- // tracked should be released.
- queue.releaseAllPointersExcept(this, eventTime);
- } else {
- queue.releaseAllPointersOlderThan(this, eventTime);
+ if (!mInGesture) {
+ if (mCurrentKey != null && mCurrentKey.isModifier()) {
+ // Before processing an up event of modifier key, all pointers already being
+ // tracked should be released.
+ queue.releaseAllPointersExcept(this, eventTime);
+ } else {
+ queue.releaseAllPointersOlderThan(this, eventTime);
+ }
}
queue.remove(this);
}
@@ -654,9 +898,11 @@ public class PointerTracker {
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
// This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
// "virtual" up event.
- public void onPhantomUpEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
- printTouchEvent("onPhntEvent:", x, y, eventTime);
+ @Override
+ public void onPhantomUpEvent(final long eventTime) {
+ if (DEBUG_EVENT) {
+ printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
+ }
onUpEventInternal();
mKeyAlreadyProcessed = true;
}
@@ -664,23 +910,42 @@ public class PointerTracker {
private void onUpEventInternal() {
mTimerProxy.cancelKeyTimers();
mIsInSlidingKeyInput = false;
+ mIsPossibleGesture = false;
// Release the last pressed key.
setReleasedKeyGraphics(mCurrentKey);
if (mIsShowingMoreKeysPanel) {
mDrawingProxy.dismissMoreKeysPanel();
mIsShowingMoreKeysPanel = false;
}
- if (mKeyAlreadyProcessed)
+
+ if (mInGesture) {
+ // Register up event on gesture tracker.
+ // TODO: Figure out how to deal with multiple fingers that are in gesture, sliding,
+ // and/or tapping mode?
+ endBatchInput(getAllBatchPoints());
+ if (mCurrentKey != null) {
+ callListenerOnRelease(mCurrentKey, mCurrentKey.mCode, true);
+ mCurrentKey = null;
+ }
+ mDrawingProxy.showGestureTrail(this);
return;
- if (!mIsRepeatableKey) {
+ }
+ // This event will be recognized as a regular code input. Clear unused batch points so they
+ // are not mistakenly included in the next batch event.
+ clearBatchInputPointsOfAllPointerTrackers();
+ if (mKeyAlreadyProcessed) {
+ return;
+ }
+ if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
}
}
- public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
+ public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
+ abortBatchInput();
onLongPressed();
- onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
mIsShowingMoreKeysPanel = true;
+ onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
}
public void onLongPressed() {
@@ -692,9 +957,10 @@ public class PointerTracker {
}
}
- public void onCancelEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
+ public void onCancelEvent(final int x, final int y, final long eventTime) {
+ if (DEBUG_EVENT) {
printTouchEvent("onCancelEvt:", x, y, eventTime);
+ }
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
@@ -714,29 +980,25 @@ public class PointerTracker {
}
}
- private void startRepeatKey(Key key) {
- if (key != null && key.isRepeatable()) {
+ private void startRepeatKey(final Key key) {
+ if (key != null && key.isRepeatable() && !mInGesture) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
- mIsRepeatableKey = true;
- } else {
- mIsRepeatableKey = false;
}
}
- public void onRegisterKey(Key key) {
+ public void onRegisterKey(final Key key) {
if (key != null) {
detectAndSendKey(key, key.mX, key.mY);
- if (!key.altCodeWhileTyping() && !key.isModifier()) {
- mTimerProxy.startTypingStateTimer();
- }
+ mTimerProxy.startTypingStateTimer(key);
}
}
- private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) {
- if (mKeyDetector == null)
+ private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final Key newKey) {
+ if (mKeyDetector == null) {
throw new NullPointerException("keyboard and/or key detector not set");
- Key curKey = mCurrentKey;
+ }
+ final Key curKey = mCurrentKey;
if (newKey == curKey) {
return false;
} else if (curKey != null) {
@@ -747,31 +1009,28 @@ public class PointerTracker {
}
}
- private void startLongPressTimer(Key key) {
- if (key != null && key.isLongPressEnabled()) {
+ private void startLongPressTimer(final Key key) {
+ if (key != null && key.isLongPressEnabled() && !mInGesture) {
mTimerProxy.startLongPressTimer(this);
}
}
- private void detectAndSendKey(Key key, int x, int y) {
+ private void detectAndSendKey(final Key key, final int x, final int y) {
if (key == null) {
callListenerOnCancelInput();
return;
}
- int code = key.mCode;
+ final int code = key.mCode;
callListenerOnCodeInput(key, code, x, y);
callListenerOnRelease(key, code, false);
}
- private long mPreviousEventTime;
-
- private void printTouchEvent(String title, int x, int y, long eventTime) {
+ private void printTouchEvent(final String title, final int x, final int y,
+ final long eventTime) {
final Key key = mKeyDetector.detectHitKey(x, y);
final String code = KeyDetector.printableCode(key);
- final long delta = eventTime - mPreviousEventTime;
Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
- (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code));
- mPreviousEventTime = eventTime;
+ (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, eventTime, code));
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 1207c3fcd..71bf31faa 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,15 +18,16 @@ package com.android.inputmethod.keyboard;
import android.graphics.Rect;
import android.text.TextUtils;
-import android.util.FloatMath;
import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.JniUtils;
import java.util.Arrays;
-import java.util.HashMap;
public class ProximityInfo {
+ /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ * in defines.h */
public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
/** Number of key widths from current touch point to search for nearest keys. */
private static float SEARCH_DISTANCE = 1.2f;
@@ -75,27 +76,6 @@ public class ProximityInfo {
mNativeProximityInfo = createNativeProximityInfo();
}
- // TODO: Remove this public constructor when the native part of the ProximityInfo becomes
- // immutable.
- // This public constructor aims only for test purpose.
- public ProximityInfo(ProximityInfo o) {
- mLocaleStr = o.mLocaleStr;
- mGridWidth = o.mGridWidth;
- mGridHeight = o.mGridHeight;
- mGridSize = o.mGridSize;
- mCellWidth = o.mCellWidth;
- mCellHeight = o.mCellHeight;
- mKeyboardMinWidth = o.mKeyboardMinWidth;
- mKeyboardHeight = o.mKeyboardHeight;
- mKeyHeight = o.mKeyHeight;
- mMostCommonKeyWidth = o.mMostCommonKeyWidth;
- mKeys = o.mKeys;
- mTouchPositionCorrection = o.mTouchPositionCorrection;
- mGridNeighbors = new Key[mGridSize][];
- computeNearestNeighbors();
- mNativeProximityInfo = createNativeProximityInfo();
- }
-
public static ProximityInfo createDummyProximityInfo() {
return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
}
@@ -132,7 +112,7 @@ public class ProximityInfo {
final Key[] keys = mKeys;
final TouchPositionCorrection touchPositionCorrection = mTouchPositionCorrection;
final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
- Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
+ Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE);
for (int i = 0; i < mGridSize; ++i) {
final int proximityCharsLength = gridNeighborKeys[i].length;
for (int j = 0; j < proximityCharsLength; ++j) {
@@ -175,7 +155,9 @@ public class ProximityInfo {
final float radius = touchPositionCorrection.mRadii[row];
sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth;
sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight;
- sweetSpotRadii[i] = radius * FloatMath.sqrt(
+ // Note that, in recent versions of Android, FloatMath is actually slower than
+ // java.lang.Math due to the way the JIT optimizes java.lang.Math.
+ sweetSpotRadii[i] = radius * (float)Math.sqrt(
hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
}
}
@@ -209,10 +191,6 @@ public class ProximityInfo {
private void computeNearestNeighbors() {
final int defaultWidth = mMostCommonKeyWidth;
final Key[] keys = mKeys;
- final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
- for (final Key key : keys) {
- keyCodeMap.put(key.mCode, key);
- }
final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
final int threshold = thresholdBase * thresholdBase;
// Round-up so we don't have any pixels outside the grid
@@ -257,7 +235,7 @@ public class ProximityInfo {
dest[index++] = code;
}
if (index < destLength) {
- dest[index] = KeyDetector.NOT_A_CODE;
+ dest[index] = Constants.NOT_A_CODE;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
new file mode 100644
index 000000000..747627b7d
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.SystemClock;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResizableIntArray;
+
+class GesturePreviewTrail {
+ private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewTrail.PREVIEW_CAPACITY;
+
+ private final GesturePreviewTrailParams mPreviewParams;
+ private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ private int mCurrentStrokeId = -1;
+ private long mCurrentDownTime;
+
+ // Use this value as imaginary zero because x-coordinates may be zero.
+ private static final int DOWN_EVENT_MARKER = -128;
+
+ static class GesturePreviewTrailParams {
+ public final int mFadeoutStartDelay;
+ public final int mFadeoutDuration;
+ public final int mUpdateInterval;
+
+ public GesturePreviewTrailParams(final TypedArray keyboardViewAttr) {
+ mFadeoutStartDelay = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailFadeoutStartDelay, 0);
+ mFadeoutDuration = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailFadeoutDuration, 0);
+ mUpdateInterval = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailUpdateInterval, 0);
+ }
+ }
+
+ public GesturePreviewTrail(final GesturePreviewTrailParams params) {
+ mPreviewParams = params;
+ }
+
+ private static int markAsDownEvent(final int xCoord) {
+ return DOWN_EVENT_MARKER - xCoord;
+ }
+
+ private static boolean isDownEventXCoord(final int xCoordOrMark) {
+ return xCoordOrMark <= DOWN_EVENT_MARKER;
+ }
+
+ private static int getXCoordValue(final int xCoordOrMark) {
+ return isDownEventXCoord(xCoordOrMark)
+ ? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark;
+ }
+
+ public void addStroke(final GestureStrokeWithPreviewTrail stroke, final long downTime) {
+ final int strokeId = stroke.getGestureStrokeId();
+ final boolean isNewStroke = strokeId != mCurrentStrokeId;
+ final int trailSize = mEventTimes.getLength();
+ stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates);
+ final int newTrailSize = mEventTimes.getLength();
+ if (stroke.getGestureStrokePreviewSize() == 0) {
+ return;
+ }
+ if (isNewStroke) {
+ final int elapsedTime = (int)(downTime - mCurrentDownTime);
+ final int[] eventTimes = mEventTimes.getPrimitiveArray();
+ for (int i = 0; i < trailSize; i++) {
+ eventTimes[i] -= elapsedTime;
+ }
+
+ if (newTrailSize > trailSize) {
+ final int[] xCoords = mXCoordinates.getPrimitiveArray();
+ xCoords[trailSize] = markAsDownEvent(xCoords[trailSize]);
+ }
+ mCurrentDownTime = downTime;
+ mCurrentStrokeId = strokeId;
+ }
+ }
+
+ private int getAlpha(final int elapsedTime) {
+ if (elapsedTime < mPreviewParams.mFadeoutStartDelay) {
+ return Constants.Color.ALPHA_OPAQUE;
+ }
+ final int decreasingAlpha = Constants.Color.ALPHA_OPAQUE
+ * (elapsedTime - mPreviewParams.mFadeoutStartDelay)
+ / mPreviewParams.mFadeoutDuration;
+ return Constants.Color.ALPHA_OPAQUE - decreasingAlpha;
+ }
+
+ /**
+ * Draw gesture preview trail
+ * @param canvas The canvas to draw the gesture preview trail
+ * @param paint The paint object to be used to draw the gesture preview trail
+ * @return true if some gesture preview trails remain to be drawn
+ */
+ public boolean drawGestureTrail(final Canvas canvas, final Paint paint) {
+ final int trailSize = mEventTimes.getLength();
+ if (trailSize == 0) {
+ return false;
+ }
+
+ final int[] eventTimes = mEventTimes.getPrimitiveArray();
+ final int[] xCoords = mXCoordinates.getPrimitiveArray();
+ final int[] yCoords = mYCoordinates.getPrimitiveArray();
+ final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentDownTime);
+ final int lingeringDuration = mPreviewParams.mFadeoutStartDelay
+ + mPreviewParams.mFadeoutDuration;
+ int startIndex;
+ for (startIndex = 0; startIndex < trailSize; startIndex++) {
+ final int elapsedTime = sinceDown - eventTimes[startIndex];
+ // Skip too old trail points.
+ if (elapsedTime < lingeringDuration) {
+ break;
+ }
+ }
+
+ if (startIndex < trailSize) {
+ int lastX = getXCoordValue(xCoords[startIndex]);
+ int lastY = yCoords[startIndex];
+ for (int i = startIndex + 1; i < trailSize - 1; i++) {
+ final int x = xCoords[i];
+ final int y = yCoords[i];
+ final int elapsedTime = sinceDown - eventTimes[i];
+ // Draw trail line only when the current point isn't a down point.
+ if (!isDownEventXCoord(x)) {
+ paint.setAlpha(getAlpha(elapsedTime));
+ canvas.drawLine(lastX, lastY, x, y, paint);
+ }
+ lastX = getXCoordValue(x);
+ lastY = y;
+ }
+ }
+
+ // TODO: Implement ring buffer to avoid moving points.
+ // Discard faded out points.
+ final int newSize = trailSize - startIndex;
+ System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
+ System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
+ System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
+ mEventTimes.setLength(newSize);
+ mXCoordinates.setLength(newSize);
+ mYCoordinates.setLength(newSize);
+ return newSize > 0;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
new file mode 100644
index 000000000..292842d22
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.latin.InputPointers;
+import com.android.inputmethod.latin.ResizableIntArray;
+
+public class GestureStroke {
+ public static final int DEFAULT_CAPACITY = 128;
+
+ private final int mPointerId;
+ private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private float mLength;
+ private float mAngle;
+ private int mIncrementalRecognitionSize;
+ private int mLastIncrementalBatchSize;
+ private long mLastPointTime;
+ private int mLastPointX;
+ private int mLastPointY;
+
+ private int mMinGestureLength;
+ private int mMinGestureSampleLength;
+
+ // TODO: Move some of these to resource.
+ private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 0.75f;
+ private static final int MIN_GESTURE_DURATION = 100; // msec
+ private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
+ private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
+ private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
+
+ private static final float DOUBLE_PI = (float)(2.0f * Math.PI);
+
+ public GestureStroke(final int pointerId) {
+ mPointerId = pointerId;
+ }
+
+ public void setGestureSampleLength(final int keyWidth) {
+ // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
+ mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH);
+ mMinGestureSampleLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
+ }
+
+ public boolean isStartOfAGesture() {
+ final int size = mEventTimes.getLength();
+ final int downDuration = (size > 0) ? mEventTimes.get(size - 1) : 0;
+ return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength;
+ }
+
+ public void reset() {
+ mLength = 0;
+ mAngle = 0;
+ mIncrementalRecognitionSize = 0;
+ mLastIncrementalBatchSize = 0;
+ mLastPointTime = 0;
+ mEventTimes.setLength(0);
+ mXCoordinates.setLength(0);
+ mYCoordinates.setLength(0);
+ }
+
+ private void updateLastPoint(final int x, final int y, final int time) {
+ mLastPointTime = time;
+ mLastPointX = x;
+ mLastPointY = y;
+ }
+
+ public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
+ final int size = mEventTimes.getLength();
+ if (size == 0) {
+ mEventTimes.add(time);
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ if (!isHistorical) {
+ updateLastPoint(x, y, time);
+ }
+ return;
+ }
+
+ final int lastX = mXCoordinates.get(size - 1);
+ final int lastY = mYCoordinates.get(size - 1);
+ final float dist = getDistance(lastX, lastY, x, y);
+ if (dist > mMinGestureSampleLength) {
+ mEventTimes.add(time);
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ mLength += dist;
+ final float angle = getAngle(lastX, lastY, x, y);
+ if (size > 1) {
+ final float curvature = getAngleDiff(angle, mAngle);
+ if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
+ if (size > mIncrementalRecognitionSize) {
+ mIncrementalRecognitionSize = size;
+ }
+ }
+ }
+ mAngle = angle;
+ }
+
+ if (!isHistorical) {
+ final int duration = (int)(time - mLastPointTime);
+ if (mLastPointTime != 0 && duration > 0) {
+ final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration;
+ if (speed < GESTURE_RECOG_SPEED_THRESHOLD) {
+ mIncrementalRecognitionSize = size;
+ }
+ }
+ updateLastPoint(x, y, time);
+ }
+ }
+
+ public void appendAllBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mEventTimes.getLength());
+ }
+
+ public void appendIncrementalBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mIncrementalRecognitionSize);
+ }
+
+ private void appendBatchPoints(final InputPointers out, final int size) {
+ out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
+ mLastIncrementalBatchSize, size - mLastIncrementalBatchSize);
+ mLastIncrementalBatchSize = size;
+ }
+
+ private static float getDistance(final int x1, final int y1, final int x2, final int y2) {
+ final float dx = x1 - x2;
+ final float dy = y1 - y2;
+ // Note that, in recent versions of Android, FloatMath is actually slower than
+ // java.lang.Math due to the way the JIT optimizes java.lang.Math.
+ return (float)Math.sqrt(dx * dx + dy * dy);
+ }
+
+ private static float getAngle(final int x1, final int y1, final int x2, final int y2) {
+ final int dx = x1 - x2;
+ final int dy = y1 - y2;
+ if (dx == 0 && dy == 0) return 0;
+ // Would it be faster to call atan2f() directly via JNI? Not sure about what the JIT
+ // does with Math.atan2().
+ return (float)Math.atan2(dy, dx);
+ }
+
+ private static float getAngleDiff(final float a1, final float a2) {
+ final float diff = Math.abs(a1 - a2);
+ if (diff > Math.PI) {
+ return DOUBLE_PI - diff;
+ }
+ return diff;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java
new file mode 100644
index 000000000..6c1a9bc01
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewTrail.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.latin.ResizableIntArray;
+
+public class GestureStrokeWithPreviewTrail extends GestureStroke {
+ public static final int PREVIEW_CAPACITY = 256;
+
+ private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
+ private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
+ private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
+
+ private int mStrokeId;
+ private int mLastPreviewSize;
+
+ public GestureStrokeWithPreviewTrail(final int pointerId) {
+ super(pointerId);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ mStrokeId++;
+ mLastPreviewSize = 0;
+ mPreviewEventTimes.setLength(0);
+ mPreviewXCoordinates.setLength(0);
+ mPreviewYCoordinates.setLength(0);
+ }
+
+ public int getGestureStrokeId() {
+ return mStrokeId;
+ }
+
+ public int getGestureStrokePreviewSize() {
+ return mPreviewEventTimes.getLength();
+ }
+
+ @Override
+ public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
+ super.addPoint(x, y, time, isHistorical);
+ mPreviewEventTimes.add(time);
+ mPreviewXCoordinates.add(x);
+ mPreviewYCoordinates.add(y);
+ }
+
+ public void appendPreviewStroke(final ResizableIntArray eventTimes,
+ final ResizableIntArray xCoords, final ResizableIntArray yCoords) {
+ final int length = mPreviewEventTimes.getLength() - mLastPreviewSize;
+ if (length <= 0) {
+ return;
+ }
+ eventTimes.append(mPreviewEventTimes, mLastPreviewSize, length);
+ xCoords.append(mPreviewXCoordinates, mLastPreviewSize, length);
+ yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length);
+ mLastPreviewSize = mPreviewEventTimes.getLength();
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index c4452a5f5..13214bb9f 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -21,6 +21,7 @@ import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED;
import android.text.TextUtils;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.StringUtils;
@@ -68,14 +69,35 @@ public class KeySpecParser {
public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, Locale locale,
final KeyboardCodesSet codesSet) {
- mCode = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet),
- needsToUpperCase, locale);
mLabel = toUpperCaseOfStringForLocale(getLabel(moreKeySpec),
needsToUpperCase, locale);
- mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec),
+ final int code = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet),
needsToUpperCase, locale);
+ if (code == Keyboard.CODE_UNSPECIFIED) {
+ // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
+ // upper case representation ("SS").
+ mCode = Keyboard.CODE_OUTPUT_TEXT;
+ mOutputText = mLabel;
+ } else {
+ mCode = code;
+ mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec),
+ needsToUpperCase, locale);
+ }
mIconId = getIconId(moreKeySpec);
}
+
+ @Override
+ public String toString() {
+ final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
+ : PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
+ final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText
+ : Keyboard.printableCode(mCode));
+ if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
+ return output;
+ } else {
+ return label + "|" + output;
+ }
+ }
}
private KeySpecParser() {
@@ -237,7 +259,7 @@ public class KeySpecParser {
throw new IllegalArgumentException();
}
- final ArrayList<T> list = new ArrayList<T>(end - start);
+ final ArrayList<T> list = CollectionUtils.newArrayList(end - start);
for (int i = start; i < end; i++) {
list.add(array[i]);
}
@@ -417,7 +439,7 @@ public class KeySpecParser {
// Skip empty entry.
if (pos - start > 0) {
if (list == null) {
- list = new ArrayList<String>();
+ list = CollectionUtils.newArrayList();
}
list.add(text.substring(start, pos));
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 80f4f259b..e40cf45cc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -18,8 +18,10 @@ package com.android.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
import android.util.Log;
+import android.util.SparseArray;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.XmlParseUtils;
@@ -32,7 +34,7 @@ public class KeyStyles {
private static final String TAG = KeyStyles.class.getSimpleName();
private static final boolean DEBUG = false;
- final HashMap<String, KeyStyle> mStyles = new HashMap<String, KeyStyle>();
+ final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap();
final KeyboardTextsSet mTextsSet;
private final KeyStyle mEmptyKeyStyle;
@@ -89,7 +91,7 @@ public class KeyStyles {
private class DeclaredKeyStyle extends KeyStyle {
private final String mParentStyleName;
- private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>();
+ private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray();
public DeclaredKeyStyle(String parentStyleName) {
mParentStyleName = parentStyleName;
@@ -100,8 +102,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseStringArray(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String[])mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String[])value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
@@ -112,8 +115,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return parseString(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index);
@@ -124,8 +128,9 @@ public class KeyStyles {
if (a.hasValue(index)) {
return a.getInt(index, defaultValue);
}
- if (mStyleAttributes.containsKey(index)) {
- return (Integer)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getInt(a, index, defaultValue);
@@ -133,12 +138,13 @@ public class KeyStyles {
@Override
public int getFlag(TypedArray a, int index) {
- int value = a.getInt(index, 0);
- if (mStyleAttributes.containsKey(index)) {
- value |= (Integer)mStyleAttributes.get(index);
+ int flags = a.getInt(index, 0);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ flags |= (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return value | parentStyle.getFlag(a, index);
+ return flags | parentStyle.getFlag(a, index);
}
void readKeyAttributes(TypedArray keyAttr) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 67cb74f4d..f7923d0b9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -17,13 +17,13 @@
package com.android.inputmethod.keyboard.internal;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import java.util.HashMap;
public class KeyboardCodesSet {
- private static final HashMap<String, int[]> sLanguageToCodesMap =
- new HashMap<String, int[]>();
- private static final HashMap<String, Integer> sNameToIdMap = new HashMap<String, Integer>();
+ private static final HashMap<String, int[]> sLanguageToCodesMap = CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sNameToIdMap = CollectionUtils.newHashMap();
private int[] mCodes = DEFAULT;
@@ -52,6 +52,7 @@ public class KeyboardCodesSet {
"key_action_next",
"key_action_previous",
"key_language_switch",
+ "key_research",
"key_unspecified",
"key_left_parenthesis",
"key_right_parenthesis",
@@ -86,6 +87,7 @@ public class KeyboardCodesSet {
Keyboard.CODE_ACTION_NEXT,
Keyboard.CODE_ACTION_PREVIOUS,
Keyboard.CODE_LANGUAGE_SWITCH,
+ Keyboard.CODE_RESEARCH,
Keyboard.CODE_UNSPECIFIED,
CODE_LEFT_PARENTHESIS,
CODE_RIGHT_PARENTHESIS,
@@ -112,6 +114,7 @@ public class KeyboardCodesSet {
DEFAULT[11],
DEFAULT[12],
DEFAULT[13],
+ DEFAULT[14],
CODE_RIGHT_PARENTHESIS,
CODE_LEFT_PARENTHESIS,
CODE_GREATER_THAN_SIGN,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 540e63b3f..4a98a3698 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -20,7 +20,9 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.SparseIntArray;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
@@ -31,11 +33,10 @@ public class KeyboardIconsSet {
public static final int ICON_UNDEFINED = 0;
private static final int ATTR_UNDEFINED = 0;
- private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID
- = new HashMap<Integer, Integer>();
+ private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
// Icon name to icon id map.
- private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>();
+ private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
private static final Object[] NAMES_AND_ATTR_IDS = {
"undefined", ATTR_UNDEFINED,
@@ -76,7 +77,9 @@ public class KeyboardIconsSet {
}
public void loadIcons(final TypedArray keyboardAttrs) {
- for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) {
+ final int size = ATTR_ID_TO_ICON_ID.size();
+ for (int index = 0; index < size; index++) {
+ final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
try {
final Drawable icon = keyboardAttrs.getDrawable(attrId);
setDefaultBounds(icon);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 43ffb85f7..4ab6832c3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -21,8 +21,6 @@ import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.ResearchLogger;
-import com.android.inputmethod.latin.define.ProductionFlag;
/**
* Keyboard state machine.
@@ -305,9 +303,6 @@ public class KeyboardState {
Log.d(TAG, "onPressKey: code=" + Keyboard.printableCode(code)
+ " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onPressKey(code, this);
- }
if (code == Keyboard.CODE_SHIFT) {
onPressShift();
} else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
@@ -341,9 +336,6 @@ public class KeyboardState {
Log.d(TAG, "onReleaseKey: code=" + Keyboard.printableCode(code)
+ " sliding=" + withSliding + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onReleaseKey(this, code, withSliding);
- }
if (code == Keyboard.CODE_SHIFT) {
onReleaseShift(withSliding);
} else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
@@ -375,9 +367,6 @@ public class KeyboardState {
if (DEBUG_EVENT) {
Log.d(TAG, "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onLongPressTimeout(code, this);
- }
if (mIsAlphabetMode && code == Keyboard.CODE_SHIFT) {
mLongPressShiftLockFired = true;
mSwitchActions.hapticAndAudioFeedback(code);
@@ -509,9 +498,6 @@ public class KeyboardState {
if (DEBUG_EVENT) {
Log.d(TAG, "onCancelInput: single=" + isSinglePointer + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onCancelInput(isSinglePointer, this);
- }
// Switch back to the previous keyboard mode if the user cancels sliding input.
if (isSinglePointer) {
if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
@@ -543,9 +529,6 @@ public class KeyboardState {
+ " single=" + isSinglePointer
+ " autoCaps=" + autoCaps + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onCodeInput(code, isSinglePointer, autoCaps, this);
- }
switch (mSwitchState) {
case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 8c218c6d3..a608cdef0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
@@ -45,14 +46,12 @@ import java.util.HashMap;
*/
public final class KeyboardTextsSet {
// Language to texts map.
- private static final HashMap<String, String[]> sLocaleToTextsMap =
- new HashMap<String, String[]>();
- private static final HashMap<String, Integer> sNameToIdsMap =
- new HashMap<String, Integer>();
+ private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
private String[] mTexts;
// Resource name to text map.
- private HashMap<String, String> mResourceNameToTextsMap = new HashMap<String, String>();
+ private HashMap<String, String> mResourceNameToTextsMap = CollectionUtils.newHashMap();
public void setLanguage(final String language) {
mTexts = sLocaleToTextsMap.get(language);
@@ -131,71 +130,71 @@ public final class KeyboardTextsSet {
/* 23 */ "more_keys_for_nordic_row2_10",
/* 24 */ "more_keys_for_nordic_row2_11",
/* 25 */ "keylabel_for_east_slavic_row1_9",
- /* 26 */ "keylabel_for_east_slavic_row2_1",
- /* 27 */ "keylabel_for_east_slavic_row3_5",
- /* 28 */ "more_keys_for_cyrillic_u",
- /* 29 */ "more_keys_for_cyrillic_ye",
- /* 30 */ "more_keys_for_cyrillic_en",
- /* 31 */ "more_keys_for_cyrillic_ha",
- /* 32 */ "more_keys_for_east_slavic_row2_1",
- /* 33 */ "more_keys_for_cyrillic_o",
- /* 34 */ "more_keys_for_cyrillic_soft_sign",
- /* 35 */ "keylabel_for_south_slavic_row1_6",
- /* 36 */ "keylabel_for_south_slavic_row2_11",
- /* 37 */ "keylabel_for_south_slavic_row3_1",
- /* 38 */ "keylabel_for_south_slavic_row3_8",
- /* 39 */ "more_keys_for_cyrillic_ie",
- /* 40 */ "more_keys_for_cyrillic_i",
- /* 41 */ "more_keys_for_single_quote",
- /* 42 */ "more_keys_for_double_quote",
- /* 43 */ "more_keys_for_tablet_double_quote",
- /* 44 */ "more_keys_for_currency_dollar",
- /* 45 */ "more_keys_for_currency_euro",
- /* 46 */ "more_keys_for_currency_pound",
- /* 47 */ "more_keys_for_currency_general",
- /* 48 */ "more_keys_for_punctuation",
- /* 49 */ "more_keys_for_star",
- /* 50 */ "more_keys_for_bullet",
- /* 51 */ "more_keys_for_plus",
- /* 52 */ "more_keys_for_left_parenthesis",
- /* 53 */ "more_keys_for_right_parenthesis",
- /* 54 */ "more_keys_for_less_than",
- /* 55 */ "more_keys_for_greater_than",
- /* 56 */ "more_keys_for_arabic_diacritics",
- /* 57 */ "keyhintlabel_for_arabic_diacritics",
- /* 58 */ "keylabel_for_symbols_1",
- /* 59 */ "keylabel_for_symbols_2",
- /* 60 */ "keylabel_for_symbols_3",
- /* 61 */ "keylabel_for_symbols_4",
- /* 62 */ "keylabel_for_symbols_5",
- /* 63 */ "keylabel_for_symbols_6",
- /* 64 */ "keylabel_for_symbols_7",
- /* 65 */ "keylabel_for_symbols_8",
- /* 66 */ "keylabel_for_symbols_9",
- /* 67 */ "keylabel_for_symbols_0",
- /* 68 */ "additional_more_keys_for_symbols_1",
- /* 69 */ "additional_more_keys_for_symbols_2",
- /* 70 */ "additional_more_keys_for_symbols_3",
- /* 71 */ "additional_more_keys_for_symbols_4",
- /* 72 */ "additional_more_keys_for_symbols_5",
- /* 73 */ "additional_more_keys_for_symbols_6",
- /* 74 */ "additional_more_keys_for_symbols_7",
- /* 75 */ "additional_more_keys_for_symbols_8",
- /* 76 */ "additional_more_keys_for_symbols_9",
- /* 77 */ "additional_more_keys_for_symbols_0",
- /* 78 */ "more_keys_for_symbols_1",
- /* 79 */ "more_keys_for_symbols_2",
- /* 80 */ "more_keys_for_symbols_3",
- /* 81 */ "more_keys_for_symbols_4",
- /* 82 */ "more_keys_for_symbols_5",
- /* 83 */ "more_keys_for_symbols_6",
- /* 84 */ "more_keys_for_symbols_7",
- /* 85 */ "more_keys_for_symbols_8",
- /* 86 */ "more_keys_for_symbols_9",
- /* 87 */ "more_keys_for_symbols_0",
- /* 88 */ "keylabel_for_comma",
- /* 89 */ "more_keys_for_comma",
- /* 90 */ "keylabel_for_symbols_exclamation",
+ /* 26 */ "keylabel_for_east_slavic_row1_12",
+ /* 27 */ "keylabel_for_east_slavic_row2_1",
+ /* 28 */ "keylabel_for_east_slavic_row2_11",
+ /* 29 */ "keylabel_for_east_slavic_row3_5",
+ /* 30 */ "more_keys_for_cyrillic_u",
+ /* 31 */ "more_keys_for_cyrillic_en",
+ /* 32 */ "more_keys_for_cyrillic_ghe",
+ /* 33 */ "more_keys_for_east_slavic_row2_1",
+ /* 34 */ "more_keys_for_cyrillic_o",
+ /* 35 */ "more_keys_for_cyrillic_soft_sign",
+ /* 36 */ "keylabel_for_south_slavic_row1_6",
+ /* 37 */ "keylabel_for_south_slavic_row2_11",
+ /* 38 */ "keylabel_for_south_slavic_row3_1",
+ /* 39 */ "keylabel_for_south_slavic_row3_8",
+ /* 40 */ "more_keys_for_cyrillic_ie",
+ /* 41 */ "more_keys_for_cyrillic_i",
+ /* 42 */ "more_keys_for_single_quote",
+ /* 43 */ "more_keys_for_double_quote",
+ /* 44 */ "more_keys_for_tablet_double_quote",
+ /* 45 */ "more_keys_for_currency_dollar",
+ /* 46 */ "more_keys_for_currency_euro",
+ /* 47 */ "more_keys_for_currency_pound",
+ /* 48 */ "more_keys_for_currency_general",
+ /* 49 */ "more_keys_for_punctuation",
+ /* 50 */ "more_keys_for_star",
+ /* 51 */ "more_keys_for_bullet",
+ /* 52 */ "more_keys_for_plus",
+ /* 53 */ "more_keys_for_left_parenthesis",
+ /* 54 */ "more_keys_for_right_parenthesis",
+ /* 55 */ "more_keys_for_less_than",
+ /* 56 */ "more_keys_for_greater_than",
+ /* 57 */ "more_keys_for_arabic_diacritics",
+ /* 58 */ "keyhintlabel_for_arabic_diacritics",
+ /* 59 */ "keylabel_for_symbols_1",
+ /* 60 */ "keylabel_for_symbols_2",
+ /* 61 */ "keylabel_for_symbols_3",
+ /* 62 */ "keylabel_for_symbols_4",
+ /* 63 */ "keylabel_for_symbols_5",
+ /* 64 */ "keylabel_for_symbols_6",
+ /* 65 */ "keylabel_for_symbols_7",
+ /* 66 */ "keylabel_for_symbols_8",
+ /* 67 */ "keylabel_for_symbols_9",
+ /* 68 */ "keylabel_for_symbols_0",
+ /* 69 */ "additional_more_keys_for_symbols_1",
+ /* 70 */ "additional_more_keys_for_symbols_2",
+ /* 71 */ "additional_more_keys_for_symbols_3",
+ /* 72 */ "additional_more_keys_for_symbols_4",
+ /* 73 */ "additional_more_keys_for_symbols_5",
+ /* 74 */ "additional_more_keys_for_symbols_6",
+ /* 75 */ "additional_more_keys_for_symbols_7",
+ /* 76 */ "additional_more_keys_for_symbols_8",
+ /* 77 */ "additional_more_keys_for_symbols_9",
+ /* 78 */ "additional_more_keys_for_symbols_0",
+ /* 79 */ "more_keys_for_symbols_1",
+ /* 80 */ "more_keys_for_symbols_2",
+ /* 81 */ "more_keys_for_symbols_3",
+ /* 82 */ "more_keys_for_symbols_4",
+ /* 83 */ "more_keys_for_symbols_5",
+ /* 84 */ "more_keys_for_symbols_6",
+ /* 85 */ "more_keys_for_symbols_7",
+ /* 86 */ "more_keys_for_symbols_8",
+ /* 87 */ "more_keys_for_symbols_9",
+ /* 88 */ "more_keys_for_symbols_0",
+ /* 89 */ "keylabel_for_comma",
+ /* 90 */ "more_keys_for_comma",
/* 91 */ "keylabel_for_symbols_question",
/* 92 */ "keylabel_for_symbols_semicolon",
/* 93 */ "keylabel_for_symbols_percent",
@@ -237,41 +236,41 @@ public final class KeyboardTextsSet {
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- EMPTY, EMPTY,
- /* ~40 */
- /* 41 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B",
+ EMPTY, EMPTY, EMPTY,
+ /* ~41 */
+ /* 42 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
// U+00A2: "¢" CENT SIGN
// U+00A3: "£" POUND SIGN
// U+20AC: "€" EURO SIGN
// U+00A5: "¥" YEN SIGN
// U+20B1: "₱" PESO SIGN
- /* 44 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- /* 45 */ "\u00A2,\u00A3,$,\u00A5,\u20B1",
- /* 46 */ "\u00A2,$,\u20AC,\u00A5,\u20B1",
- /* 47 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1",
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
+ /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
+ /* 46 */ "\u00A2,\u00A3,$,\u00A5,\u20B1",
+ /* 47 */ "\u00A2,$,\u20AC,\u00A5,\u20B1",
+ /* 48 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
// U+2020: "†" DAGGER
// U+2021: "‡" DOUBLE DAGGER
// U+2605: "★" BLACK STAR
- /* 49 */ "\u2020,\u2021,\u2605",
+ /* 50 */ "\u2020,\u2021,\u2605",
// U+266A: "♪" EIGHTH NOTE
// U+2665: "♥" BLACK HEART SUIT
// U+2660: "♠" BLACK SPADE SUIT
// U+2666: "♦" BLACK DIAMOND SUIT
// U+2663: "♣" BLACK CLUB SUIT
- /* 50 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
+ /* 51 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
// U+00B1: "±" PLUS-MINUS SIGN
- /* 51 */ "\u00B1",
+ /* 52 */ "\u00B1",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 52 */ "!fixedColumnOrder!3,<,{,[",
- /* 53 */ "!fixedColumnOrder!3,>,},]",
+ /* 53 */ "!fixedColumnOrder!3,<,{,[",
+ /* 54 */ "!fixedColumnOrder!3,>,},]",
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
// U+2264: "≤" LESS-THAN OR EQUAL TO
@@ -287,51 +286,50 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
- /* 55 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
- /* 56 */ EMPTY,
+ /* 55 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
/* 57 */ EMPTY,
- /* 58 */ "1",
- /* 59 */ "2",
- /* 60 */ "3",
- /* 61 */ "4",
- /* 62 */ "5",
- /* 63 */ "6",
- /* 64 */ "7",
- /* 65 */ "8",
- /* 66 */ "9",
- /* 67 */ "0",
- /* 68~ */
+ /* 58 */ EMPTY,
+ /* 59 */ "1",
+ /* 60 */ "2",
+ /* 61 */ "3",
+ /* 62 */ "4",
+ /* 63 */ "5",
+ /* 64 */ "6",
+ /* 65 */ "7",
+ /* 66 */ "8",
+ /* 67 */ "9",
+ /* 68 */ "0",
+ /* 69~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~77 */
+ /* ~78 */
// U+00B9: "¹" SUPERSCRIPT ONE
// U+00BD: "½" VULGAR FRACTION ONE HALF
// U+2153: "⅓" VULGAR FRACTION ONE THIRD
// U+00BC: "¼" VULGAR FRACTION ONE QUARTER
// U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
- /* 78 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
+ /* 79 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
// U+00B2: "²" SUPERSCRIPT TWO
// U+2154: "⅔" VULGAR FRACTION TWO THIRDS
- /* 79 */ "\u00B2,\u2154",
+ /* 80 */ "\u00B2,\u2154",
// U+00B3: "³" SUPERSCRIPT THREE
// U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
// U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
- /* 80 */ "\u00B3,\u00BE,\u215C",
+ /* 81 */ "\u00B3,\u00BE,\u215C",
// U+2074: "⁴" SUPERSCRIPT FOUR
- /* 81 */ "\u2074",
+ /* 82 */ "\u2074",
// U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
- /* 82 */ "\u215D",
- /* 83 */ EMPTY,
+ /* 83 */ "\u215D",
+ /* 84 */ EMPTY,
// U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
- /* 84 */ "\u215E",
- /* 85 */ EMPTY,
+ /* 85 */ "\u215E",
/* 86 */ EMPTY,
+ /* 87 */ EMPTY,
// U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
// U+2205: "∅" EMPTY SET
- /* 87 */ "\u207F,\u2205",
- /* 88 */ ",",
- /* 89 */ EMPTY,
- /* 90 */ "!",
+ /* 88 */ "\u207F,\u2205",
+ /* 89 */ ",",
+ /* 90 */ EMPTY,
/* 91 */ "?",
/* 92 */ ";",
/* 93 */ "%",
@@ -379,38 +377,91 @@ public final class KeyboardTextsSet {
/* 121 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
};
+ /* Language af: Afrikaans */
+ private static final String[] LANGUAGE_af = {
+ // This is the same as Dutch except more keys of y and demoting vowels with diaeresis.
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E1,\u00E2,\u00E4,\u00E0,\u00E6,\u00E3,\u00E5,\u0101",
+ // 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+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ /* 2 */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133",
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ /* 3 */ "\u00F3,\u00F4,\u00F6,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B",
+ /* 5 */ null,
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ /* 6 */ "\u00F1,\u0144",
+ /* 7 */ null,
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ /* 8 */ "\u00FD,\u0133",
+ };
+
/* Language ar: Arabic */
private static final String[] LANGUAGE_ar = {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null,
- /* ~47 */
+ /* ~48 */
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 49 */ "\u2605,\u066D",
+ /* 50 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 50 */ "\u266A",
- /* 51 */ null,
+ /* 51 */ "\u266A",
+ /* 52 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -426,8 +477,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0654: "ٔ" ARABIC HAMZA ABOVE
// U+0652: "ْ" ARABIC SUKUN
@@ -443,47 +494,46 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 56 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
- /* 57 */ "\u0651",
+ /* 57 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 58 */ "\u0651",
// U+0661: "١" ARABIC-INDIC DIGIT ONE
- /* 58 */ "\u0661",
+ /* 59 */ "\u0661",
// U+0662: "٢" ARABIC-INDIC DIGIT TWO
- /* 59 */ "\u0662",
+ /* 60 */ "\u0662",
// U+0663: "٣" ARABIC-INDIC DIGIT THREE
- /* 60 */ "\u0663",
+ /* 61 */ "\u0663",
// U+0664: "٤" ARABIC-INDIC DIGIT FOUR
- /* 61 */ "\u0664",
+ /* 62 */ "\u0664",
// U+0665: "٥" ARABIC-INDIC DIGIT FIVE
- /* 62 */ "\u0665",
+ /* 63 */ "\u0665",
// U+0666: "٦" ARABIC-INDIC DIGIT SIX
- /* 63 */ "\u0666",
+ /* 64 */ "\u0666",
// U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
- /* 64 */ "\u0667",
+ /* 65 */ "\u0667",
// U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
- /* 65 */ "\u0668",
+ /* 66 */ "\u0668",
// U+0669: "٩" ARABIC-INDIC DIGIT NINE
- /* 66 */ "\u0669",
+ /* 67 */ "\u0669",
// U+0660: "٠" ARABIC-INDIC DIGIT ZERO
- /* 67 */ "\u0660",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
+ /* 68 */ "\u0660",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 77 */ "0,\u066B,\u066C",
- /* 78~ */
+ /* 78 */ "0,\u066B,\u066C",
+ /* 79~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~87 */
+ /* ~88 */
// U+060C: "،" ARABIC COMMA
- /* 88 */ "\u060C",
- /* 89 */ "\\,",
- /* 90 */ null,
+ /* 89 */ "\u060C",
+ /* 90 */ "\\,",
/* 91 */ "\u061F",
/* 92 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
@@ -512,19 +562,24 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+045E: "ў" CYRILLIC SMALL LETTER SHORT U
/* 25 */ "\u045E",
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 26 */ "\u0451",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* 27 */ "\u0456",
- /* 28~ */
- null, null, null,
- /* ~30 */
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
- /* 32 */ null,
- /* 33 */ null,
+ /* 29 */ "\u0456",
+ /* 30~ */
+ null, null, null, null, null,
+ /* ~34 */
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language ca: Catalan */
@@ -857,31 +912,22 @@ public final class KeyboardTextsSet {
/* 8~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~47 */
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~48 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 48 */ "!fixedColumnOrder!9,\",\',#,-,\u00A1,!,\u00BF,\\,,?,@,&,\\%,+,;,:,/,(,)",
- /* 49~ */
+ /* 49 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
+ /* 50~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~89 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null,
+ /* ~99 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 90 */ "\u00A1",
+ /* 100 */ "!,\u00A1",
+ /* 101 */ null,
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 91 */ "\u00BF",
- /* 92 */ null,
- /* 93 */ null,
- /* 94 */ "!",
- /* 95 */ "?",
- /* 96~ */
- null, null, null,
- /* ~98 */
- /* 99 */ "\u00A1",
- /* 100 */ "\u00A1,!",
- /* 101 */ "\u00BF",
- /* 102 */ "\u00BF,?",
+ /* 102 */ "?,\u00BF",
};
/* Language et: Estonian */
@@ -989,33 +1035,33 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null,
- /* ~47 */
+ /* ~48 */
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 49 */ "\u2605,\u066D",
+ /* 50 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 50 */ "\u266A",
- /* 51 */ null,
+ /* 51 */ "\u266A",
+ /* 52 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1031,8 +1077,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0652: "ْ" ARABIC SUKUN
// U+0651: "ّ" ARABIC SHADDA
@@ -1048,47 +1094,46 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 56 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
- /* 57 */ "\u064B",
+ /* 57 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 58 */ "\u064B",
// U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
- /* 58 */ "\u06F1",
+ /* 59 */ "\u06F1",
// U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
- /* 59 */ "\u06F2",
+ /* 60 */ "\u06F2",
// U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
- /* 60 */ "\u06F3",
+ /* 61 */ "\u06F3",
// U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
- /* 61 */ "\u06F4",
+ /* 62 */ "\u06F4",
// U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
- /* 62 */ "\u06F5",
+ /* 63 */ "\u06F5",
// U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
- /* 63 */ "\u06F6",
+ /* 64 */ "\u06F6",
// U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
- /* 64 */ "\u06F7",
+ /* 65 */ "\u06F7",
// U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
- /* 65 */ "\u06F8",
+ /* 66 */ "\u06F8",
// U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
- /* 66 */ "\u06F9",
+ /* 67 */ "\u06F9",
// U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
- /* 67 */ "\u06F0",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
+ /* 68 */ "\u06F0",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 77 */ "0,\u066B,\u066C",
- /* 78~ */
+ /* 78 */ "0,\u066B,\u066C",
+ /* 79~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~87 */
+ /* ~88 */
// U+060C: "،" ARABIC COMMA
- /* 88 */ "\u060C",
- /* 89 */ "\\,",
- /* 90 */ null,
+ /* 89 */ "\u060C",
+ /* 90 */ "\\,",
/* 91 */ "\u061F",
/* 92 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
@@ -1219,38 +1264,38 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~57 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~58 */
// U+0967: "१" DEVANAGARI DIGIT ONE
- /* 58 */ "\u0967",
+ /* 59 */ "\u0967",
// U+0968: "२" DEVANAGARI DIGIT TWO
- /* 59 */ "\u0968",
+ /* 60 */ "\u0968",
// U+0969: "३" DEVANAGARI DIGIT THREE
- /* 60 */ "\u0969",
+ /* 61 */ "\u0969",
// U+096A: "४" DEVANAGARI DIGIT FOUR
- /* 61 */ "\u096A",
+ /* 62 */ "\u096A",
// U+096B: "५" DEVANAGARI DIGIT FIVE
- /* 62 */ "\u096B",
+ /* 63 */ "\u096B",
// U+096C: "६" DEVANAGARI DIGIT SIX
- /* 63 */ "\u096C",
+ /* 64 */ "\u096C",
// U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* 64 */ "\u096D",
+ /* 65 */ "\u096D",
// U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* 65 */ "\u096E",
+ /* 66 */ "\u096E",
// U+096F: "९" DEVANAGARI DIGIT NINE
- /* 66 */ "\u096F",
+ /* 67 */ "\u096F",
// U+0966: "०" DEVANAGARI DIGIT ZERO
- /* 67 */ "\u0966",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
- /* 77 */ "0",
+ /* 68 */ "\u0966",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
+ /* 78 */ "0",
};
/* Language hr: Croatian */
@@ -1438,27 +1483,27 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null, null,
- /* ~48 */
+ /* ~49 */
// U+2605: "★" BLACK STAR
- /* 49 */ "\u2605",
- /* 50 */ null,
+ /* 50 */ "\u2605",
+ /* 51 */ null,
// U+00B1: "±" PLUS-MINUS SIGN
// U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
- /* 51 */ "\u00B1,\uFB29",
+ /* 52 */ "\u00B1,\uFB29",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 52 */ "!fixedColumnOrder!3,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!3,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1474,8 +1519,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
};
/* Language ky: Kirghiz */
@@ -1486,22 +1531,29 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ /* 26 */ "\u044A",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
+ /* 29 */ "\u0438",
// U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
- /* 28 */ "\u04AF",
- /* 29 */ null,
+ /* 30 */ "\u04AF",
// U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
- /* 30 */ "\u04A3",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
+ /* 31 */ "\u04A3",
/* 32 */ null,
+ /* 33 */ null,
// U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
- /* 33 */ "\u04E9",
+ /* 34 */ "\u04E9",
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language lt: Lithuanian */
@@ -1688,21 +1740,21 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~34 */
+ null, null, null, null, null, null,
+ /* ~35 */
// U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* 35 */ "\u0455",
+ /* 36 */ "\u0455",
// U+045C: "ќ" CYRILLIC SMALL LETTER KJE
- /* 36 */ "\u045C",
+ /* 37 */ "\u045C",
// U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* 37 */ "\u0437",
+ /* 38 */ "\u0437",
// U+0453: "ѓ" CYRILLIC SMALL LETTER GJE
- /* 38 */ "\u0453",
+ /* 39 */ "\u0453",
// U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* 39 */ "\u0450",
+ /* 40 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* 40 */ "\u045D",
- /* 41 */ null,
+ /* 41 */ "\u045D",
+ /* 42 */ null,
// U+2018: "‘" LEFT SINGLE QUOTATION MARK
// U+2019: "’" RIGHT SINGLE QUOTATION MARK
// U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
@@ -1713,10 +1765,10 @@ public final class KeyboardTextsSet {
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201E;,&#x201C;,&#x201D;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
};
/* Language nb: Norwegian Bokmål */
@@ -1978,20 +2030,24 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ /* 26 */ "\u044A",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
- /* 28 */ null,
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* 29 */ "\u0451",
- /* 30 */ null,
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
- /* 32 */ null,
- /* 33 */ null,
+ /* 29 */ "\u0438",
+ /* 30~ */
+ null, null, null, null, null,
+ /* ~34 */
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language sk: Slovak */
@@ -2109,21 +2165,40 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~34 */
+ null, null, null, null, null, null,
+ /* ~35 */
+ // TODO: Move these to sr-Latn once we can handle IETF language tag with script name specified.
+ // BEGIN: More keys definitions for Serbian (Latin)
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // <string name="more_keys_for_s">&#x0161;,&#x00DF;,&#x015B;</string>
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // <string name="more_keys_for_c">&#x010D;,&#x00E7;,&#x0107;</string>
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ // <string name="more_keys_for_d">&#x010F;</string>
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // <string name="more_keys_for_z">&#x017E;,&#x017A;,&#x017C;</string>
+ // END: More keys definitions for Serbian (Latin)
+ // BEGIN: More keys definitions for Serbian (Cyrillic)
// U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* 35 */ "\u0437",
+ /* 36 */ "\u0437",
// U+045B: "ћ" CYRILLIC SMALL LETTER TSHE
- /* 36 */ "\u045B",
+ /* 37 */ "\u045B",
// U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* 37 */ "\u0455",
+ /* 38 */ "\u0455",
// U+0452: "ђ" CYRILLIC SMALL LETTER DJE
- /* 38 */ "\u0452",
+ /* 39 */ "\u0452",
// U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* 39 */ "\u0450",
+ /* 40 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* 40 */ "\u045D",
- /* 41 */ null,
+ /* 41 */ "\u045D",
+ /* 42 */ null,
+ // END: More keys definitions for Serbian (Cyrillic)
// U+2018: "‘" LEFT SINGLE QUOTATION MARK
// U+2019: "’" RIGHT SINGLE QUOTATION MARK
// U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
@@ -2134,10 +2209,10 @@ public final class KeyboardTextsSet {
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201E;,&#x201C;,&#x201D;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
};
/* Language sv: Swedish */
@@ -2182,6 +2257,111 @@ public final class KeyboardTextsSet {
/* 24 */ "\u00E6",
};
+ /* Language sw: Swahili */
+ private static final String[] LANGUAGE_sw = {
+ // This is the same as English except more_keys_for_g.
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113",
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC",
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5",
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B",
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 5 */ "\u00DF",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 6 */ "\u00F1",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ /* 7 */ "\u00E7",
+ /* 8~ */
+ null, null, null, null, null, null, null,
+ /* ~14 */
+ /* 15 */ "g\'",
+ };
+
+ /* Language tl: Tagalog */
+ private static final String[] LANGUAGE_tl = {
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ /* 0 */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ /* 2 */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ /* 3 */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
+ /* 5 */ null,
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ /* 6 */ "\u00F1,\u0144",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ /* 7 */ "\u00E7,\u0107,\u010D",
+ };
+
/* Language tr: Turkish */
private static final String[] LANGUAGE_tr = {
// U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
@@ -2235,20 +2415,23 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+0457: "ї" CYRILLIC SMALL LETTER YI
+ /* 26 */ "\u0457",
// U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* 26 */ "\u0456",
+ /* 27 */ "\u0456",
+ // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE
+ /* 28 */ "\u0454",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
- /* 28~ */
- null, null, null,
- /* ~30 */
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
+ /* 29 */ "\u0438",
+ /* 30 */ null,
+ /* 31 */ null,
+ // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN
+ /* 32 */ "\u0491",
// U+0457: "ї" CYRILLIC SMALL LETTER YI
- /* 32 */ "\u0457",
- /* 33 */ null,
+ /* 33 */ "\u0457",
+ /* 34 */ null,
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
};
/* Language vi: Vietnamese */
@@ -2332,6 +2515,53 @@ public final class KeyboardTextsSet {
/* 9 */ "\u0111",
};
+ /* Language zu: Zulu */
+ private static final String[] LANGUAGE_zu = {
+ // This is the same as English
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113",
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC",
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5",
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B",
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 5 */ "\u00DF",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 6 */ "\u00F1",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ /* 7 */ "\u00E7",
+ };
+
/* Language zz: No language */
private static final String[] LANGUAGE_zz = {
// U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -2457,6 +2687,7 @@ public final class KeyboardTextsSet {
private static final Object[] LANGUAGES_AND_TEXTS = {
"DEFAULT", LANGUAGE_DEFAULT, /* default */
+ "af", LANGUAGE_af, /* Afrikaans */
"ar", LANGUAGE_ar, /* Arabic */
"be", LANGUAGE_be, /* Belarusian */
"ca", LANGUAGE_ca, /* Catalan */
@@ -2490,9 +2721,12 @@ public final class KeyboardTextsSet {
"sl", LANGUAGE_sl, /* Slovenian */
"sr", LANGUAGE_sr, /* Serbian */
"sv", LANGUAGE_sv, /* Swedish */
+ "sw", LANGUAGE_sw, /* Swahili */
+ "tl", LANGUAGE_tl, /* Tagalog */
"tr", LANGUAGE_tr, /* Turkish */
"uk", LANGUAGE_uk, /* Ukrainian */
"vi", LANGUAGE_vi, /* Vietnamese */
+ "zu", LANGUAGE_zu, /* Zulu */
"zz", LANGUAGE_zz, /* No language */
};
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 5db65c660..e0858c019 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -18,72 +18,160 @@ package com.android.inputmethod.keyboard.internal;
import android.util.Log;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.CollectionUtils;
-import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.ArrayList;
public class PointerTrackerQueue {
private static final String TAG = PointerTrackerQueue.class.getSimpleName();
private static final boolean DEBUG = false;
- private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
+ public interface Element {
+ public boolean isModifier();
+ public boolean isInSlidingKeyInput();
+ public void onPhantomUpEvent(long eventTime);
+ }
+
+ private static final int INITIAL_CAPACITY = 10;
+ private final ArrayList<Element> mExpandableArrayOfActivePointers =
+ CollectionUtils.newArrayList(INITIAL_CAPACITY);
+ private int mArraySize = 0;
- public synchronized void add(PointerTracker tracker) {
- mQueue.add(tracker);
+ public synchronized int size() {
+ return mArraySize;
+ }
+
+ public synchronized void add(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ if (arraySize < expandableArray.size()) {
+ expandableArray.set(arraySize, pointer);
+ } else {
+ expandableArray.add(pointer);
+ }
+ mArraySize = arraySize + 1;
}
- public synchronized void remove(PointerTracker tracker) {
- mQueue.remove(tracker);
+ public synchronized void remove(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize = 0;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (newSize != index) {
+ Log.w(TAG, "Found duplicated element in remove: " + pointer);
+ }
+ continue; // Remove this element from the expandableArray.
+ }
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
+ }
+ newSize++;
+ }
+ mArraySize = newSize;
}
- public synchronized void releaseAllPointersOlderThan(PointerTracker tracker,
- long eventTime) {
+ public synchronized void releaseAllPointersOlderThan(final Element pointer,
+ final long eventTime) {
if (DEBUG) {
- Log.d(TAG, "releaseAllPoniterOlderThan: [" + tracker.mPointerId + "] " + this);
+ Log.d(TAG, "releaseAllPoniterOlderThan: " + pointer + " " + this);
}
- if (!mQueue.contains(tracker)) {
- return;
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize, index;
+ for (newSize = index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ break; // Stop releasing elements.
+ }
+ if (!element.isModifier()) {
+ element.onPhantomUpEvent(eventTime);
+ continue; // Remove this element from the expandableArray.
+ }
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
+ }
+ newSize++;
}
- final Iterator<PointerTracker> it = mQueue.iterator();
- while (it.hasNext()) {
- final PointerTracker t = it.next();
- if (t == tracker) {
- break;
+ // Shift rest of the expandableArray.
+ int count = 0;
+ for (; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (count > 0) {
+ Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: "
+ + pointer);
+ }
+ count++;
}
- if (!t.isModifier()) {
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
- it.remove();
+ if (newSize != index) {
+ expandableArray.set(newSize, expandableArray.get(index));
+ newSize++;
}
}
+ mArraySize = newSize;
}
- public void releaseAllPointers(long eventTime) {
+ public void releaseAllPointers(final long eventTime) {
releaseAllPointersExcept(null, eventTime);
}
- public synchronized void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
+ public synchronized void releaseAllPointersExcept(final Element pointer,
+ final long eventTime) {
if (DEBUG) {
- if (tracker == null) {
+ if (pointer == null) {
Log.d(TAG, "releaseAllPoniters: " + this);
} else {
- Log.d(TAG, "releaseAllPoniterExcept: [" + tracker.mPointerId + "] " + this);
+ Log.d(TAG, "releaseAllPoniterExcept: " + pointer + " " + this);
+ }
+ }
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize = 0, count = 0;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (count > 0) {
+ Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: " + pointer);
+ }
+ count++;
+ } else {
+ element.onPhantomUpEvent(eventTime);
+ continue; // Remove this element from the expandableArray.
+ }
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
}
+ newSize++;
}
- final Iterator<PointerTracker> it = mQueue.iterator();
- while (it.hasNext()) {
- final PointerTracker t = it.next();
- if (t != tracker) {
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
- it.remove();
+ mArraySize = newSize;
+ }
+
+ public synchronized boolean hasModifierKeyOlderThan(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ return false; // Stop searching modifier key.
+ }
+ if (element.isModifier()) {
+ return true;
}
}
+ return false;
}
public synchronized boolean isAnyInSlidingKeyInput() {
- for (final PointerTracker tracker : mQueue) {
- if (tracker.isInSlidingKeyInput()) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element.isInSlidingKeyInput()) {
return true;
}
}
@@ -91,14 +179,16 @@ public class PointerTrackerQueue {
}
@Override
- public String toString() {
+ public synchronized String toString() {
final StringBuilder sb = new StringBuilder();
- for (final PointerTracker tracker : mQueue) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
if (sb.length() > 0)
sb.append(" ");
- sb.append("[" + tracker.mPointerId + " "
- + Keyboard.printableCode(tracker.getKey().mCode) + "]");
+ sb.append(element.toString());
}
- return sb.toString();
+ return "[" + sb.toString() + "]";
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
new file mode 100644
index 000000000..269b202b5
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.widget.RelativeLayout;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.GesturePreviewTrailParams;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+public class PreviewPlacerView extends RelativeLayout {
+ private final Paint mGesturePaint;
+ private final Paint mTextPaint;
+ private final int mGestureFloatingPreviewTextColor;
+ private final int mGestureFloatingPreviewTextOffset;
+ private final int mGestureFloatingPreviewTextShadowColor;
+ private final int mGestureFloatingPreviewTextShadowBorder;
+ private final int mGestureFloatingPreviewTextShadingColor;
+ private final int mGestureFloatingPreviewTextShadingBorder;
+ private final int mGestureFloatingPreviewTextConnectorColor;
+ private final int mGestureFloatingPreviewTextConnectorWidth;
+ /* package */ final int mGestureFloatingPreviewTextLingerTimeout;
+
+ private int mXOrigin;
+ private int mYOrigin;
+
+ private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
+ CollectionUtils.newSparseArray();
+ private final GesturePreviewTrailParams mGesturePreviewTrailParams;
+
+ private String mGestureFloatingPreviewText;
+ private int mLastPointerX;
+ private int mLastPointerY;
+
+ private boolean mDrawsGesturePreviewTrail;
+ private boolean mDrawsGestureFloatingPreviewText;
+
+ private final DrawingHandler mDrawingHandler;
+
+ private static class DrawingHandler extends StaticInnerHandlerWrapper<PreviewPlacerView> {
+ private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 0;
+ private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 1;
+
+ private final GesturePreviewTrailParams mGesturePreviewTrailParams;
+
+ public DrawingHandler(final PreviewPlacerView outerInstance,
+ final GesturePreviewTrailParams gesturePreviewTrailParams) {
+ super(outerInstance);
+ mGesturePreviewTrailParams = gesturePreviewTrailParams;
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ final PreviewPlacerView placerView = getOuterInstance();
+ if (placerView == null) return;
+ switch (msg.what) {
+ case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
+ placerView.setGestureFloatingPreviewText(null);
+ break;
+ case MSG_UPDATE_GESTURE_PREVIEW_TRAIL:
+ placerView.invalidate();
+ break;
+ }
+ }
+
+ private void cancelDismissGestureFloatingPreviewText() {
+ removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ cancelDismissGestureFloatingPreviewText();
+ final PreviewPlacerView placerView = getOuterInstance();
+ sendMessageDelayed(
+ obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
+ placerView.mGestureFloatingPreviewTextLingerTimeout);
+ }
+
+ private void cancelUpdateGestureTrailPreview() {
+ removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
+ }
+
+ public void postUpdateGestureTrailPreview() {
+ cancelUpdateGestureTrailPreview();
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
+ mGesturePreviewTrailParams.mUpdateInterval);
+ }
+
+ public void cancelAllMessages() {
+ cancelDismissGestureFloatingPreviewText();
+ cancelUpdateGestureTrailPreview();
+ }
+ }
+
+ public PreviewPlacerView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.attr.keyboardViewStyle);
+ }
+
+ public PreviewPlacerView(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context);
+ setWillNotDraw(false);
+
+ final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
+ attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+ final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0);
+ mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0);
+ mGestureFloatingPreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0);
+ mGestureFloatingPreviewTextShadowColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextShadowColor, 0);
+ mGestureFloatingPreviewTextShadowBorder = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextShadowBorder, 0);
+ mGestureFloatingPreviewTextShadingColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextShadingColor, 0);
+ mGestureFloatingPreviewTextShadingBorder = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextShadingBorder, 0);
+ mGestureFloatingPreviewTextConnectorColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorColor, 0);
+ mGestureFloatingPreviewTextConnectorWidth = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextConnectorWidth, 0);
+ mGestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
+ final int gesturePreviewTrailColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gesturePreviewTrailColor, 0);
+ final int gesturePreviewTrailWidth = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gesturePreviewTrailWidth, 0);
+ mGesturePreviewTrailParams = new GesturePreviewTrailParams(keyboardViewAttr);
+ keyboardViewAttr.recycle();
+
+ mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams);
+
+ mGesturePaint = new Paint();
+ mGesturePaint.setAntiAlias(true);
+ mGesturePaint.setStyle(Paint.Style.STROKE);
+ mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
+ mGesturePaint.setColor(gesturePreviewTrailColor);
+ mGesturePaint.setStrokeWidth(gesturePreviewTrailWidth);
+
+ mTextPaint = new Paint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setStrokeJoin(Paint.Join.ROUND);
+ mTextPaint.setTextAlign(Align.CENTER);
+ mTextPaint.setTextSize(gestureFloatingPreviewTextSize);
+ }
+
+ public void setOrigin(final int x, final int y) {
+ mXOrigin = x;
+ mYOrigin = y;
+ }
+
+ public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
+ final boolean drawsGestureFloatingPreviewText) {
+ mDrawsGesturePreviewTrail = drawsGesturePreviewTrail;
+ mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText;
+ }
+
+ public void invalidatePointer(final PointerTracker tracker) {
+ GesturePreviewTrail trail;
+ synchronized (mGesturePreviewTrails) {
+ trail = mGesturePreviewTrails.get(tracker.mPointerId);
+ if (trail == null) {
+ trail = new GesturePreviewTrail(mGesturePreviewTrailParams);
+ mGesturePreviewTrails.put(tracker.mPointerId, trail);
+ }
+ }
+ trail.addStroke(tracker.getGestureStrokeWithPreviewTrail(), tracker.getDownTime());
+
+ mLastPointerX = tracker.getLastX();
+ mLastPointerY = tracker.getLastY();
+ // TODO: Should narrow the invalidate region.
+ invalidate();
+ }
+
+ @Override
+ public void onDraw(final Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.translate(mXOrigin, mYOrigin);
+ if (mDrawsGesturePreviewTrail) {
+ boolean needsUpdatingGesturePreviewTrail = false;
+ synchronized (mGesturePreviewTrails) {
+ // Trails count == fingers count that have ever been active.
+ final int trailsCount = mGesturePreviewTrails.size();
+ for (int index = 0; index < trailsCount; index++) {
+ final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
+ needsUpdatingGesturePreviewTrail |=
+ trail.drawGestureTrail(canvas, mGesturePaint);
+ }
+ }
+ if (needsUpdatingGesturePreviewTrail) {
+ mDrawingHandler.postUpdateGestureTrailPreview();
+ }
+ }
+ if (mDrawsGestureFloatingPreviewText) {
+ drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
+ }
+ canvas.translate(-mXOrigin, -mYOrigin);
+ }
+
+ public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
+ mGestureFloatingPreviewText = gestureFloatingPreviewText;
+ invalidate();
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ mDrawingHandler.dismissGestureFloatingPreviewText();
+ }
+
+ public void cancelAllMessages() {
+ mDrawingHandler.cancelAllMessages();
+ }
+
+ private void drawGestureFloatingPreviewText(final Canvas canvas,
+ final String gestureFloatingPreviewText) {
+ if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
+ return;
+ }
+
+ final Paint paint = mTextPaint;
+ // TODO: Figure out how we should deal with the floating preview text with multiple moving
+ // fingers.
+ final int lastX = mLastPointerX;
+ final int lastY = mLastPointerY;
+ final int textSize = (int)paint.getTextSize();
+ final int canvasWidth = canvas.getWidth();
+
+ final int halfTextWidth = (int)paint.measureText(gestureFloatingPreviewText) / 2 + textSize;
+ final int textX = Math.min(Math.max(lastX, halfTextWidth), canvasWidth - halfTextWidth);
+
+ int textY = Math.max(-textSize, lastY - mGestureFloatingPreviewTextOffset);
+ if (textY < 0) {
+ // Paint black text shadow if preview extends above keyboard region.
+ paint.setStyle(Paint.Style.FILL_AND_STROKE);
+ paint.setColor(mGestureFloatingPreviewTextShadowColor);
+ paint.setStrokeWidth(mGestureFloatingPreviewTextShadowBorder);
+ canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
+ }
+
+ // Paint the vertical line connecting the touch point to the preview text.
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(mGestureFloatingPreviewTextConnectorColor);
+ paint.setStrokeWidth(mGestureFloatingPreviewTextConnectorWidth);
+ final int lineTopY = textY - textSize / 4;
+ canvas.drawLine(lastX, lastY, lastX, lineTopY, paint);
+ if (lastX != textX) {
+ // Paint the horizontal line connection the touch point to the preview text.
+ canvas.drawLine(lastX, lineTopY, textX, lineTopY, paint);
+ }
+
+ // Paint the shading for the text preview
+ paint.setStyle(Paint.Style.FILL_AND_STROKE);
+ paint.setColor(mGestureFloatingPreviewTextShadingColor);
+ paint.setStrokeWidth(mGestureFloatingPreviewTextShadingBorder);
+ canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
+
+ // Paint the text preview
+ paint.setColor(mGestureFloatingPreviewTextColor);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java
index 107138395..9e2cbec52 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java
@@ -14,17 +14,19 @@
* the License.
*/
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
public class SuddenJumpingTouchEventHandler {
private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
@@ -70,7 +72,7 @@ public class SuddenJumpingTouchEventHandler {
* the sudden moves subside, a DOWN event is simulated for the second key.
* @param me the motion event
* @return true if the event was consumed, so that it doesn't continue to be handled by
- * {@link LatinKeyboardView}.
+ * {@link MainKeyboardView}.
*/
private boolean handleSuddenJumping(MotionEvent me) {
if (!mNeedsSuddenJumpingHack)
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index f8f1395b3..4b47a261f 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -91,7 +91,7 @@ public class AdditionalSubtype {
}
final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR);
final ArrayList<InputMethodSubtype> subtypesList =
- new ArrayList<InputMethodSubtype>(prefSubtypeArray.length);
+ CollectionUtils.newArrayList(prefSubtypeArray.length);
for (final String prefSubtype : prefSubtypeArray) {
final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype);
if (subtype.getNameResId() == SubtypeLocale.UNKNOWN_KEYBOARD_LAYOUT) {
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 779a38823..d01592a4d 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -89,7 +89,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- final TreeSet<SubtypeLocaleItem> items = new TreeSet<SubtypeLocaleItem>();
+ final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet();
final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context);
final int count = imi.getSubtypeCount();
for (int i = 0; i < count; i++) {
@@ -533,7 +533,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
private InputMethodSubtype[] getSubtypes() {
final PreferenceGroup group = getPreferenceScreen();
- final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList();
final int count = group.getPreferenceCount();
for (int i = 0; i < count; i++) {
final Preference pref = group.getPreference(i);
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index e0452483c..01ba30077 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -21,34 +21,17 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.text.TextUtils;
import android.util.Log;
-import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
public class AutoCorrection {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrection.class.getSimpleName();
+ private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
private AutoCorrection() {
// Purely static class: can't instantiate.
}
- public static CharSequence computeAutoCorrectionWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord, final float autoCorrectionThreshold,
- final CharSequence whitelistedWord) {
- if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
- return whitelistedWord;
- } else if (hasAutoCorrectionForConsideredWord(
- dictionaries, wordComposer, suggestions, consideredWord)) {
- return consideredWord;
- } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions,
- consideredWord, autoCorrectionThreshold)) {
- return suggestions.get(0).mWord;
- }
- return null;
- }
-
public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
CharSequence word, boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
@@ -56,7 +39,6 @@ public class AutoCorrection {
}
final CharSequence lowerCasedWord = word.toString().toLowerCase();
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
// managing to get null in here. Presumably the language is changing to a language with
@@ -81,7 +63,6 @@ public class AutoCorrection {
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
@@ -92,46 +73,26 @@ public class AutoCorrection {
return maxFreq;
}
- public static boolean allowsToBeAutoCorrected(
+ // Returns true if this isn't in any dictionary.
+ public static boolean isNotAWord(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
- final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
- // If "word" is in the whitelist dictionary, it should not be auto corrected.
- if (whitelistDictionary != null
- && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
- return true;
- }
return !isValidWord(dictionaries, word, ignoreCase);
}
- private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) {
- return whiteListedWord != null;
- }
-
- private static boolean hasAutoCorrectionForConsideredWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord) {
- if (TextUtils.isEmpty(consideredWord)) return false;
- return wordComposer.size() > 1 && suggestions.size() > 0
- && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
- }
-
- private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
- ArrayList<SuggestedWordInfo> suggestions,
+ public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
CharSequence consideredWord, float autoCorrectionThreshold) {
- if (wordComposer.size() > 1 && suggestions.size() > 0) {
- final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
- //final int autoCorrectionSuggestionScore = sortedScores[0];
- final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore;
+ if (null != suggestion) {
+ // Shortlist a whitelisted word
+ if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true;
+ final int autoCorrectionSuggestionScore = suggestion.mScore;
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
final float normalizedScore = BinaryDictionary.calcNormalizedScore(
- consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
+ consideredWord.toString(), suggestion.mWord.toString(),
autoCorrectionSuggestionScore);
if (DBG) {
- Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","
+ Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
+ "(" + autoCorrectionThreshold + ")");
}
@@ -139,10 +100,43 @@ public class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return true;
+ return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
+ suggestion.mWord);
}
}
return false;
}
+ // TODO: Resolve the inconsistencies between the native auto correction algorithms and
+ // this safety net
+ public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
+ final CharSequence suggestion) {
+ // Safety net for auto correction.
+ // Actually if we hit this safety net, it's a bug.
+ // If user selected aggressive auto correction mode, there is no need to use the safety
+ // net.
+ // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
+ // we should not use net because relatively edit distance can be big.
+ final int typedWordLength = typedWord.length();
+ if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) {
+ return false;
+ }
+ final int maxEditDistanceOfNativeDictionary =
+ (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
+ final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
+ if (DBG) {
+ Log.d(TAG, "Autocorrected edit distance = " + distance
+ + ", " + maxEditDistanceOfNativeDictionary);
+ }
+ if (distance > maxEditDistanceOfNativeDictionary) {
+ if (DBG) {
+ Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
+ Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
+ + "Turning off auto-correction.");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index d0613bd72..8909526d8 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -18,9 +18,12 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
+import android.util.SparseArray;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
@@ -40,22 +43,44 @@ public class BinaryDictionary extends Dictionary {
*/
public static final int MAX_WORD_LENGTH = 48;
public static final int MAX_WORDS = 18;
+ public static final int MAX_SPACES = 16;
private static final String TAG = "BinaryDictionary";
- private static final int MAX_BIGRAMS = 60;
+ private static final int MAX_PREDICTIONS = 60;
+ private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS);
private static final int TYPED_LETTER_MULTIPLIER = 2;
- private int mDicTypeId;
private long mNativeDict;
- private final int[] mInputCodes = new int[MAX_WORD_LENGTH];
- private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
- private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
- private final int[] mScores = new int[MAX_WORDS];
- private final int[] mBigramScores = new int[MAX_BIGRAMS];
+ private final Locale mLocale;
+ private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
+ // TODO: The below should be int[] mOutputCodePoints
+ private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
+ private final int[] mSpaceIndices = new int[MAX_SPACES];
+ private final int[] mOutputScores = new int[MAX_RESULTS];
+ private final int[] mOutputTypes = new int[MAX_RESULTS];
private final boolean mUseFullEditDistance;
+ private final SparseArray<DicTraverseSession> mDicTraverseSessions =
+ CollectionUtils.newSparseArray();
+
+ // TODO: There should be a way to remove used DicTraverseSession objects from
+ // {@code mDicTraverseSessions}.
+ private DicTraverseSession getTraverseSession(int traverseSessionId) {
+ synchronized(mDicTraverseSessions) {
+ DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
+ if (traverseSession == null) {
+ traverseSession = mDicTraverseSessions.get(traverseSessionId);
+ if (traverseSession == null) {
+ traverseSession = new DicTraverseSession(mLocale, mNativeDict);
+ mDicTraverseSessions.put(traverseSessionId, traverseSession);
+ }
+ }
+ return traverseSession;
+ }
+ }
+
/**
* Constructor for the binary dictionary. This is supposed to be called from the
* dictionary factory.
@@ -65,14 +90,13 @@ public class BinaryDictionary extends Dictionary {
* @param offset the offset of the dictionary data within the file.
* @param length the length of the binary data.
* @param useFullEditDistance whether to use the full edit distance in suggestions
+ * @param dictType the dictionary type, as a human-readable string
*/
public BinaryDictionary(final Context context,
final String filename, final long offset, final long length,
- final boolean useFullEditDistance, final Locale locale) {
- // Note: at the moment a binary dictionary is always of the "main" type.
- // Initializing this here will help transitioning out of the scheme where
- // the Suggest class knows everything about every single dictionary.
- mDicTypeId = Suggest.DIC_MAIN;
+ final boolean useFullEditDistance, final Locale locale, final String dictType) {
+ super(dictType);
+ mLocale = locale;
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
}
@@ -82,121 +106,88 @@ public class BinaryDictionary extends Dictionary {
}
private native long openNative(String sourceDir, long dictOffset, long dictSize,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords);
+ int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
+ int maxPredictions);
private native void closeNative(long dict);
- private native int getFrequencyNative(long dict, int[] word, int wordLength);
+ private native int getFrequencyNative(long dict, int[] word);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
- private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates,
- int[] yCoordinates, int[] inputCodes, int codesSize, int[] prevWordForBigrams,
- boolean useFullEditDistance, char[] outputChars, int[] scores);
- private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength,
- int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores,
- int maxWordLength, int maxBigrams);
- private static native float calcNormalizedScoreNative(
- char[] before, int beforeLength, char[] after, int afterLength, int score);
- private static native int editDistanceNative(
- char[] before, int beforeLength, char[] after, int afterLength);
-
+ private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession,
+ int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds,
+ int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture,
+ int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
+ int[] outputScores, int[] outputIndices, int[] outputTypes);
+ private static native float calcNormalizedScoreNative(char[] before, char[] after, int score);
+ private static native int editDistanceNative(char[] before, char[] after);
+
+ // TODO: Move native dict into session
private final void loadDictionary(String path, long startOffset, long length) {
- mNativeDict = openNative(path, startOffset, length,
- TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS);
+ mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
+ FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
}
@Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- if (mNativeDict == 0) return;
-
- int[] codePoints = StringUtils.toCodePointArray(previousWord.toString());
- Arrays.fill(mOutputChars_bigrams, (char) 0);
- Arrays.fill(mBigramScores, 0);
-
- int codesSize = codes.size();
- Arrays.fill(mInputCodes, -1);
- if (codesSize > 0) {
- mInputCodes[0] = codes.getCodeAt(0);
- }
-
- int count = getBigramsNative(mNativeDict, codePoints, codePoints.length, mInputCodes,
- codesSize, mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS);
- if (count > MAX_BIGRAMS) {
- count = MAX_BIGRAMS;
- }
-
- for (int j = 0; j < count; ++j) {
- if (codesSize > 0 && mBigramScores[j] < 1) break;
- final int start = j * MAX_WORD_LENGTH;
- int len = 0;
- while (len < MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) {
- ++len;
- }
- if (len > 0) {
- callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
- mDicTypeId, Dictionary.BIGRAM);
- }
- }
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
}
- // proximityInfo and/or prevWordForBigrams may not be null.
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- final int count = getSuggestions(codes, prevWordForBigrams, proximityInfo, mOutputChars,
- mScores);
+ public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ if (!isValidDictionary()) return null;
+
+ Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
+ // TODO: toLowerCase in the native code
+ final int[] prevWordCodePointArray = (null == prevWord)
+ ? null : StringUtils.toCodePointArray(prevWord.toString());
+ final int composerSize = composer.size();
+
+ final boolean isGesture = composer.isBatchMode();
+ if (composerSize <= 1 || !isGesture) {
+ if (composerSize > MAX_WORD_LENGTH - 1) return null;
+ for (int i = 0; i < composerSize; i++) {
+ mInputCodePoints[i] = composer.getCodeAt(i);
+ }
+ }
+ final InputPointers ips = composer.getInputPointers();
+ final int codesSize = isGesture ? ips.getPointerSize() : composerSize;
+ // proximityInfo and/or prevWordForBigrams may not be null.
+ final int tmpCount = getSuggestionsNative(mNativeDict,
+ proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(),
+ ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
+ mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
+ mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
+ final int count = Math.min(tmpCount, MAX_PREDICTIONS);
+
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
for (int j = 0; j < count; ++j) {
- if (mScores[j] < 1) break;
+ if (composerSize > 0 && mOutputScores[j] < 1) break;
final int start = j * MAX_WORD_LENGTH;
int len = 0;
while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
++len;
}
if (len > 0) {
- callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
- Dictionary.UNIGRAM);
+ final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
+ ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
+ suggestions.add(new SuggestedWordInfo(
+ new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType));
}
}
+ return suggestions;
}
/* package for test */ boolean isValidDictionary() {
return mNativeDict != 0;
}
- // proximityInfo may not be null.
- /* package for test */ int getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo,
- char[] outputChars, int[] scores) {
- if (!isValidDictionary()) return -1;
-
- final int codesSize = codes.size();
- // Won't deal with really long words.
- if (codesSize > MAX_WORD_LENGTH - 1) return -1;
-
- Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
- for (int i = 0; i < codesSize; i++) {
- mInputCodes[i] = codes.getCodeAt(i);
- }
- Arrays.fill(outputChars, (char) 0);
- Arrays.fill(scores, 0);
-
- final int[] prevWordCodePointArray = null == prevWordForBigrams
- ? null : StringUtils.toCodePointArray(prevWordForBigrams.toString());
-
- // TODO: pass the previous word to native code
- return getSuggestionsNative(
- mNativeDict, proximityInfo.getNativeProximityInfo(),
- codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
- prevWordCodePointArray, mUseFullEditDistance, outputChars, scores);
- }
-
public static float calcNormalizedScore(String before, String after, int score) {
- return calcNormalizedScoreNative(before.toCharArray(), before.length(),
- after.toCharArray(), after.length(), score);
+ return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score);
}
public static int editDistance(String before, String after) {
- return editDistanceNative(
- before.toCharArray(), before.length(), after.toCharArray(), after.length());
+ return editDistanceNative(before.toCharArray(), after.toCharArray());
}
@Override
@@ -207,8 +198,8 @@ public class BinaryDictionary extends Dictionary {
@Override
public int getFrequency(CharSequence word) {
if (word == null) return -1;
- int[] chars = StringUtils.toCodePointArray(word.toString());
- return getFrequencyNative(mNativeDict, chars, chars.length);
+ int[] codePoints = StringUtils.toCodePointArray(word.toString());
+ return getFrequencyNative(mNativeDict, codePoints);
}
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
@@ -221,11 +212,20 @@ public class BinaryDictionary extends Dictionary {
}
@Override
- public synchronized void close() {
+ public void close() {
+ synchronized (mDicTraverseSessions) {
+ final int sessionsSize = mDicTraverseSessions.size();
+ for (int index = 0; index < sessionsSize; ++index) {
+ final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
+ if (traverseSession != null) {
+ traverseSession.close();
+ }
+ }
+ }
closeInternal();
}
- private void closeInternal() {
+ private synchronized void closeInternal() {
if (mNativeDict != 0) {
closeNative(mNativeDict);
mNativeDict = 0;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 236c198ad..799aea8ef 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -99,7 +99,7 @@ public class BinaryDictionaryFileDumper {
}
try {
- final List<WordListInfo> list = new ArrayList<WordListInfo>();
+ final List<WordListInfo> list = CollectionUtils.newArrayList();
do {
final String wordListId = c.getString(0);
final String wordListLocale = c.getString(1);
@@ -267,7 +267,7 @@ public class BinaryDictionaryFileDumper {
final ContentResolver resolver = context.getContentResolver();
final List<WordListInfo> idList = getWordListWordListInfos(locale, context,
hasDefaultWordList);
- final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+ final List<AssetFileAddress> fileAddressList = CollectionUtils.newArrayList();
for (WordListInfo id : idList) {
final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context);
if (null != afd) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 063243e1b..e1cb195bc 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -23,6 +25,10 @@ import android.content.res.AssetFileDescriptor;
import android.util.Log;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
@@ -51,6 +57,9 @@ class BinaryDictionaryGetter {
private static final String MAIN_DICTIONARY_CATEGORY = "main";
public static final String ID_CATEGORY_SEPARATOR = ":";
+ // The key considered to read the version attribute in a dictionary file.
+ private static String VERSION_KEY = "version";
+
// Prevents this from being instantiated
private BinaryDictionaryGetter() {}
@@ -254,8 +263,7 @@ class BinaryDictionaryGetter {
final Context context) {
final File[] directoryList = getCachedDirectoryList(context);
if (null == directoryList) return EMPTY_FILE_ARRAY;
- final HashMap<String, FileAndMatchLevel> cacheFiles =
- new HashMap<String, FileAndMatchLevel>();
+ final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
for (File directory : directoryList) {
if (!directory.isDirectory()) continue;
final String dirLocale = getWordListIdFromFileName(directory.getName());
@@ -336,6 +344,54 @@ class BinaryDictionaryGetter {
return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
}
+ // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason
+ // for this is, since those do not include whitelist entries, the new code with an old version
+ // of the dictionary would lose whitelist functionality.
+ private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
+ // Only for English - other languages didn't have a whitelist, hence this
+ // ad-hock ## HACK ##
+ if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true;
+
+ FileInputStream inStream = null;
+ try {
+ // Read the version of the file
+ inStream = new FileInputStream(f);
+ final ByteBuffer buffer = inStream.getChannel().map(
+ FileChannel.MapMode.READ_ONLY, 0, f.length());
+ final int magic = buffer.getInt();
+ if (magic != BinaryDictInputOutput.VERSION_2_MAGIC_NUMBER) {
+ return false;
+ }
+ final int formatVersion = buffer.getInt();
+ final int headerSize = buffer.getInt();
+ final HashMap<String, String> options = CollectionUtils.newHashMap();
+ BinaryDictInputOutput.populateOptions(buffer, headerSize, options);
+
+ final String version = options.get(VERSION_KEY);
+ if (null == version) {
+ // No version in the options : the format is unexpected
+ return false;
+ }
+ // Version 18 is the first one to include the whitelist
+ // Obviously this is a big ## HACK ##
+ return Integer.parseInt(version) >= 18;
+ } catch (java.io.FileNotFoundException e) {
+ return false;
+ } catch (java.io.IOException e) {
+ return false;
+ } catch (NumberFormatException e) {
+ return false;
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
/**
* Returns a list of file addresses for a given locale, trying relevant methods in order.
*
@@ -362,18 +418,19 @@ class BinaryDictionaryGetter {
final DictPackSettings dictPackSettings = new DictPackSettings(context);
boolean foundMainDict = false;
- final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
+ final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList();
// cachedWordLists may not be null, see doc for getCachedDictionaryList
for (final File f : cachedWordLists) {
final String wordListId = getWordListIdFromFileName(f.getName());
- if (isMainWordListId(wordListId)) {
+ final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f);
+ if (canUse && isMainWordListId(wordListId)) {
foundMainDict = true;
}
if (!dictPackSettings.isWordListActive(wordListId)) continue;
- if (f.canRead()) {
+ if (canUse) {
fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
} else {
- Log.e(TAG, "Found a cached dictionary file but cannot read it");
+ Log.e(TAG, "Found a cached dictionary file but cannot read or use it");
}
}
diff --git a/java/src/com/android/inputmethod/latin/BoundedTreeSet.java b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
new file mode 100644
index 000000000..cf977617d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * A TreeSet that is bounded in size and throws everything that's smaller than its limit
+ */
+public class BoundedTreeSet extends TreeSet<SuggestedWordInfo> {
+ private final int mCapacity;
+ public BoundedTreeSet(final Comparator<SuggestedWordInfo> comparator, final int capacity) {
+ super(comparator);
+ mCapacity = capacity;
+ }
+
+ @Override
+ public boolean add(final SuggestedWordInfo e) {
+ if (size() < mCapacity) return super.add(e);
+ if (comparator().compare(e, last()) > 0) return false;
+ super.add(e);
+ pollLast(); // removes the last element
+ return true;
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
+ if (null == e) return false;
+ return super.addAll(e);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java
new file mode 100644
index 000000000..baa2ee1cd
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class CollectionUtils {
+ private CollectionUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static <K,V> HashMap<K,V> newHashMap() {
+ return new HashMap<K,V>();
+ }
+
+ public static <K,V> TreeMap<K,V> newTreeMap() {
+ return new TreeMap<K,V>();
+ }
+
+ public static <K, V> Map<K,V> newSynchronizedTreeMap() {
+ final TreeMap<K,V> treeMap = newTreeMap();
+ return Collections.synchronizedMap(treeMap);
+ }
+
+ public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() {
+ return new ConcurrentHashMap<K,V>();
+ }
+
+ public static <E> HashSet<E> newHashSet() {
+ return new HashSet<E>();
+ }
+
+ public static <E> TreeSet<E> newTreeSet() {
+ return new TreeSet<E>();
+ }
+
+ public static <E> ArrayList<E> newArrayList() {
+ return new ArrayList<E>();
+ }
+
+ public static <E> ArrayList<E> newArrayList(final int initialCapacity) {
+ return new ArrayList<E>(initialCapacity);
+ }
+
+ public static <E> ArrayList<E> newArrayList(final Collection<E> collection) {
+ return new ArrayList<E>(collection);
+ }
+
+ public static <E> LinkedList<E> newLinkedList() {
+ return new LinkedList<E>();
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
+ return new CopyOnWriteArrayList<E>();
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(
+ final Collection<E> collection) {
+ return new CopyOnWriteArrayList<E>(collection);
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) {
+ return new CopyOnWriteArrayList<E>(array);
+ }
+
+ public static <E> SparseArray<E> newSparseArray() {
+ return new SparseArray<E>();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index e79db367c..d71c0f995 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -19,6 +19,13 @@ package com.android.inputmethod.latin;
import android.view.inputmethod.EditorInfo;
public final class Constants {
+ public static final class Color {
+ /**
+ * The alpha value for fully opaque.
+ */
+ public final static int ALPHA_OPAQUE = 255;
+ }
+
public static final class ImeOption {
/**
* The private IME option used to indicate that no microphone should be shown for a given
@@ -121,6 +128,13 @@ public final class Constants {
}
}
+ public static final int NOT_A_CODE = -1;
+
+ // See {@link KeyboardActionListener.Adapter#isInvalidCoordinate(int)}.
+ public static final int NOT_A_COORDINATE = -1;
+ public static final int SUGGESTION_STRIP_COORDINATE = -2;
+ public static final int SPELL_CHECKER_COORDINATE = -3;
+
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 34308dfb3..5edc4314f 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -52,6 +52,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/** The number of contacts in the most recent dictionary rebuild. */
static private int sContactCountAtLastRebuild = 0;
+ /** The locale for this contacts dictionary. Controls name bigram predictions. */
+ public final Locale mLocale;
+
private ContentObserver mObserver;
/**
@@ -59,8 +62,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private final boolean mUseFirstLastBigrams;
- public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) {
- super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId);
+ public ContactsBinaryDictionary(final Context context, Locale locale) {
+ super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
+ mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
@@ -116,12 +120,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
}
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- super.getBigrams(codes, previousWord, callback);
- }
-
private boolean useFirstLastBigramsForLocale(Locale locale) {
// TODO: Add firstname/lastname bigram rules for other languages.
if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
@@ -163,7 +161,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* bigrams depending on locale.
*/
private void addName(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
@@ -173,7 +171,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS);
if (!TextUtils.isEmpty(prevWord)) {
@@ -262,14 +260,14 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* Checks if the words in a name are in the current binary dictionary.
*/
private boolean isNameInDictionary(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
int end = getWordEndPosition(name, len, i);
String word = name.substring(i, end);
i = end - 1;
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
if (!super.isValidBigramLocked(prevWord, word)) {
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
deleted file mode 100644
index cbfbd0ec8..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.os.SystemClock;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.keyboard.Keyboard;
-
-// TODO: This class is superseded by {@link ContactsBinaryDictionary}. Should be cleaned up.
-/**
- * An expandable dictionary that stores the words from Contacts provider.
- *
- * @deprecated Use {@link ContactsBinaryDictionary}.
- */
-@Deprecated
-public class ContactsDictionary extends ExpandableDictionary {
-
- private static final String[] PROJECTION = {
- BaseColumns._ID,
- Contacts.DISPLAY_NAME,
- };
-
- private static final String TAG = "ContactsDictionary";
-
- /**
- * Frequency for contacts information into the dictionary
- */
- private static final int FREQUENCY_FOR_CONTACTS = 40;
- private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
-
- private static final int INDEX_NAME = 1;
-
- private ContentObserver mObserver;
-
- private long mLastLoadedContacts;
-
- public ContactsDictionary(final Context context, final int dicTypeId) {
- super(context, dicTypeId);
- registerObserver(context);
- loadDictionary();
- }
-
- private synchronized void registerObserver(final Context context) {
- // Perform a managed query. The Activity will handle closing and requerying the cursor
- // when needed.
- if (mObserver != null) return;
- ContentResolver cres = context.getContentResolver();
- cres.registerContentObserver(
- Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- setRequiresReload(true);
- }
- });
- }
-
- public void reopen(final Context context) {
- registerObserver(context);
- }
-
- @Override
- public synchronized void close() {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- super.close();
- }
-
- @Override
- public void startDictionaryLoadingTaskLocked() {
- long now = SystemClock.uptimeMillis();
- if (mLastLoadedContacts == 0
- || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
- super.startDictionaryLoadingTaskLocked();
- }
- }
-
- @Override
- public void loadDictionaryAsync() {
- try {
- Cursor cursor = getContext().getContentResolver()
- .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
- if (cursor != null) {
- addWords(cursor);
- }
- } catch(IllegalStateException e) {
- Log.e(TAG, "Contacts DB is having problems");
- }
- mLastLoadedContacts = SystemClock.uptimeMillis();
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- // Do not return bigrams from Contacts when nothing was typed.
- if (codes.size() <= 0) return;
- super.getBigrams(codes, previousWord, callback);
- }
-
- private void addWords(Cursor cursor) {
- clearDictionary();
-
- final int maxWordLength = getMaxWordLength();
- try {
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast()) {
- String name = cursor.getString(INDEX_NAME);
-
- if (name != null && -1 == name.indexOf('@')) {
- int len = name.length();
- String prevWord = null;
-
- // TODO: Better tokenization for non-Latin writing systems
- for (int i = 0; i < len; i++) {
- if (Character.isLetter(name.charAt(i))) {
- int j;
- for (j = i + 1; j < len; j++) {
- char c = name.charAt(j);
-
- if (!(c == Keyboard.CODE_DASH
- || c == Keyboard.CODE_SINGLE_QUOTE
- || Character.isLetter(c))) {
- break;
- }
- }
-
- String word = name.substring(i, j);
- i = j - 1;
-
- // Safeguard against adding really long words. Stack
- // may overflow due to recursion
- // Also don't add single letter words, possibly confuses
- // capitalization of i.
- final int wordLen = word.length();
- if (wordLen < maxWordLength && wordLen > 1) {
- super.addWord(word, null /* shortcut */,
- FREQUENCY_FOR_CONTACTS);
- if (!TextUtils.isEmpty(prevWord)) {
- super.setBigramAndGetFrequency(prevWord, word,
- FREQUENCY_FOR_CONTACTS_BIGRAM);
- }
- prevWord = word;
- }
- }
- }
- }
- cursor.moveToNext();
- }
- }
- cursor.close();
- } catch(IllegalStateException e) {
- Log.e(TAG, "Contacts DB is having problems");
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
new file mode 100644
index 000000000..359da72cc
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.Locale;
+
+public class DicTraverseSession {
+ static {
+ JniUtils.loadNativeLibrary();
+ }
+
+ private native long setDicTraverseSessionNative(String locale);
+ private native void initDicTraverseSessionNative(long nativeDicTraverseSession,
+ long dictionary, int[] previousWord, int previousWordLength);
+ private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession);
+
+ private long mNativeDicTraverseSession;
+
+ public DicTraverseSession(Locale locale, long dictionary) {
+ mNativeDicTraverseSession = createNativeDicTraverseSession(
+ locale != null ? locale.toString() : "");
+ initSession(dictionary);
+ }
+
+ public long getSession() {
+ return mNativeDicTraverseSession;
+ }
+
+ public void initSession(long dictionary) {
+ initSession(dictionary, null, 0);
+ }
+
+ public void initSession(long dictionary, int[] previousWord, int previousWordLength) {
+ initDicTraverseSessionNative(
+ mNativeDicTraverseSession, dictionary, previousWord, previousWordLength);
+ }
+
+ private final long createNativeDicTraverseSession(String locale) {
+ return setDicTraverseSessionNative(locale);
+ }
+
+ private void closeInternal() {
+ if (mNativeDicTraverseSession != 0) {
+ releaseDicTraverseSessionNative(mNativeDicTraverseSession);
+ mNativeDicTraverseSession = 0;
+ }
+ }
+
+ public void close() {
+ closeInternal();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ closeInternal();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 7cd9bc2a8..88d0c09dd 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -17,6 +17,9 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
/**
* Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
@@ -28,54 +31,41 @@ public abstract class Dictionary {
*/
protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
- public static final int UNIGRAM = 0;
- public static final int BIGRAM = 1;
-
public static final int NOT_A_PROBABILITY = -1;
- /**
- * Interface to be implemented by classes requesting words to be fetched from the dictionary.
- * @see #getWords(WordComposer, CharSequence, WordCallback, ProximityInfo)
- */
- public interface WordCallback {
- /**
- * Adds a word to a list of suggestions. The word is expected to be ordered based on
- * the provided score.
- * @param word the character array containing the word
- * @param wordOffset starting offset of the word in the character array
- * @param wordLength length of valid characters in the character array
- * @param score the score of occurrence. This is normalized between 1 and 255, but
- * can exceed those limits
- * @param dicTypeId of the dictionary where word was from
- * @param dataType tells type of this data, either UNIGRAM or BIGRAM
- * @return true if the word was added, false if no more words are required
- */
- boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId,
- int dataType);
+
+ public static final String TYPE_USER_TYPED = "user_typed";
+ public static final String TYPE_APPLICATION_DEFINED = "application_defined";
+ public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
+ public static final String TYPE_MAIN = "main";
+ public static final String TYPE_CONTACTS = "contacts";
+ // User dictionary, the system-managed one.
+ public static final String TYPE_USER = "user";
+ // User history dictionary internal to LatinIME.
+ public static final String TYPE_USER_HISTORY = "history";
+ protected final String mDictType;
+
+ public Dictionary(final String dictType) {
+ mDictType = dictType;
}
/**
- * Searches for words in the dictionary that match the characters in the composer. Matched
- * words are added through the callback object.
- * @param composer the key sequence to match
- * @param prevWordForBigrams the previous word, or null if none
- * @param callback the callback object to send matched words to as possible candidates
+ * Searches for suggestions for a given context. For the moment the context is only the
+ * previous word.
+ * @param composer the key sequence to match with coordinate info, as a WordComposer
+ * @param prevWord the previous word, or null if none
* @param proximityInfo the object for key proximity. May be ignored by some implementations.
- * @see WordCallback#addWord(char[], int, int, int, int, int)
+ * @return the list of suggestions (possibly null if none)
*/
- abstract public void getWords(final WordComposer composer,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo);
+ // TODO: pass more context than just the previous word, to enable better suggestions (n-gram
+ // and more)
+ abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo);
- /**
- * Searches for pairs in the bigram dictionary that matches the previous word and all the
- * possible words following are added through the callback object.
- * @param composer the key sequence to match
- * @param previousWord the word before
- * @param callback the callback object to send possible word following previous word
- */
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- // empty base implementation
+ // The default implementation of this method ignores sessionId.
+ // Subclasses that want to use sessionId need to override this method.
+ public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ return getSuggestions(composer, prevWord, proximityInfo);
}
/**
@@ -115,4 +105,12 @@ public abstract class Dictionary {
public void close() {
// empty base implementation
}
+
+ /**
+ * Subclasses may override to indicate that this Dictionary is not yet properly initialized.
+ */
+
+ public boolean isInitialized() {
+ return true;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 1a05fcd86..4acab6b05 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -17,9 +17,11 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -31,36 +33,44 @@ public class DictionaryCollection extends Dictionary {
private final String TAG = DictionaryCollection.class.getSimpleName();
protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
- public DictionaryCollection() {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+ public DictionaryCollection(final String dictType) {
+ super(dictType);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
}
- public DictionaryCollection(Dictionary... dictionaries) {
+ public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
+ super(dictType);
if (null == dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
} else {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
}
- public DictionaryCollection(Collection<Dictionary> dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
+ super(dictType);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
@Override
- public void getWords(final WordComposer composer, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- for (final Dictionary dict : mDictionaries)
- dict.getWords(composer, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- for (final Dictionary dict : mDictionaries)
- dict.getBigrams(composer, previousWord, callback);
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
+ if (dictionaries.isEmpty()) return null;
+ // To avoid creating unnecessary objects, we get the list out of the first
+ // dictionary and add the rest to it if not null, hence the get(0)
+ ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
+ prevWord, proximityInfo);
+ if (null == suggestions) suggestions = CollectionUtils.newArrayList();
+ final int length = dictionaries.size();
+ for (int i = 1; i < length; ++ i) {
+ final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
+ prevWord, proximityInfo);
+ if (null != sugg) suggestions.addAll(sugg);
+ }
+ return suggestions;
}
@Override
@@ -82,8 +92,9 @@ public class DictionaryCollection extends Dictionary {
return maxFreq;
}
- public boolean isEmpty() {
- return mDictionaries.isEmpty();
+ @Override
+ public boolean isInitialized() {
+ return !mDictionaries.isEmpty();
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index a22d73af7..cdd01d0c7 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -49,17 +49,18 @@ public class DictionaryFactory {
final Locale locale, final boolean useFullEditDistance) {
if (null == locale) {
Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(createBinaryDictionary(context, locale));
+ return new DictionaryCollection(Dictionary.TYPE_MAIN,
+ createBinaryDictionary(context, locale));
}
- final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
+ final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList();
final ArrayList<AssetFileAddress> assetFileList =
BinaryDictionaryGetter.getDictionaryFiles(locale, context);
if (null != assetFileList) {
for (final AssetFileAddress f : assetFileList) {
final BinaryDictionary binaryDictionary =
new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
if (binaryDictionary.isValidDictionary()) {
dictList.add(binaryDictionary);
}
@@ -69,7 +70,7 @@ public class DictionaryFactory {
// If the list is empty, that means we should not use any dictionary (for example, the user
// explicitly disabled the main dictionary), so the following is okay. dictList is never
// null, but if for some reason it is, DictionaryCollection handles it gracefully.
- return new DictionaryCollection(dictList);
+ return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList);
}
/**
@@ -112,7 +113,7 @@ public class DictionaryFactory {
return null;
}
return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(),
- false /* useFullEditDistance */, locale);
+ false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
} catch (android.content.res.Resources.NotFoundException e) {
Log.e(TAG, "Could not find the resource");
return null;
@@ -140,7 +141,7 @@ public class DictionaryFactory {
long startOffset, long length, final boolean useFullEditDistance, Locale locale) {
if (dictionary.isFile()) {
return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
} else {
Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
return null;
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
deleted file mode 100644
index 0f34d50bb..000000000
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-
-import java.util.regex.Pattern;
-
-/**
- * Utility methods to deal with editing text through an InputConnection.
- */
-public class EditingUtils {
- /**
- * Number of characters we want to look back in order to identify the previous word
- */
- // Provision for a long word pair and a separator
- private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1;
- private static final int INVALID_CURSOR_POSITION = -1;
-
- private EditingUtils() {
- // Unintentional empty constructor for singleton.
- }
-
- private static int getCursorPosition(InputConnection connection) {
- if (null == connection) return INVALID_CURSOR_POSITION;
- final ExtractedText extracted = connection.getExtractedText(new ExtractedTextRequest(), 0);
- if (extracted == null) {
- return INVALID_CURSOR_POSITION;
- }
- return extracted.startOffset + extracted.selectionStart;
- }
-
- /**
- * @param connection connection to the current text field.
- * @param separators characters which may separate words
- * @return the word that surrounds the cursor, including up to one trailing
- * separator. For example, if the field contains "he|llo world", where |
- * represents the cursor, then "hello " will be returned.
- */
- public static String getWordAtCursor(InputConnection connection, String separators) {
- // getWordRangeAtCursor returns null if the connection is null
- Range r = getWordRangeAtCursor(connection, separators);
- return (r == null) ? null : r.mWord;
- }
-
- /**
- * Represents a range of text, relative to the current cursor position.
- */
- public static class Range {
- /** Characters before selection start */
- public final int mCharsBefore;
-
- /**
- * Characters after selection start, including one trailing word
- * separator.
- */
- public final int mCharsAfter;
-
- /** The actual characters that make up a word */
- public final String mWord;
-
- public Range(int charsBefore, int charsAfter, String word) {
- if (charsBefore < 0 || charsAfter < 0) {
- throw new IndexOutOfBoundsException();
- }
- this.mCharsBefore = charsBefore;
- this.mCharsAfter = charsAfter;
- this.mWord = word;
- }
- }
-
- private static Range getWordRangeAtCursor(InputConnection connection, String sep) {
- if (connection == null || sep == null) {
- return null;
- }
- CharSequence before = connection.getTextBeforeCursor(1000, 0);
- CharSequence after = connection.getTextAfterCursor(1000, 0);
- if (before == null || after == null) {
- return null;
- }
-
- // Find first word separator before the cursor
- int start = before.length();
- while (start > 0 && !isWhitespace(before.charAt(start - 1), sep)) start--;
-
- // Find last word separator after the cursor
- int end = -1;
- while (++end < after.length() && !isWhitespace(after.charAt(end), sep)) {
- // Nothing to do here.
- }
-
- int cursor = getCursorPosition(connection);
- if (start >= 0 && cursor + end <= after.length() + before.length()) {
- String word = before.toString().substring(start, before.length())
- + after.toString().substring(0, end);
- return new Range(before.length() - start, end, word);
- }
-
- return null;
- }
-
- private static boolean isWhitespace(int code, String whitespace) {
- return whitespace.contains(String.valueOf((char) code));
- }
-
- private static final Pattern spaceRegex = Pattern.compile("\\s+");
-
- public static CharSequence getPreviousWord(InputConnection connection,
- String sentenceSeperators) {
- //TODO: Should fix this. This could be slow!
- if (null == connection) return null;
- CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
- return getPreviousWord(prev, sentenceSeperators);
- }
-
- // Get the word before the whitespace preceding the non-whitespace preceding the cursor.
- // Also, it won't return words that end in a separator.
- // Example :
- // "abc def|" -> abc
- // "abc def |" -> abc
- // "abc def. |" -> abc
- // "abc def . |" -> def
- // "abc|" -> null
- // "abc |" -> null
- // "abc. def|" -> null
- public static CharSequence getPreviousWord(CharSequence prev, String sentenceSeperators) {
- if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
-
- // If we can't find two words, or we found an empty word, return null.
- if (w.length < 2 || w[w.length - 2].length() <= 0) return null;
-
- // If ends in a separator, return null
- char lastChar = w[w.length - 2].charAt(w[w.length - 2].length() - 1);
- if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
-
- return w[w.length - 2];
- }
-
- public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) {
- if (null == connection) return null;
- final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
- return getThisWord(prev, sentenceSeperators);
- }
-
- // Get the word immediately before the cursor, even if there is whitespace between it and
- // the cursor - but not if there is punctuation.
- // Example :
- // "abc def|" -> def
- // "abc def |" -> def
- // "abc def. |" -> null
- // "abc def . |" -> null
- public static CharSequence getThisWord(CharSequence prev, String sentenceSeperators) {
- if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
-
- // No word : return null
- if (w.length < 1 || w[w.length - 1].length() <= 0) return null;
-
- // If ends in a separator, return null
- char lastChar = w[w.length - 1].charAt(w[w.length - 1].length() - 1);
- if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
-
- return w[w.length - 1];
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index c65404cbc..cdf5247de 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -19,6 +19,7 @@ import android.os.SystemClock;
import android.util.Log;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
@@ -61,7 +62,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* that filename.
*/
private static final HashMap<String, DictionaryController> sSharedDictionaryControllers =
- new HashMap<String, DictionaryController>();
+ CollectionUtils.newHashMap();
/** The application context. */
protected final Context mContext;
@@ -75,9 +76,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/** The expandable fusion dictionary used to generate the binary dictionary. */
private FusionDictionary mFusionDictionary;
- /** The dictionary type id. */
- public final int mDicTypeId;
-
/**
* The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
* dictionary instances with the same filename is supported, with access controlled by
@@ -123,11 +121,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* @param context The application context of the parent.
* @param filename The filename for this binary dictionary. Multiple dictionaries with the same
* filename is supported.
- * @param dictType The type of this dictionary.
+ * @param dictType the dictionary type, as a human-readable string
*/
public ExpandableBinaryDictionary(
- final Context context, final String filename, final int dictType) {
- mDicTypeId = dictType;
+ final Context context, final String filename, final String dictType) {
+ super(dictType);
mFilename = filename;
mContext = context;
mBinaryDictionary = null;
@@ -161,9 +159,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* the native side.
*/
public void clearFusionDictionary() {
+ final HashMap<String, String> attributes = CollectionUtils.newHashMap();
mFusionDictionary = new FusionDictionary(new Node(),
- new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
- false));
+ new FusionDictionary.DictionaryOptions(attributes, false, false));
}
/**
@@ -177,7 +175,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
mFusionDictionary.add(word, frequency, null);
} else {
// TODO: Do this in the subclass, with this class taking an arraylist.
- final ArrayList<WeightedString> shortcutTargets = new ArrayList<WeightedString>();
+ final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList();
shortcutTargets.add(new WeightedString(shortcutTarget, frequency));
mFusionDictionary.add(word, frequency, shortcutTargets);
}
@@ -194,46 +192,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- protected final void getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- // Ensure that there are no concurrent calls to getWords. If there are, do nothing and
- // return.
- if (mLocalDictionaryController.tryLock()) {
- try {
- if (mBinaryDictionary != null) {
- mBinaryDictionary.getWords(codes, prevWordForBigrams, callback, proximityInfo);
- }
- } finally {
- mLocalDictionaryController.unlock();
- }
- }
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- asyncReloadDictionaryIfRequired();
- getBigramsInner(codes, previousWord, callback);
- }
-
- protected void getBigramsInner(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
if (mLocalDictionaryController.tryLock()) {
try {
if (mBinaryDictionary != null) {
- mBinaryDictionary.getBigrams(codes, previousWord, callback);
+ return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo);
}
} finally {
mLocalDictionaryController.unlock();
}
}
+ return null;
}
@Override
@@ -306,7 +277,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary
final BinaryDictionary newBinaryDictionary =
new BinaryDictionary(mContext, filename, 0, length, true /* useFullEditDistance */,
- null);
+ null, mDictType);
if (mBinaryDictionary != null) {
// Ensure all threads accessing the current dictionary have finished before swapping in
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 34a92fd30..8a38d1e1b 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -17,10 +17,11 @@
package com.android.inputmethod.latin;
import android.content.Context;
+import android.text.TextUtils;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
import java.util.ArrayList;
@@ -37,7 +38,6 @@ public class ExpandableDictionary extends Dictionary {
private Context mContext;
private char[] mWordBuilder = new char[BinaryDictionary.MAX_WORD_LENGTH];
- private int mDicTypeId;
private int mMaxDepth;
private int mInputLength;
@@ -151,11 +151,11 @@ public class ExpandableDictionary extends Dictionary {
private int[][] mCodes;
- public ExpandableDictionary(Context context, int dicTypeId) {
+ public ExpandableDictionary(final Context context, final String dictType) {
+ super(dictType);
mContext = context;
clearDictionary();
mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][];
- mDicTypeId = dicTypeId;
}
public void loadDictionary() {
@@ -230,7 +230,7 @@ public class ExpandableDictionary extends Dictionary {
childNode.mTerminal = true;
if (isShortcutOnly) {
if (null == childNode.mShortcutTargets) {
- childNode.mShortcutTargets = new ArrayList<char[]>();
+ childNode.mShortcutTargets = CollectionUtils.newArrayList();
}
childNode.mShortcutTargets.add(shortcutTarget.toCharArray());
} else {
@@ -247,27 +247,43 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ if (reloadDictionaryIfRequired()) return null;
+ if (composer.size() > 1) {
+ if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
+ return null;
+ }
+ final ArrayList<SuggestedWordInfo> suggestions =
+ getWordsInner(composer, prevWord, proximityInfo);
+ return suggestions;
+ } else {
+ if (TextUtils.isEmpty(prevWord)) return null;
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ runBigramReverseLookUp(prevWord, suggestions);
+ return suggestions;
+ }
+ }
+
+ // This reloads the dictionary if required, and returns whether it's currently updating its
+ // contents or not.
+ // @VisibleForTesting
+ boolean reloadDictionaryIfRequired() {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
- // Currently updating contacts, don't return any results.
- if (mUpdatingDictionary) return;
- }
- if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
- return;
+ return mUpdatingDictionary;
}
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
}
- protected final void getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
- final int[] xCoordinates = codes.getXCoordinates();
- final int[] yCoordinates = codes.getYCoordinates();
+ final InputPointers ips = codes.getInputPointers();
+ final int[] xCoordinates = ips.getXCoordinates();
+ final int[] yCoordinates = ips.getYCoordinates();
// Cache the codes so that we don't have to lookup an array list
for (int i = 0; i < mInputLength; i++) {
// TODO: Calculate proximity info here.
@@ -275,16 +291,17 @@ public class ExpandableDictionary extends Dictionary {
mCodes[i] = new int[ProximityInfo.MAX_PROXIMITY_CHARS_SIZE];
}
final int x = xCoordinates != null && i < xCoordinates.length ?
- xCoordinates[i] : WordComposer.NOT_A_COORDINATE;
+ xCoordinates[i] : Constants.NOT_A_COORDINATE;
final int y = xCoordinates != null && i < yCoordinates.length ?
- yCoordinates[i] : WordComposer.NOT_A_COORDINATE;
+ yCoordinates[i] : Constants.NOT_A_COORDINATE;
proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]);
}
mMaxDepth = mInputLength * 3;
- getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback);
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, suggestions);
for (int i = 0; i < mInputLength; i++) {
- getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, callback);
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, suggestions);
}
+ return suggestions;
}
@Override
@@ -368,24 +385,27 @@ public class ExpandableDictionary extends Dictionary {
* @param word the word to insert, as an array of code points
* @param depth the depth of the node in the tree
* @param finalFreq the frequency for this word
+ * @param suggestions the suggestion collection to add the suggestions to
* @return whether there is still space for more words.
- * @see Dictionary.WordCallback#addWord(char[], int, int, int, int, int)
*/
private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth,
- final int finalFreq, final WordCallback callback) {
+ final int finalFreq, final ArrayList<SuggestedWordInfo> suggestions) {
if (finalFreq > 0 && !node.mShortcutOnly) {
- if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, Dictionary.UNIGRAM)) {
- return false;
- }
+ // Use KIND_CORRECTION always. This dictionary does not really have a notion of
+ // COMPLETION against CORRECTION; we could artificially add one by looking at
+ // the respective size of the typed word and the suggestion if it matters sometime
+ // in the future.
+ suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
+ SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
}
if (null != node.mShortcutTargets) {
final int length = node.mShortcutTargets.size();
for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
- if (!callback.addWord(shortcut, 0, shortcut.length, finalFreq, mDicTypeId,
- Dictionary.UNIGRAM)) {
- return false;
- }
+ suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
+ finalFreq, SuggestedWordInfo.KIND_SHORTCUT, mDictType));
+ if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
}
}
return true;
@@ -408,12 +428,12 @@ public class ExpandableDictionary extends Dictionary {
* case we skip over some punctuations such as apostrophe in the traversal. That is, if you type
* "wouldve", it could be matching "would've", so the depth will be one more than the
* inputIndex
- * @param callback the callback class for adding a word
+ * @param suggestions the list in which to add suggestions
*/
// TODO: Share this routine with the native code for BinaryDictionary
protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word,
final int depth, final boolean completion, int snr, int inputIndex, int skipPos,
- WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
final int count = roots.mLength;
final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed.
@@ -443,14 +463,14 @@ public class ExpandableDictionary extends Dictionary {
} else {
finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength);
}
- if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, callback)) {
+ if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, suggestions)) {
// No space left in the queue, bail out
return;
}
}
if (children != null) {
getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex,
- skipPos, callback);
+ skipPos, suggestions);
}
} else if ((c == Keyboard.CODE_SINGLE_QUOTE
&& currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) {
@@ -458,7 +478,7 @@ public class ExpandableDictionary extends Dictionary {
word[depth] = c;
if (children != null) {
getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
- skipPos, callback);
+ skipPos, suggestions);
}
} else {
// Don't use alternatives if we're looking for missing characters
@@ -466,7 +486,7 @@ public class ExpandableDictionary extends Dictionary {
for (int j = 0; j < alternativesSize; j++) {
final int addedAttenuation = (j > 0 ? 1 : 2);
final int currentChar = currentChars[j];
- if (currentChar == KeyDetector.NOT_A_CODE) {
+ if (currentChar == Constants.NOT_A_CODE) {
break;
}
if (currentChar == lowerC || currentChar == c) {
@@ -483,7 +503,7 @@ public class ExpandableDictionary extends Dictionary {
snr * addedAttenuation, mInputLength);
}
if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq,
- callback)) {
+ suggestions)) {
// No space left in the queue, bail out
return;
}
@@ -491,12 +511,12 @@ public class ExpandableDictionary extends Dictionary {
if (children != null) {
getWordsRec(children, codes, word, depth + 1,
true, snr * addedAttenuation, inputIndex + 1,
- skipPos, callback);
+ skipPos, suggestions);
}
} else if (children != null) {
getWordsRec(children, codes, word, depth + 1,
false, snr * addedAttenuation, inputIndex + 1,
- skipPos, callback);
+ skipPos, suggestions);
}
}
}
@@ -514,8 +534,10 @@ public class ExpandableDictionary extends Dictionary {
/**
* Adds bigrams to the in-memory trie structure that is being used to retrieve any word
+ * @param word1 the first word of this bigram
+ * @param word2 the second word of this bigram
* @param frequency frequency for this bigram
- * @param addFrequency if true, it adds to current frequency, else it overwrites the old value
+ * @param fcp an instance of ForgettingCurveParams to use for decay policy
* @return returns the final bigram frequency
*/
private int setBigramAndGetFrequency(
@@ -528,7 +550,7 @@ public class ExpandableDictionary extends Dictionary {
Node secondWord = searchWord(mRoots, word2, 0, null);
LinkedList<NextWord> bigrams = firstWord.mNGrams;
if (bigrams == null || bigrams.size() == 0) {
- firstWord.mNGrams = new LinkedList<NextWord>();
+ firstWord.mNGrams = CollectionUtils.newLinkedList();
bigrams = firstWord.mNGrams;
} else {
for (NextWord nw : bigrams) {
@@ -580,32 +602,14 @@ public class ExpandableDictionary extends Dictionary {
return searchWord(childNode.mChildren, word, depth + 1, childNode);
}
- // @VisibleForTesting
- boolean reloadDictionaryIfRequired() {
- synchronized (mUpdatingLock) {
- // If we need to update, start off a background task
- if (mRequiresReload) startDictionaryLoadingTaskLocked();
- // Currently updating contacts, don't return any results.
- return mUpdatingDictionary;
- }
- }
-
private void runBigramReverseLookUp(final CharSequence previousWord,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
// Search for the lowercase version of the word only, because that's where bigrams
// store their sons.
Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
previousWord.length());
if (prevWord != null && prevWord.mNGrams != null) {
- reverseLookUp(prevWord.mNGrams, callback);
- }
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- if (!reloadDictionaryIfRequired()) {
- runBigramReverseLookUp(previousWord, callback);
+ reverseLookUp(prevWord.mNGrams, suggestions);
}
}
@@ -633,11 +637,12 @@ public class ExpandableDictionary extends Dictionary {
/**
* reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
- * through callback.
+ * to the suggestions list passed as an argument.
* @param terminalNodes list of terminal nodes we want to add
+ * @param suggestions the suggestion collection to add the word to
*/
private void reverseLookUp(LinkedList<NextWord> terminalNodes,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
Node node;
int freq;
for (NextWord nextWord : terminalNodes) {
@@ -648,11 +653,15 @@ public class ExpandableDictionary extends Dictionary {
--index;
mLookedUpString[index] = node.mCode;
node = node.mParent;
- } while (node != null);
-
- if (freq >= 0) {
- callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index,
- freq, mDicTypeId, Dictionary.BIGRAM);
+ } while (node != null && index > 0);
+
+ // If node is null, we have a word longer than MAX_WORD_LENGTH in the dictionary.
+ // It's a little unclear how this can happen, but just in case it does it's safer
+ // to ignore the word in this case.
+ if (freq >= 0 && node == null) {
+ suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
+ BinaryDictionary.MAX_WORD_LENGTH - index),
+ freq, SuggestedWordInfo.KIND_CORRECTION, mDictType));
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java
index b882a4860..1461c0240 100644
--- a/java/src/com/android/inputmethod/latin/ImfUtils.java
+++ b/java/src/com/android/inputmethod/latin/ImfUtils.java
@@ -90,6 +90,13 @@ public class ImfUtils {
return false;
}
+ public static InputMethodSubtype getCurrentInputMethodSubtype(Context context,
+ InputMethodSubtype defaultSubtype) {
+ final InputMethodManager imm = getInputMethodManager(context);
+ final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+ return (currentSubtype != null) ? currentSubtype : defaultSubtype;
+ }
+
public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context,
final boolean shouldIncludeAuxiliarySubtypes) {
final InputMethodManager imm = getInputMethodManager(context);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index 229ae2f3c..7bcda9bc4 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -29,11 +29,12 @@ public class InputAttributes {
final public boolean mInputTypeNoAutoCorrect;
final public boolean mIsSettingsSuggestionStripOn;
final public boolean mApplicationSpecifiedCompletionOn;
- final public int mEditorAction;
+ final private int mInputType;
public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
final int inputType = null != editorInfo ? editorInfo.inputType : 0;
final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
+ mInputType = inputType;
if (inputClass != InputType.TYPE_CLASS_TEXT) {
// If we are not looking at a TYPE_CLASS_TEXT field, the following strange
// cases may arise, so we do a couple sanity checks for them. If it's a
@@ -64,7 +65,7 @@ public class InputAttributes {
final boolean flagAutoComplete =
0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- // Make sure that passwords are not displayed in {@link SuggestionsView}.
+ // Make sure that passwords are not displayed in {@link SuggestionStripView}.
if (InputTypeUtils.isPasswordInputType(inputType)
|| InputTypeUtils.isVisiblePasswordInputType(inputType)
|| InputTypeUtils.isEmailVariation(variation)
@@ -92,8 +93,10 @@ public class InputAttributes {
mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
}
- mEditorAction = (editorInfo == null) ? EditorInfo.IME_ACTION_UNSPECIFIED
- : editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
+ }
+
+ public boolean isSameInputType(final EditorInfo editorInfo) {
+ return editorInfo.inputType == mInputType;
}
@SuppressWarnings("unused")
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
new file mode 100644
index 000000000..ff2feb51d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+// TODO: This class is not thread-safe.
+public class InputPointers {
+ private final int mDefaultCapacity;
+ private final ResizableIntArray mXCoordinates;
+ private final ResizableIntArray mYCoordinates;
+ private final ResizableIntArray mPointerIds;
+ private final ResizableIntArray mTimes;
+
+ public InputPointers(int defaultCapacity) {
+ mDefaultCapacity = defaultCapacity;
+ mXCoordinates = new ResizableIntArray(defaultCapacity);
+ mYCoordinates = new ResizableIntArray(defaultCapacity);
+ mPointerIds = new ResizableIntArray(defaultCapacity);
+ mTimes = new ResizableIntArray(defaultCapacity);
+ }
+
+ public void addPointer(int index, int x, int y, int pointerId, int time) {
+ mXCoordinates.add(index, x);
+ mYCoordinates.add(index, y);
+ mPointerIds.add(index, pointerId);
+ mTimes.add(index, time);
+ }
+
+ public void addPointer(int x, int y, int pointerId, int time) {
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ mPointerIds.add(pointerId);
+ mTimes.add(time);
+ }
+
+ public void set(InputPointers ip) {
+ mXCoordinates.set(ip.mXCoordinates);
+ mYCoordinates.set(ip.mYCoordinates);
+ mPointerIds.set(ip.mPointerIds);
+ mTimes.set(ip.mTimes);
+ }
+
+ public void copy(InputPointers ip) {
+ mXCoordinates.copy(ip.mXCoordinates);
+ mYCoordinates.copy(ip.mYCoordinates);
+ mPointerIds.copy(ip.mPointerIds);
+ mTimes.copy(ip.mTimes);
+ }
+
+ /**
+ * Append the pointers in the specified {@link InputPointers} to the end of this.
+ * @param src the source {@link InputPointers} to read the data from.
+ * @param startPos the starting index of the pointers in {@code src}.
+ * @param length the number of pointers to be appended.
+ */
+ public void append(InputPointers src, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
+ mXCoordinates.append(src.mXCoordinates, startPos, length);
+ mYCoordinates.append(src.mYCoordinates, startPos, length);
+ mPointerIds.append(src.mPointerIds, startPos, length);
+ mTimes.append(src.mTimes, startPos, length);
+ }
+
+ /**
+ * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray}
+ * to the end of this.
+ * @param pointerId the pointer id of the source.
+ * @param times the source {@link ResizableIntArray} to read the event times from.
+ * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from.
+ * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from.
+ * @param startPos the starting index of the data in {@code times} and etc.
+ * @param length the number of data to be appended.
+ */
+ public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates,
+ ResizableIntArray yCoordinates, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
+ mXCoordinates.append(xCoordinates, startPos, length);
+ mYCoordinates.append(yCoordinates, startPos, length);
+ mPointerIds.fill(pointerId, mPointerIds.getLength(), length);
+ mTimes.append(times, startPos, length);
+ }
+
+ public void reset() {
+ final int defaultCapacity = mDefaultCapacity;
+ mXCoordinates.reset(defaultCapacity);
+ mYCoordinates.reset(defaultCapacity);
+ mPointerIds.reset(defaultCapacity);
+ mTimes.reset(defaultCapacity);
+ }
+
+ public int getPointerSize() {
+ return mXCoordinates.getLength();
+ }
+
+ public int[] getXCoordinates() {
+ return mXCoordinates.getPrimitiveArray();
+ }
+
+ public int[] getYCoordinates() {
+ return mYCoordinates.getPrimitiveArray();
+ }
+
+ public int[] getPointerIds() {
+ return mPointerIds.getPrimitiveArray();
+ }
+
+ public int[] getTimes() {
+ return mTimes.getPrimitiveArray();
+ }
+
+ @Override
+ public String toString() {
+ return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes
+ + " x=" + mXCoordinates + " y=" + mYCoordinates;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index 0dcb811b5..c15f45345 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -24,7 +24,7 @@ import android.view.View;
import android.widget.LinearLayout;
public class InputView extends LinearLayout {
- private View mSuggestionsContainer;
+ private View mSuggestionStripContainer;
private View mKeyboardView;
private int mKeyboardTopPadding;
@@ -43,13 +43,13 @@ public class InputView extends LinearLayout {
@Override
protected void onFinishInflate() {
- mSuggestionsContainer = findViewById(R.id.suggestions_container);
+ mSuggestionStripContainer = findViewById(R.id.suggestions_container);
mKeyboardView = findViewById(R.id.keyboard_view);
}
@Override
public boolean dispatchTouchEvent(MotionEvent me) {
- if (mSuggestionsContainer.getVisibility() == VISIBLE
+ if (mSuggestionStripContainer.getVisibility() == VISIBLE
&& mKeyboardView.getVisibility() == VISIBLE
&& forwardTouchEvent(me)) {
return true;
@@ -57,7 +57,8 @@ public class InputView extends LinearLayout {
return super.dispatchTouchEvent(me);
}
- // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView.
+ // The touch events that hit the top padding of keyboard should be forwarded to
+ // {@link SuggestionStripView}.
private boolean forwardTouchEvent(MotionEvent me) {
final Rect rect = mInputViewRect;
this.getGlobalVisibleRect(rect);
@@ -96,7 +97,7 @@ public class InputView extends LinearLayout {
}
final Rect receivingRect = mEventReceivingRect;
- mSuggestionsContainer.getGlobalVisibleRect(receivingRect);
+ mSuggestionStripContainer.getGlobalVisibleRect(receivingRect);
final int translatedX = x - receivingRect.left;
final int translatedY;
if (y < forwardingLimitY) {
@@ -106,7 +107,7 @@ public class InputView extends LinearLayout {
translatedY = y - receivingRect.top;
}
me.setLocation(translatedX, translatedY);
- mSuggestionsContainer.dispatchTouchEvent(me);
+ mSuggestionStripContainer.dispatchTouchEvent(me);
return true;
}
}
diff --git a/java/src/com/android/inputmethod/latin/JniUtils.java b/java/src/com/android/inputmethod/latin/JniUtils.java
index 4808b867a..86a3826d8 100644
--- a/java/src/com/android/inputmethod/latin/JniUtils.java
+++ b/java/src/com/android/inputmethod/latin/JniUtils.java
@@ -31,11 +31,7 @@ public class JniUtils {
try {
System.loadLibrary(JniLibName.JNI_LIB_NAME);
} catch (UnsatisfiedLinkError ule) {
- Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME);
- if (LatinImeLogger.sDBG) {
- throw new RuntimeException(
- "Could not load native library " + JniLibName.JNI_LIB_NAME);
- }
+ Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 4e1f5fe92..bb39ce4f7 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -41,26 +41,26 @@ public class LastComposedWord {
public static final int NOT_A_SEPARATOR = -1;
public final int[] mPrimaryKeyCodes;
- public final int[] mXCoordinates;
- public final int[] mYCoordinates;
public final String mTypedWord;
public final String mCommittedWord;
public final int mSeparatorCode;
public final CharSequence mPrevWord;
+ public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH);
private boolean mActive;
public static final LastComposedWord NOT_A_COMPOSED_WORD =
- new LastComposedWord(null, null, null, "", "", NOT_A_SEPARATOR, null);
+ new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null);
// Warning: this is using the passed objects as is and fully expects them to be
// immutable. Do not fiddle with their contents after you passed them to this constructor.
- public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates,
- final int[] yCoordinates, final String typedWord, final String committedWord,
+ public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers,
+ final String typedWord, final String committedWord,
final int separatorCode, final CharSequence prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
- mXCoordinates = xCoordinates;
- mYCoordinates = yCoordinates;
+ if (inputPointers != null) {
+ mInputPointers.copy(inputPointers);
+ }
mTypedWord = typedWord;
mCommittedWord = committedWord;
mSeparatorCode = separatorCode;
@@ -73,10 +73,10 @@ public class LastComposedWord {
}
public boolean canRevertCommit() {
- return mActive && !TextUtils.isEmpty(mCommittedWord);
+ return mActive && !TextUtils.isEmpty(mCommittedWord) && !didCommitTypedWord();
}
- public boolean didCommitTypedWord() {
+ private boolean didCommitTypedWord() {
return TextUtils.equals(mTypedWord, mCommittedWord);
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 97e898af9..df200cd0e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -20,6 +20,7 @@ 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;
@@ -38,7 +39,6 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
@@ -48,31 +48,31 @@ import android.util.Printer;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.CompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethod.latin.suggestions.SuggestionsView;
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+import com.android.inputmethod.research.ResearchLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -83,7 +83,8 @@ import java.util.Locale;
* Input method implementation for Qwerty'ish keyboard.
*/
public class LatinIME extends InputMethodService implements KeyboardActionListener,
- SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
+ SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
+ Suggest.SuggestInitializationListener {
private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean TRACE = false;
private static boolean DEBUG;
@@ -103,27 +104,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
private static final String SCHEME_PACKAGE = "package";
- /** Whether to use the binary version of the contacts dictionary */
- public static final boolean USE_BINARY_CONTACTS_DICTIONARY = true;
-
- /** Whether to use the binary version of the user dictionary */
- public static final boolean USE_BINARY_USER_DICTIONARY = true;
-
- // TODO: migrate this to SettingsValues
- private int mSuggestionVisibility;
- private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
- = R.string.prefs_suggestion_visibility_show_value;
- private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- = R.string.prefs_suggestion_visibility_show_only_portrait_value;
- private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
- = R.string.prefs_suggestion_visibility_hide_value;
-
- private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
- SUGGESTION_VISIBILILTY_SHOW_VALUE,
- SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
- SUGGESTION_VISIBILILTY_HIDE_VALUE
- };
-
private static final int SPACE_STATE_NONE = 0;
// Double space: the state where the user pressed space twice quickly, which LatinIME
// resolved as period-space. Undoing this converts the period to a space.
@@ -143,13 +123,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Current space state of the input method. This can be any of the above constants.
private int mSpaceState;
- private SettingsValues mSettingsValues;
- private InputAttributes mInputAttributes;
+ private SettingsValues mCurrentSettings;
private View mExtractArea;
private View mKeyPreviewBackingView;
private View mSuggestionsContainer;
- private SuggestionsView mSuggestionsView;
+ private SuggestionStripView mSuggestionStripView;
/* package for tests */ Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private ApplicationInfo mTargetApplicationInfo;
@@ -162,15 +141,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mShouldSwitchToLastSubtype = true;
private boolean mIsMainDictionaryAvailable;
- // TODO: revert this back to the concrete class after transition.
- private Dictionary mUserDictionary;
+ private UserBinaryDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary;
private boolean mIsUserDictionaryAvailable;
private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
private WordComposer mWordComposer = new WordComposer();
-
- private int mCorrectionMode;
+ private RichInputConnection mConnection = new RichInputConnection(this);
// Keep track of the last selection range to decide if we need to show word alternatives
private static final int NOT_A_CURSOR_POSITION = -1;
@@ -199,18 +176,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private AlertDialog mOptionsDialog;
+ private final boolean mIsHardwareAcceleratedDrawingEnabled;
+
public final UIHandler mHandler = new UIHandler(this);
public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
- private static final int MSG_UPDATE_SHIFT_STATE = 1;
- private static final int MSG_SPACE_TYPED = 4;
- private static final int MSG_SET_BIGRAM_PREDICTIONS = 5;
- private static final int MSG_PENDING_IMS_CALLBACK = 6;
- private static final int MSG_UPDATE_SUGGESTIONS = 7;
+ private static final int MSG_UPDATE_SHIFT_STATE = 0;
+ private static final int MSG_PENDING_IMS_CALLBACK = 1;
+ private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
private long mDoubleSpacesTurnIntoPeriodTimeout;
+ private long mDoubleSpaceTimerStart;
public UIHandler(LatinIME outerInstance) {
super(outerInstance);
@@ -231,29 +209,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final LatinIME latinIme = getOuterInstance();
final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
switch (msg.what) {
- case MSG_UPDATE_SUGGESTIONS:
- latinIme.updateSuggestions();
+ case MSG_UPDATE_SUGGESTION_STRIP:
+ latinIme.updateSuggestionStrip();
break;
case MSG_UPDATE_SHIFT_STATE:
switcher.updateShiftState();
break;
- case MSG_SET_BIGRAM_PREDICTIONS:
- latinIme.updateBigramPredictions();
- break;
}
}
- public void postUpdateSuggestions() {
- removeMessages(MSG_UPDATE_SUGGESTIONS);
- sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
+ public void postUpdateSuggestionStrip() {
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions);
}
- public void cancelUpdateSuggestions() {
- removeMessages(MSG_UPDATE_SUGGESTIONS);
+ public void cancelUpdateSuggestionStrip() {
+ removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
}
public boolean hasPendingUpdateSuggestions() {
- return hasMessages(MSG_UPDATE_SUGGESTIONS);
+ return hasMessages(MSG_UPDATE_SUGGESTION_STRIP);
}
public void postUpdateShiftState() {
@@ -265,26 +239,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
removeMessages(MSG_UPDATE_SHIFT_STATE);
}
- public void postUpdateBigramPredictions() {
- removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
- sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
- }
-
- public void cancelUpdateBigramPredictions() {
- removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
- }
-
public void startDoubleSpacesTimer() {
- removeMessages(MSG_SPACE_TYPED);
- sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
+ mDoubleSpaceTimerStart = SystemClock.uptimeMillis();
}
public void cancelDoubleSpacesTimer() {
- removeMessages(MSG_SPACE_TYPED);
+ mDoubleSpaceTimerStart = 0;
}
public boolean isAcceptingDoubleSpaces() {
- return hasMessages(MSG_SPACE_TYPED);
+ return SystemClock.uptimeMillis() - mDoubleSpaceTimerStart
+ < mDoubleSpacesTurnIntoPeriodTimeout;
}
// Working variables for the following methods.
@@ -385,6 +350,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
super();
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+ mIsHardwareAcceleratedDrawingEnabled =
+ InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
+ Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
}
@Override
@@ -393,7 +361,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mPrefs = prefs;
LatinImeLogger.init(this, prefs);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.init(this, prefs);
+ ResearchLogger.getInstance().init(this, prefs);
}
InputMethodManagerCompatWrapper.init(this);
SubtypeSwitcher.init(this);
@@ -411,22 +379,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
loadSettings();
- ImfUtils.setAdditionalInputMethodSubtypes(this, mSettingsValues.getAdditionalSubtypes());
+ ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
- // TODO: remove the following when it's not needed by updateCorrectionMode() any more
- mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
- updateCorrectionMode();
-
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- initSuggest();
- tryGC = false;
- } catch (OutOfMemoryError e) {
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
- }
- }
+ initSuggest();
mDisplayOrientation = res.getConfiguration().orientation;
@@ -450,46 +405,58 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// Has to be package-visible for unit tests
- /* package */ void loadSettings() {
+ /* package for test */
+ void loadSettings() {
// 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.
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ final InputAttributes inputAttributes =
+ new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
@Override
protected SettingsValues job(Resources res) {
- return new SettingsValues(mPrefs, LatinIME.this);
+ return new SettingsValues(mPrefs, inputAttributes, LatinIME.this);
}
};
- mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
- mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues);
+ mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
+ mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings);
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
+ // Note that this method is called from a non-UI thread.
+ @Override
+ public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable) {
+ mIsMainDictionaryAvailable = isMainDictionaryAvailable;
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
+ }
+ }
+
private void initSuggest() {
final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
final String localeStr = subtypeLocale.toString();
- final Dictionary oldContactsDictionary;
+ final ContactsBinaryDictionary oldContactsDictionary;
if (mSuggest != null) {
oldContactsDictionary = mSuggest.getContactsDictionary();
mSuggest.close();
} else {
oldContactsDictionary = null;
}
- mSuggest = new Suggest(this, subtypeLocale);
- if (mSettingsValues.mAutoCorrectEnabled) {
- mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ mSuggest = new Suggest(this /* Context */, subtypeLocale,
+ this /* SuggestInitializationListener */);
+ if (mCurrentSettings.mCorrectionEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
}
mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
-
- if (USE_BINARY_USER_DICTIONARY) {
- mUserDictionary = new UserBinaryDictionary(this, localeStr);
- mIsUserDictionaryAvailable = ((UserBinaryDictionary)mUserDictionary).isEnabled();
- } else {
- mUserDictionary = new UserDictionary(this, localeStr);
- mIsUserDictionaryAvailable = ((UserDictionary)mUserDictionary).isEnabled();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().initSuggest(mSuggest);
}
+
+ mUserDictionary = new UserBinaryDictionary(this, localeStr);
+ mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
mSuggest.setUserDictionary(mUserDictionary);
resetContactsDictionary(oldContactsDictionary);
@@ -497,44 +464,43 @@ 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.
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- mUserHistoryDictionary = UserHistoryDictionary.getInstance(
- this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs);
+ mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs);
mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
}
/**
* Resets the contacts dictionary in mSuggest according to the user settings.
*
- * This method takes an optional contacts dictionary to use. Since the contacts dictionary
- * does not depend on the locale, it can be reused across different instances of Suggest.
- * The dictionary will also be opened or closed as necessary depending on the settings.
+ * This method takes an optional contacts dictionary to use when the locale hasn't changed
+ * since the contacts dictionary can be opened or closed as necessary depending on the settings.
*
* @param oldContactsDictionary an optional dictionary to use, or null
*/
- private void resetContactsDictionary(final Dictionary oldContactsDictionary) {
- final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
+ private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
+ final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict);
- final Dictionary dictionaryToUse;
+ final ContactsBinaryDictionary dictionaryToUse;
if (!shouldSetDictionary) {
// Make sure the dictionary is closed. If it is already closed, this is a no-op,
// so it's safe to call it anyways.
if (null != oldContactsDictionary) oldContactsDictionary.close();
dictionaryToUse = null;
- } else if (null != oldContactsDictionary) {
- // Make sure the old contacts dictionary is opened. If it is already open, this is a
- // no-op, so it's safe to call it anyways.
- if (USE_BINARY_CONTACTS_DICTIONARY) {
- ((ContactsBinaryDictionary)oldContactsDictionary).reopen(this);
- } else {
- ((ContactsDictionary)oldContactsDictionary).reopen(this);
- }
- dictionaryToUse = oldContactsDictionary;
} else {
- if (USE_BINARY_CONTACTS_DICTIONARY) {
- dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS,
- mSubtypeSwitcher.getCurrentSubtypeLocale());
+ final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ if (null != oldContactsDictionary) {
+ if (!oldContactsDictionary.mLocale.equals(locale)) {
+ // If the locale has changed then recreate the contacts dictionary. This
+ // allows locale dependent rules for handling bigram name predictions.
+ oldContactsDictionary.close();
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
+ } else {
+ // Make sure the old contacts dictionary is opened. If it is already open,
+ // this is a no-op, so it's safe to call it anyways.
+ oldContactsDictionary.reopen(this);
+ dictionaryToUse = oldContactsDictionary;
+ }
} else {
- dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
}
}
@@ -545,7 +511,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
/* package private */ void resetSuggestMainDict() {
final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
- mSuggest.resetMainDict(this, subtypeLocale);
+ mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */);
mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
}
@@ -564,23 +530,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onConfigurationChanged(Configuration conf) {
- mSubtypeSwitcher.onConfigurationChanged(conf);
+ // System locale has been changed. Needs to reload keyboard.
+ if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) {
+ loadKeyboard();
+ }
// If orientation changed while predicting, commit the change
if (mDisplayOrientation != conf.orientation) {
mDisplayOrientation = conf.orientation;
mHandler.startOrientationChanging();
- final InputConnection ic = getCurrentInputConnection();
- commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
- if (ic != null) ic.finishComposingText(); // For voice input
- if (isShowingOptionDialog())
+ mConnection.beginBatchEdit();
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mConnection.finishComposingText();
+ mConnection.endBatchEdit();
+ if (isShowingOptionDialog()) {
mOptionsDialog.dismiss();
+ }
}
super.onConfigurationChanged(conf);
}
@Override
public View onCreateInputView() {
- return mKeyboardSwitcher.onCreateInputView();
+ return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled);
}
@Override
@@ -590,9 +561,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
.findViewById(android.R.id.extractArea);
mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
- mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
- if (mSuggestionsView != null)
- mSuggestionsView.setListener(this, view);
+ mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
+ if (mSuggestionStripView != null)
+ mSuggestionStripView.setListener(this, view);
if (LatinImeLogger.sVISUALDEBUG) {
mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
}
@@ -629,6 +600,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
mSubtypeSwitcher.updateSubtype(subtype);
+ loadKeyboard();
}
private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
@@ -639,7 +611,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
super.onStartInputView(editorInfo, restarting);
final KeyboardSwitcher switcher = mKeyboardSwitcher;
- LatinKeyboardView inputView = switcher.getKeyboardView();
+ final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
if (editorInfo == null) {
Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -682,84 +654,130 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
LatinImeLogger.onStartInputView(editorInfo);
// In landscape mode, this method gets called without the input view being created.
- if (inputView == null) {
+ if (mainKeyboardView == null) {
return;
}
// Forward this event to the accessibility utilities, if enabled.
final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
if (accessUtils.isTouchExplorationEnabled()) {
- accessUtils.onStartInputViewInternal(editorInfo, restarting);
+ accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
}
- mSubtypeSwitcher.updateParametersOnStartInputView();
+ final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart
+ || mLastSelectionEnd != editorInfo.initialSelEnd;
+ final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo);
+ final boolean isDifferentTextField = !restarting || inputTypeChanged;
+ if (isDifferentTextField) {
+ final boolean currentSubtypeEnabled = mSubtypeSwitcher
+ .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
+ if (!currentSubtypeEnabled) {
+ // Current subtype is disabled. Needs to update subtype and keyboard.
+ final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype(
+ this, mSubtypeSwitcher.getNoLanguageSubtype());
+ mSubtypeSwitcher.updateSubtype(newSubtype);
+ loadKeyboard();
+ }
+ }
// The EditorInfo might have a flag that affects fullscreen mode.
// Note: This call should be done by InputMethodService?
updateFullscreenMode();
- mLastSelectionStart = editorInfo.initialSelStart;
- mLastSelectionEnd = editorInfo.initialSelEnd;
- mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
mApplicationSpecifiedCompletions = null;
- inputView.closing();
- mEnteredText = null;
- resetComposingState(true /* alsoResetLastComposedWord */);
- mDeleteCount = 0;
- mSpaceState = SPACE_STATE_NONE;
-
- loadSettings();
- updateCorrectionMode();
- updateSuggestionVisibility(mResources);
+ if (isDifferentTextField || selectionChanged) {
+ // If the selection changed, we reset the input state. Essentially, we come here with
+ // restarting == true when the app called setText() or similar. We should reset the
+ // state if the app set the text to something else, but keep it if it set a suggestion
+ // or something.
+ mEnteredText = null;
+ resetComposingState(true /* alsoResetLastComposedWord */);
+ mDeleteCount = 0;
+ mSpaceState = SPACE_STATE_NONE;
- if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
- mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.clear();
+ }
}
- switcher.loadKeyboard(editorInfo, mSettingsValues);
+ if (isDifferentTextField) {
+ mainKeyboardView.closing();
+ loadSettings();
+
+ if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
+ }
- if (mSuggestionsView != null)
- mSuggestionsView.clear();
+ switcher.loadKeyboard(editorInfo, mCurrentSettings);
+ }
setSuggestionStripShownInternal(
isSuggestionsStripVisible(), /* needsInputViewShown */ false);
- // Delay updating suggestions because keyboard input view may not be shown at this point.
- mHandler.postUpdateSuggestions();
+
+ mLastSelectionStart = editorInfo.initialSelStart;
+ mLastSelectionEnd = editorInfo.initialSelEnd;
+ // If we come here something in the text state is very likely to have changed.
+ // We should update the shift state regardless of whether we are restarting or not, because
+ // this is not perceived as a layout change that may be disruptive like we may have with
+ // switcher.loadKeyboard; in apps like Talk, we come here when the text is sent and the
+ // field gets emptied and we need to re-evaluate the shift state, but not the whole layout
+ // which would be disruptive.
+ mKeyboardSwitcher.updateShiftState();
+
+ mHandler.cancelUpdateSuggestionStrip();
mHandler.cancelDoubleSpacesTimer();
- inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
- mSettingsValues.mKeyPreviewPopupDismissDelay);
- inputView.setProximityCorrectionEnabled(true);
+ mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
+ mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
+ mCurrentSettings.mKeyPreviewPopupDismissDelay);
+ mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled);
+ mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled,
+ mCurrentSettings.mGestureFloatingPreviewTextEnabled);
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
}
+ // Callback for the TargetApplicationGetter
+ @Override
public void onTargetApplicationKnown(final ApplicationInfo info) {
mTargetApplicationInfo = info;
}
@Override
public void onWindowHidden() {
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
+ getCurrentInputConnection());
+ }
super.onWindowHidden();
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.closing();
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
private void onFinishInputInternal() {
super.onFinishInput();
LatinImeLogger.commit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().latinIME_onFinishInputInternal();
+ }
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.closing();
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
private void onFinishInputViewInternal(boolean finishingInput) {
super.onFinishInputView(finishingInput);
mKeyboardSwitcher.onFinishInputView();
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.cancelAllMessages();
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.cancelAllMessages();
+ }
// Remove pending messages related to update suggestions
- mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateSuggestionStrip();
}
@Override
@@ -768,7 +786,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
int composingSpanStart, int composingSpanEnd) {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
composingSpanStart, composingSpanEnd);
-
if (DEBUG) {
Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
+ ", ose=" + oldSelEnd
@@ -780,9 +797,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", ce=" + composingSpanEnd);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
+ final boolean expectingUpdateSelectionFromLogger =
+ ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
- composingSpanEnd);
+ composingSpanEnd, mExpectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, mConnection);
+ if (expectingUpdateSelectionFromLogger) {
+ // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work
+ return;
+ }
}
// TODO: refactor the following code to be less contrived.
@@ -841,7 +865,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
@Override
public void onExtractedTextClicked() {
- if (isSuggestionsRequested()) return;
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
super.onExtractedTextClicked();
}
@@ -857,7 +881,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
@Override
public void onExtractedCursorMovement(int dx, int dy) {
- if (isSuggestionsRequested()) return;
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
super.onExtractedCursorMovement(dx, dy);
}
@@ -885,43 +909,45 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
- }
- if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
- mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
- if (applicationSpecifiedCompletions == null) {
- clearSuggestions();
- return;
+ if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
+ mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
+ if (applicationSpecifiedCompletions == null) {
+ clearSuggestionStrip();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onDisplayCompletions(null);
}
+ return;
+ }
- final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
- SuggestedWords.getFromApplicationSpecifiedCompletions(
- applicationSpecifiedCompletions);
- final SuggestedWords suggestedWords = new SuggestedWords(
- applicationSuggestedWords,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
- false /* isPunctuationSuggestions */,
- false /* isObsoleteSuggestions */,
- false /* isPrediction */);
- // When in fullscreen mode, show completions generated by the application
- final boolean isAutoCorrection = false;
- setSuggestions(suggestedWords, isAutoCorrection);
- setAutoCorrectionIndicator(isAutoCorrection);
- // TODO: is this the right thing to do? What should we auto-correct to in
- // this case? This says to keep whatever the user typed.
- mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
- setSuggestionStripShown(true);
+ final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
+ SuggestedWords.getFromApplicationSpecifiedCompletions(
+ applicationSpecifiedCompletions);
+ final SuggestedWords suggestedWords = new SuggestedWords(
+ applicationSuggestedWords,
+ false /* typedWordValid */,
+ false /* hasAutoCorrectionCandidate */,
+ false /* isPunctuationSuggestions */,
+ false /* isObsoleteSuggestions */,
+ false /* isPrediction */);
+ // When in fullscreen mode, show completions generated by the application
+ final boolean isAutoCorrection = false;
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
+ setAutoCorrectionIndicator(isAutoCorrection);
+ // TODO: is this the right thing to do? What should we auto-correct to in
+ // this case? This says to keep whatever the user typed.
+ mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
+ setSuggestionStripShown(true);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
}
}
private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
// TODO: Modify this if we support suggestions with hard keyboard
if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ final boolean inputViewShown = (mainKeyboardView != null)
+ ? mainKeyboardView.isShown() : false;
final boolean shouldShowSuggestions = shown
&& (needsInputViewShown ? inputViewShown : true);
if (isFullscreenMode()) {
@@ -944,11 +970,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return currentHeight;
}
- final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- if (keyboardView == null) {
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView == null) {
return 0;
}
- final int keyboardHeight = keyboardView.getHeight();
+ final int keyboardHeight = mainKeyboardView.getHeight();
final int suggestionsHeight = mSuggestionsContainer.getHeight();
final int displayHeight = mResources.getDisplayMetrics().heightPixels;
final Rect rect = new Rect();
@@ -958,7 +984,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
- keyboardHeight;
final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
- params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight);
+ params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
mKeyPreviewBackingView.setLayoutParams(params);
return params.height;
}
@@ -966,9 +992,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onComputeInsets(InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
- final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView == null || mSuggestionsContainer == null)
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView == null || mSuggestionsContainer == null) {
return;
+ }
final int adjustedBackingHeight = getAdjustedBackingViewHeight();
final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
@@ -981,13 +1008,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
int touchY = extraHeight;
// Need to set touchable region only if input view is being shown
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- if (keyboardView != null && keyboardView.isShown()) {
+ if (mainKeyboardView.isShown()) {
if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
touchY -= suggestionsHeight;
}
- final int touchWidth = inputView.getWidth();
- final int touchHeight = inputView.getHeight() + extraHeight
+ final int touchWidth = mainKeyboardView.getWidth();
+ final int touchHeight = mainKeyboardView.getHeight() + extraHeight
// Extend touchable region below the keyboard.
+ EXTENDED_TOUCHABLE_REGION_HEIGHT;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
@@ -1001,7 +1027,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public boolean onEvaluateFullscreenMode() {
// Reread resource value here, because this method is called by framework anytime as needed.
final boolean isFullscreenModeAllowed =
- mSettingsValues.isFullscreenModeAllowed(getResources());
+ mCurrentSettings.isFullscreenModeAllowed(getResources());
return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
}
@@ -1016,15 +1042,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// This will reset the whole input state to the starting state. It will clear
- // the composing word, reset the last composed word, tell the inputconnection
- // and the composingStateManager about it.
+ // the composing word, reset the last composed word, tell the inputconnection about it.
private void resetEntireInputState() {
resetComposingState(true /* alsoResetLastComposedWord */);
- updateSuggestions();
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.finishComposingText();
- }
+ clearSuggestionStrip();
+ mConnection.finishComposingText();
}
private void resetComposingState(final boolean alsoResetLastComposedWord) {
@@ -1033,26 +1055,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
- public void commitTyped(final InputConnection ic, final int separatorCode) {
+ private void commitTyped(final int separatorCode) {
if (!mWordComposer.isComposingWord()) return;
final CharSequence typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
- if (ic != null) {
- ic.commitText(typedWord, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(typedWord);
- }
- }
+ mConnection.commitText(typedWord, 1);
final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
mLastComposedWord = mWordComposer.commitWord(
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
separatorCode, prevWord);
}
- updateSuggestions();
}
+ // Called from the KeyboardSwitcher which needs to know auto caps state to display
+ // the right layout.
public int getCurrentAutoCapsState() {
- if (!mSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
+ if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
final EditorInfo ei = getCurrentInputEditorInfo();
if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
@@ -1070,49 +1088,50 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// unless needed.
if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF;
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return Constants.TextUtils.CAP_MODE_OFF;
// TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
// Note: getCursorCapsMode() returns the current capitalization mode that is any
// combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
// of them.
- return ic.getCursorCapsMode(inputType);
+ return mConnection.getCursorCapsMode(inputType);
}
- // "ic" may be null
- private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) {
- if (null == ic) return;
- CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+ // Factor in auto-caps and manual caps and compute the current caps mode.
+ private int getActualCapsMode() {
+ final int manual = mKeyboardSwitcher.getManualCapsMode();
+ if (manual != WordComposer.CAPS_MODE_OFF) return manual;
+ final int auto = getCurrentAutoCapsState();
+ if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
+ return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
+ }
+ if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
+ return WordComposer.CAPS_MODE_OFF;
+ }
+
+ private void swapSwapperAndSpace() {
+ CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
// It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
if (lastTwo != null && lastTwo.length() == 2
&& lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
- ic.deleteSurroundingText(2, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(lastTwo.charAt(1) + " ", 1);
+ mConnection.deleteSurroundingText(2, 0);
+ mConnection.commitText(lastTwo.charAt(1) + " ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit();
+ ResearchLogger.latinIME_swapSwapperAndSpace();
}
mKeyboardSwitcher.updateShiftState();
}
}
- private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
- if (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
- if (ic == null) return false;
- final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
+ private boolean maybeDoubleSpace() {
+ if (!mCurrentSettings.mCorrectionEnabled) return false;
+ if (!mHandler.isAcceptingDoubleSpaces()) return false;
+ final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
if (lastThree != null && lastThree.length() == 3
&& canBeFollowedByPeriod(lastThree.charAt(0))
&& lastThree.charAt(1) == Keyboard.CODE_SPACE
- && lastThree.charAt(2) == Keyboard.CODE_SPACE
- && mHandler.isAcceptingDoubleSpaces()) {
+ && lastThree.charAt(2) == Keyboard.CODE_SPACE) {
mHandler.cancelDoubleSpacesTimer();
- ic.deleteSurroundingText(2, 0);
- ic.commitText(". ", 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_doubleSpaceAutoPeriod();
- }
+ mConnection.deleteSurroundingText(2, 0);
+ mConnection.commitText(". ", 1);
mKeyboardSwitcher.updateShiftState();
return true;
}
@@ -1131,29 +1150,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
}
- // "ic" may be null
- private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) {
- if (ic == null) return;
- final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
- if (lastOne != null && lastOne.length() == 1
- && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
- }
- }
-
+ // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
+ // pressed.
@Override
- public boolean addWordToDictionary(String word) {
- if (USE_BINARY_USER_DICTIONARY) {
- ((UserBinaryDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- } else {
- ((UserDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- }
- // Suggestion strip should be updated after the operation of adding word to the
- // user dictionary
- mHandler.postUpdateSuggestions();
+ public boolean addWordToUserDictionary(String word) {
+ mUserDictionary.addWordToUserDictionary(word, 128);
return true;
}
@@ -1193,17 +1194,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void performEditorAction(int actionId) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.performEditorAction(actionId);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_performEditorAction(actionId);
- }
- }
+ mConnection.performEditorAction(actionId);
}
private void handleLanguageSwitchKey() {
- final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList;
+ final boolean includesOtherImes = mCurrentSettings.mIncludesOtherImesInLanguageSwitchList;
final IBinder token = getWindow().getWindow().getAttributes().token;
if (mShouldSwitchToLastSubtype) {
final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
@@ -1221,12 +1216,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- static private void sendUpDownEnterOrBackspace(final int code, final InputConnection ic) {
+ private void sendUpDownEnterOrBackspace(final int code) {
final long eventTime = SystemClock.uptimeMillis();
- ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
- ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
@@ -1236,27 +1231,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (code >= '0' && code <= '9') {
super.sendKeyChar((char)code);
- return;
- }
-
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
- // we want to be able to compile against the Ice Cream Sandwich SDK.
- if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
- && mTargetApplicationInfo.targetSdkVersion < 16) {
- // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
- // a hardware keyboard event on pressing enter or delete. This is bad for many
- // reasons (there are race conditions with commits) but some applications are
- // relying on this behavior so we continue to support it for older apps.
- sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER, ic);
- } else {
- final String text = new String(new int[] { code }, 0, 1);
- ic.commitText(text, text.length());
- }
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_sendKeyCodePoint(code);
}
+ return;
+ }
+
+ // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
+ // we want to be able to compile against the Ice Cream Sandwich SDK.
+ if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
+ && mTargetApplicationInfo.targetSdkVersion < 16) {
+ // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
+ // a hardware keyboard event on pressing enter or delete. This is bad for many
+ // reasons (there are race conditions with commits) but some applications are
+ // relying on this behavior so we continue to support it for older apps.
+ sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER);
+ } else {
+ final String text = new String(new int[] { code }, 0, 1);
+ mConnection.commitText(text, text.length());
}
}
@@ -1268,13 +1260,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mDeleteCount = 0;
}
mLastKeyTime = when;
-
- if (ProductionFlag.IS_EXPERIMENTAL) {
- if (ResearchLogger.sIsLogging) {
- ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y);
- }
- }
-
+ mConnection.beginBatchEdit();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
// The space state depends only on the last character pressed and its own previous
// state. Here, we revert the space state to neutral if the key is actually modifying
@@ -1307,7 +1293,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
onSettingsKeyPressed();
break;
case Keyboard.CODE_SHORTCUT:
- mSubtypeSwitcher.switchToShortcutIME();
+ mSubtypeSwitcher.switchToShortcutIME(this);
break;
case Keyboard.CODE_ACTION_ENTER:
performEditorAction(getActionId(switcher.getKeyboard()));
@@ -1321,23 +1307,29 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case Keyboard.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
- default:
- if (primaryCode == Keyboard.CODE_TAB
- && mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) {
- performEditorAction(EditorInfo.IME_ACTION_NEXT);
- break;
+ case Keyboard.CODE_RESEARCH:
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().presentResearchDialog(this);
}
+ break;
+ default:
mSpaceState = SPACE_STATE_NONE;
- if (mSettingsValues.isWordSeparator(primaryCode)) {
+ if (mCurrentSettings.isWordSeparator(primaryCode)) {
didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
} else {
+ if (SPACE_STATE_PHANTOM == spaceState) {
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ }
+ final int keyX, keyY;
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
- handleCharacter(primaryCode, x, y, spaceState);
+ keyX = x;
+ keyY = y;
} else {
- handleCharacter(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE,
- spaceState);
+ keyX = Constants.NOT_A_COORDINATE;
+ keyY = Constants.NOT_A_COORDINATE;
}
+ handleCharacter(primaryCode, keyX, keyY, spaceState);
}
mExpectingUpdateSelection = true;
mShouldSwitchToLastSubtype = true;
@@ -1349,23 +1341,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
&& primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
mEnteredText = null;
+ mConnection.endBatchEdit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
+ }
}
+ // Called from PointerTracker through the KeyboardActionListener interface
@Override
- public void onTextInput(CharSequence text) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- ic.beginBatchEdit();
- commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
- text = specificTldProcessingOnTextInput(ic, text);
+ public void onTextInput(CharSequence rawText) {
+ mConnection.beginBatchEdit();
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mHandler.postUpdateSuggestionStrip();
+ final CharSequence text = specificTldProcessingOnTextInput(rawText);
if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
- ic.commitText(text, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(text);
- }
- ic.endBatchEdit();
+ mConnection.commitText(text, 1);
+ mConnection.endBatchEdit();
mKeyboardSwitcher.updateShiftState();
mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
mSpaceState = SPACE_STATE_NONE;
@@ -1373,9 +1366,61 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetComposingState(true /* alsoResetLastComposedWord */);
}
- // ic may not be null
- private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
- final CharSequence text) {
+ @Override
+ public void onStartBatchInput() {
+ mConnection.beginBatchEdit();
+ if (mWordComposer.isComposingWord()) {
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mExpectingUpdateSelection = true;
+ // TODO: Can we remove this?
+ mSpaceState = SPACE_STATE_PHANTOM;
+ }
+ mConnection.endBatchEdit();
+ // TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
+ }
+
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {
+ mWordComposer.setBatchInputPointers(batchPointers);
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
+ final String gestureFloatingPreviewText = (suggestedWords.size() > 0)
+ ? suggestedWords.getWord(0) : null;
+ mKeyboardSwitcher.getMainKeyboardView()
+ .showGestureFloatingPreviewText(gestureFloatingPreviewText);
+ }
+
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mWordComposer.setBatchInputPointers(batchPointers);
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
+ final String gestureFloatingPreviewText = (suggestedWords.size() > 0)
+ ? suggestedWords.getWord(0) : null;
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ mainKeyboardView.showGestureFloatingPreviewText(gestureFloatingPreviewText);
+ mainKeyboardView.dismissGestureFloatingPreviewText();
+ if (suggestedWords == null || suggestedWords.size() == 0) {
+ return;
+ }
+ final CharSequence text = suggestedWords.getWord(0);
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+ mWordComposer.setBatchInputWord(text);
+ mConnection.beginBatchEdit();
+ if (SPACE_STATE_PHANTOM == mSpaceState) {
+ sendKeyCodePoint(Keyboard.CODE_SPACE);
+ }
+ mConnection.setComposingText(text, 1);
+ mExpectingUpdateSelection = true;
+ mConnection.endBatchEdit();
+ mKeyboardSwitcher.updateShiftState();
+ mSpaceState = SPACE_STATE_PHANTOM;
+ }
+
+ private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
|| !Character.isLetter(text.charAt(1))) {
// Not a tld: do nothing.
@@ -1384,7 +1429,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We have a TLD (or something that looks like this): make sure we don't add
// a space even if currently in phantom mode.
mSpaceState = SPACE_STATE_NONE;
- final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
+ final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
&& lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
return text.subSequence(1, text.length());
@@ -1393,6 +1438,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
+ // Called from PointerTracker through the KeyboardActionListener interface
@Override
public void onCancelInput() {
// User released a finger outside any key
@@ -1400,27 +1446,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void handleBackspace(final int spaceState) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- ic.beginBatchEdit();
- handleBackspaceWhileInBatchEdit(spaceState, ic);
- ic.endBatchEdit();
- }
-
- // "ic" may not be null.
- private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
// In many cases, we may have to put the keyboard in auto-shift state again.
mHandler.postUpdateShiftState();
- if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
+ if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
// Cancel multi-character input: remove the text we just entered.
// This is triggered on backspace after a key that inputs multiple characters,
// like the smiley key or the .com key.
final int length = mEnteredText.length();
- ic.deleteSurroundingText(length, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(length);
- }
+ mConnection.deleteSurroundingText(length, 0);
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
// reverting any autocorrect at this point. So we can safely return.
@@ -1430,37 +1464,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mWordComposer.isComposingWord()) {
final int length = mWordComposer.size();
if (length > 0) {
- mWordComposer.deleteLast();
- ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
- // If we have deleted the last remaining character of a word, then we are not
- // isComposingWord() any more.
- if (!mWordComposer.isComposingWord()) {
- // Not composing word any more, so we can show bigrams.
- mHandler.postUpdateBigramPredictions();
+ // Immediately after a batch input.
+ if (SPACE_STATE_PHANTOM == spaceState) {
+ mWordComposer.reset();
} else {
- // Still composing a word, so we still have letters to deduce a suggestion from.
- mHandler.postUpdateSuggestions();
+ mWordComposer.deleteLast();
}
+ mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ mHandler.postUpdateSuggestionStrip();
} else {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
+ mConnection.deleteSurroundingText(1, 0);
}
} else {
if (mLastComposedWord.canRevertCommit()) {
Utils.Stats.onAutoCorrectionCancellation();
- revertCommit(ic);
+ revertCommit();
return;
}
if (SPACE_STATE_DOUBLE == spaceState) {
- if (revertDoubleSpaceWhileInBatchEdit(ic)) {
+ mHandler.cancelDoubleSpacesTimer();
+ if (mConnection.revertDoubleSpace()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
return;
}
} else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- if (revertSwapPunctuation(ic)) {
+ if (mConnection.revertSwapPunctuation()) {
// Likewise
return;
}
@@ -1471,11 +1500,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mLastSelectionStart != mLastSelectionEnd) {
// If there is a selection, remove it.
final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
- ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
- ic.deleteSurroundingText(lengthToDelete, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete);
- }
+ mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
+ mConnection.deleteSurroundingText(lengthToDelete, 0);
} else {
// There is no selection, just delete one character.
if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
@@ -1490,40 +1516,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// a hardware keyboard event on pressing enter or delete. This is bad for many
// reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps.
- sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL, ic);
+ sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL);
} else {
- ic.deleteSurroundingText(1, 0);
- }
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
+ mConnection.deleteSurroundingText(1, 0);
}
if (mDeleteCount > DELETE_ACCELERATE_AT) {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
+ mConnection.deleteSurroundingText(1, 0);
}
}
- if (isSuggestionsRequested()) {
- restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+ restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
}
}
}
- // ic may be null
- private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code,
+ private boolean maybeStripSpace(final int code,
final int spaceState, final boolean isFromSuggestionStrip) {
if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ mConnection.removeTrailingSpace();
return false;
} else if ((SPACE_STATE_WEAK == spaceState
|| SPACE_STATE_SWAP_PUNCTUATION == spaceState)
&& isFromSuggestionStrip) {
- if (mSettingsValues.isWeakSpaceSwapper(code)) {
+ if (mCurrentSettings.isWeakSpaceSwapper(code)) {
return true;
} else {
- if (mSettingsValues.isWeakSpaceStripper(code)) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ if (mCurrentSettings.isWeakSpaceStripper(code)) {
+ mConnection.removeTrailingSpace();
}
return false;
}
@@ -1534,20 +1553,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void handleCharacter(final int primaryCode, final int x,
final int y, final int spaceState) {
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) ic.beginBatchEdit();
- // TODO: if ic is null, does it make any sense to call this?
- handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic);
- if (null != ic) ic.endBatchEdit();
- }
-
- // "ic" may be null without this crashing, but the behavior will be really strange
- private void handleCharacterWhileInBatchEdit(final int primaryCode,
- final int x, final int y, final int spaceState, final InputConnection ic) {
boolean isComposingWord = mWordComposer.isComposingWord();
if (SPACE_STATE_PHANTOM == spaceState &&
- !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) {
+ !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) {
if (isComposingWord) {
// Sanity check
throw new RuntimeException("Should not be composing here");
@@ -1559,8 +1568,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
// thread here.
if (!isComposingWord && (isAlphabet(primaryCode)
- || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode))
- && isSuggestionsRequested() && !isCursorTouchingWord()) {
+ || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode))
+ && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) &&
+ !mConnection.isCursorTouchingWord(mCurrentSettings)) {
// Reset entirely the composing state anyway, then start composing a new word unless
// the character is a single quote. The idea here is, single quote is not a
// separator and it should be treated as a normal character, except in the first
@@ -1571,85 +1581,68 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// it entirely and resume suggestions on the previous word, we'd like to still
// have touch coordinates for it.
resetComposingState(false /* alsoResetLastComposedWord */);
- clearSuggestions();
}
if (isComposingWord) {
- mWordComposer.add(
- primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector());
- if (ic != null) {
- // If it's the first letter, make note of auto-caps state
- if (mWordComposer.size() == 1) {
- mWordComposer.setAutoCapitalized(
- getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
- }
- ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ final int keyX, keyY;
+ if (KeyboardActionListener.Adapter.isInvalidCoordinate(x)
+ || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) {
+ keyX = x;
+ keyY = y;
+ } else {
+ final KeyDetector keyDetector =
+ mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
+ keyX = keyDetector.getTouchX(x);
+ keyY = keyDetector.getTouchY(y);
+ }
+ mWordComposer.add(primaryCode, keyX, keyY);
+ // If it's the first letter, make note of auto-caps state
+ if (mWordComposer.size() == 1) {
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
}
- mHandler.postUpdateSuggestions();
+ mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
- spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode,
+ spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
sendKeyCodePoint(primaryCode);
if (swapWeakSpace) {
- swapSwapperAndSpaceWhileInBatchEdit(ic);
+ swapSwapperAndSpace();
mSpaceState = SPACE_STATE_WEAK;
}
- // Some characters are not word separators, yet they don't start a new
- // composing span. For these, we haven't changed the suggestion strip, and
- // if the "add to dictionary" hint is shown, we should do so now. Examples of
- // such characters include single quote, dollar, and others; the exact list is
- // the list of characters for which we enter handleCharacterWhileInBatchEdit
- // that don't match the test if ((isAlphabet...)) at the top of this method.
- if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
- mHandler.postUpdateBigramPredictions();
- }
+ // In case the "add to dictionary" hint was still displayed.
+ if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
}
+ mHandler.postUpdateSuggestionStrip();
Utils.Stats.onNonSeparator((char)primaryCode, x, y);
}
// Returns true if we did an autocorrection, false otherwise.
private boolean handleSeparator(final int primaryCode, final int x, final int y,
final int spaceState) {
- // Should dismiss the "Touch again to save" message when handling separator
- if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
- mHandler.cancelUpdateBigramPredictions();
- mHandler.postUpdateSuggestions();
- }
-
boolean didAutoCorrect = false;
// Handle separator
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.beginBatchEdit();
- }
if (mWordComposer.isComposingWord()) {
- // In certain languages where single quote is a separator, it's better
- // not to auto correct, but accept the typed word. For instance,
- // in Italian dov' should not be expanded to dove' because the elision
- // requires the last vowel to be removed.
- final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
- && !mInputAttributes.mInputTypeNoAutoCorrect;
- if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
- commitCurrentAutoCorrection(primaryCode, ic);
+ if (mCurrentSettings.mCorrectionEnabled) {
+ commitCurrentAutoCorrection(primaryCode);
didAutoCorrect = true;
} else {
- commitTyped(ic, primaryCode);
+ commitTyped(primaryCode);
}
}
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
+ Constants.SUGGESTION_STRIP_COORDINATE == x);
if (SPACE_STATE_PHANTOM == spaceState &&
- mSettingsValues.isPhantomSpacePromotingSymbol(primaryCode)) {
+ mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
sendKeyCodePoint(primaryCode);
if (Keyboard.CODE_SPACE == primaryCode) {
- if (isSuggestionsRequested()) {
- if (maybeDoubleSpaceWhileInBatchEdit(ic)) {
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+ if (maybeDoubleSpace()) {
mSpaceState = SPACE_STATE_DOUBLE;
} else if (!isShowingPunctuationList()) {
mSpaceState = SPACE_STATE_WEAK;
@@ -1657,21 +1650,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mHandler.startDoubleSpacesTimer();
- if (!isCursorTouchingWord()) {
- mHandler.cancelUpdateSuggestions();
- mHandler.postUpdateBigramPredictions();
+ if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
+ mHandler.postUpdateSuggestionStrip();
}
} else {
if (swapWeakSpace) {
- swapSwapperAndSpaceWhileInBatchEdit(ic);
+ swapSwapperAndSpace();
mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
- } else if (SPACE_STATE_PHANTOM == spaceState) {
+ } else if (SPACE_STATE_PHANTOM == spaceState
+ && !mCurrentSettings.isWeakSpaceStripper(primaryCode)) {
// If we are in phantom space state, and the user presses a separator, we want to
// stay in phantom space state so that the next keypress has a chance to add the
// space. For example, if I type "Good dat", pick "day" from the suggestion strip
// then insert a comma and go on to typing the next word, I want the space to be
// inserted automatically before the next word, the same way it is when I don't
// input the comma.
+ // The case is a little different if the separator is a space stripper. Such a
+ // separator does not normally need a space on the right (that's the difference
+ // between swappers and strippers), so we should not stay in phantom space state if
+ // the separator is a stripper. Hence the additional test above.
mSpaceState = SPACE_STATE_PHANTOM;
}
@@ -1682,9 +1679,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
Utils.Stats.onSeparator((char)primaryCode, x, y);
- if (ic != null) {
- ic.endBatchEdit();
- }
+ mHandler.postUpdateShiftState();
return didAutoCorrect;
}
@@ -1695,153 +1690,134 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void handleClose() {
- commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR);
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
requestHideSelf(0);
- LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null)
- inputView.closing();
- }
-
- public boolean isSuggestionsRequested() {
- return mInputAttributes.mIsSettingsSuggestionStripOn
- && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
- }
-
- public boolean isShowingPunctuationList() {
- if (mSuggestionsView == null) return false;
- return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
- public boolean isShowingSuggestionsStrip() {
- return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
- || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
+ // TODO: make this private
+ // Outside LatinIME, only used by the test suite.
+ /* package for tests */
+ boolean isShowingPunctuationList() {
+ if (mSuggestionStripView == null) return false;
+ return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
}
- public boolean isSuggestionsStripVisible() {
- if (mSuggestionsView == null)
+ private boolean isSuggestionsStripVisible() {
+ if (mSuggestionStripView == null)
return false;
- if (mSuggestionsView.isShowingAddToDictionaryHint())
+ if (mSuggestionStripView.isShowingAddToDictionaryHint())
return true;
- if (!isShowingSuggestionsStrip())
+ if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation))
return false;
- if (mInputAttributes.mApplicationSpecifiedCompletionOn)
+ if (mCurrentSettings.isApplicationSpecifiedCompletionsOn())
return true;
- return isSuggestionsRequested();
+ return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation);
}
- public void switchToKeyboardView() {
- if (DEBUG) {
- Log.d(TAG, "Switch to keyboard view.");
- }
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_switchToKeyboardView();
- }
- View v = mKeyboardSwitcher.getKeyboardView();
- if (v != null) {
- // Confirms that the keyboard view doesn't have parent view.
- ViewParent p = v.getParent();
- if (p != null && p instanceof ViewGroup) {
- ((ViewGroup) p).removeView(v);
- }
- setInputView(v);
- }
- setSuggestionStripShown(isSuggestionsStripVisible());
- updateInputViewShown();
- mHandler.postUpdateSuggestions();
- }
-
- public void clearSuggestions() {
- setSuggestions(SuggestedWords.EMPTY, false);
+ private void clearSuggestionStrip() {
+ setSuggestionStrip(SuggestedWords.EMPTY, false);
setAutoCorrectionIndicator(false);
}
- private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) {
- if (mSuggestionsView != null) {
- mSuggestionsView.setSuggestions(words);
+ private void setSuggestionStrip(final SuggestedWords words, final boolean isAutoCorrection) {
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.setSuggestions(words);
mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
}
}
private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
// Put a blue underline to a word in TextView which will be auto-corrected.
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
&& mWordComposer.isComposingWord()) {
mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
final CharSequence textWithUnderline =
getTextWithUnderline(mWordComposer.getTypedWord());
- ic.setComposingText(textWithUnderline, 1);
+ mConnection.setComposingText(textWithUnderline, 1);
}
}
- public void updateSuggestions() {
+ private void updateSuggestionStrip() {
+ mHandler.cancelUpdateSuggestionStrip();
+
// Check if we have a suggestion engine attached.
- if ((mSuggest == null || !isSuggestionsRequested())) {
+ if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
if (mWordComposer.isComposingWord()) {
- Log.w(TAG, "Called updateSuggestions but suggestions were not requested!");
+ Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
+ + "requested!");
mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
}
return;
}
- mHandler.cancelUpdateSuggestions();
- mHandler.cancelUpdateBigramPredictions();
-
- if (!mWordComposer.isComposingWord()) {
+ if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
return;
}
- // TODO: May need a better way of retrieving previous word
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence prevWord;
- if (null == ic) {
- prevWord = null;
- } else {
- prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
- }
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ final String typedWord = mWordComposer.getTypedWord();
+ showSuggestionStrip(suggestedWords, typedWord);
+ }
- final CharSequence typedWord = mWordComposer.getTypedWord();
- // getSuggestedWords handles gracefully a null value of prevWord
+ private SuggestedWords getSuggestedWords() {
+ final String typedWord = mWordComposer.getTypedWord();
+ // Get the word on which we should search the bigrams. If we are composing a word, it's
+ // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
+ // should just skip whitespace if any, so 1.
+ // TODO: this is slow (2-way IPC) - we should probably cache this instead.
+ final CharSequence prevWord =
+ mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
+ mWordComposer.isComposingWord() ? 2 : 1);
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
- prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
-
- // Basically, we update the suggestion strip only when suggestion count > 1. However,
- // there is an exception: We update the suggestion strip whenever typed word's length
- // is 1 or typed word is found in dictionary, regardless of suggestion count. Actually,
- // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
- // need to clear the previous state when the user starts typing a word (i.e. typed word's
- // length == 1).
- if (suggestedWords.size() > 1 || typedWord.length() == 1
- || !suggestedWords.mAllowsToBeAutoCorrected
- || mSuggestionsView.isShowingAddToDictionaryHint()) {
- showSuggestions(suggestedWords, typedWord);
+ prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(),
+ mCurrentSettings.mCorrectionEnabled);
+ return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
+ }
+
+ private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
+ final SuggestedWords suggestedWords) {
+ // TODO: consolidate this into getSuggestedWords
+ // We update the suggestion strip only when we have some suggestions to show, i.e. when
+ // the suggestion count is > 1; else, we leave the old suggestions, with the typed word
+ // replaced with the new one. However, when the word is a dictionary word, or when the
+ // length of the typed word is 1 or 0 (after a deletion typically), we do want to remove the
+ // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to
+ // revert to suggestions - although it is unclear how we can come here if it's displayed.
+ if (suggestedWords.size() > 1 || typedWord.length() <= 1
+ || !suggestedWords.mTypedWordValid
+ || mSuggestionStripView.isShowingAddToDictionaryHint()) {
+ return suggestedWords;
} else {
- SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
- if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
+ SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions();
+ if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
previousSuggestions = SuggestedWords.EMPTY;
}
final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
SuggestedWords.getTypedWordAndPreviousSuggestions(
typedWord, previousSuggestions);
- final SuggestedWords obsoleteSuggestedWords =
- new SuggestedWords(typedWordAndPreviousSuggestions,
+ return new SuggestedWords(typedWordAndPreviousSuggestions,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
true /* isObsoleteSuggestions */,
false /* isPrediction */);
- showSuggestions(obsoleteSuggestedWords, typedWord);
}
}
- public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) {
+ private void showSuggestionStrip(final SuggestedWords suggestedWords,
+ final CharSequence typedWord) {
+ if (null == suggestedWords || suggestedWords.size() <= 0) {
+ clearSuggestionStrip();
+ return;
+ }
final CharSequence autoCorrection;
if (suggestedWords.size() > 0) {
- if (suggestedWords.hasAutoCorrectionWord()) {
+ if (suggestedWords.mWillAutoCorrect) {
autoCorrection = suggestedWords.getWord(1);
} else {
autoCorrection = typedWord;
@@ -1851,94 +1827,82 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mWordComposer.setAutoCorrection(autoCorrection);
final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
- setSuggestions(suggestedWords, isAutoCorrection);
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
setAutoCorrectionIndicator(isAutoCorrection);
setSuggestionStripShown(isSuggestionsStripVisible());
}
- private void commitCurrentAutoCorrection(final int separatorCodePoint,
- final InputConnection ic) {
+ private void commitCurrentAutoCorrection(final int separatorCodePoint) {
// Complete any pending suggestions query first
if (mHandler.hasPendingUpdateSuggestions()) {
- mHandler.cancelUpdateSuggestions();
- updateSuggestions();
+ updateSuggestionStrip();
}
- final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedWord = mWordComposer.getTypedWord();
+ final CharSequence autoCorrection = (typedAutoCorrection != null)
+ ? typedAutoCorrection : typedWord;
if (autoCorrection != null) {
- final String typedWord = mWordComposer.getTypedWord();
if (TextUtils.isEmpty(typedWord)) {
throw new RuntimeException("We have an auto-correction but the typed word "
+ "is empty? Impossible! I must commit suicide.");
}
Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord,
- autoCorrection.toString());
- }
mExpectingUpdateSelection = true;
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
separatorCodePoint);
- if (!typedWord.equals(autoCorrection) && null != ic) {
+ if (!typedWord.equals(autoCorrection)) {
// This will make the correction flash for a short while as a visual clue
// to the user that auto-correction happened.
- ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
+ mConnection.commitCorrection(
+ new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
typedWord, autoCorrection));
}
}
}
+ // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
+ // interface
@Override
- public void pickSuggestionManually(final int index, final CharSequence suggestion,
- int x, int y) {
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) ic.beginBatchEdit();
- pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic);
- if (null != ic) ic.endBatchEdit();
- }
-
- public void pickSuggestionManuallyWhileInBatchEdit(final int index,
- final CharSequence suggestion, final int x, final int y, final InputConnection ic) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
+ public void pickSuggestionManually(final int index, final CharSequence suggestion) {
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
// Word separators are suggested before the user inputs something.
// So, LatinImeLogger logs "" as a user's input.
LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
// Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y);
- }
final int primaryCode = suggestion.charAt(0);
onCodeInput(primaryCode,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_punctuationSuggestion(index, suggestion);
+ }
return;
}
- if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) {
+ mConnection.beginBatchEdit();
+ if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0
+ // In the batch input mode, a manually picked suggested word should just replace
+ // the current batch input text and there is no need for a phantom space.
+ && !mWordComposer.isBatchMode()) {
int firstChar = Character.codePointAt(suggestion, 0);
- if ((!mSettingsValues.isWeakSpaceStripper(firstChar))
- && (!mSettingsValues.isWeakSpaceSwapper(firstChar))) {
+ if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
+ && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
}
- if (mInputAttributes.mApplicationSpecifiedCompletionOn
+ if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()
&& mApplicationSpecifiedCompletions != null
&& index >= 0 && index < mApplicationSpecifiedCompletions.length) {
- if (mSuggestionsView != null) {
- mSuggestionsView.clear();
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.clear();
}
mKeyboardSwitcher.updateShiftState();
resetComposingState(true /* alsoResetLastComposedWord */);
- if (ic != null) {
- final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
- ic.commitCompletion(completionInfo);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
- completionInfo.getText(), x, y);
- }
- }
+ final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
+ mConnection.commitCompletion(completionInfo);
+ mConnection.endBatchEdit();
return;
}
@@ -1947,12 +1911,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final String replacedWord = mWordComposer.getTypedWord().toString();
LatinImeLogger.logOnManualSuggestion(replacedWord,
suggestion.toString(), index, suggestedWords);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y);
- }
mExpectingUpdateSelection = true;
commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion);
+ }
+ mConnection.endBatchEdit();
// Don't allow cancellation of manual pick
mLastComposedWord.deactivate();
mSpaceState = SPACE_STATE_PHANTOM;
@@ -1960,37 +1925,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mKeyboardSwitcher.updateShiftState();
// We should show the "Touch again to save" hint if the user pressed the first entry
- // AND either:
- // - There is no dictionary (we know that because we tried to load it => null != mSuggest
- // AND mSuggest.hasMainDictionary() is false)
- // - There is a dictionary and the word is not in it
+ // AND it's in none of our current dictionaries (main, user or otherwise).
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
- // We used to look at mCorrectionMode here, but showing the hint should have nothing
- // to do with the autocorrection setting.
final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
- // If there is no dictionary the hint should be shown.
- && (!mSuggest.hasMainDictionary()
- // If "suggestion" is not in the dictionary, the hint should be shown.
- || !AutoCorrection.isValidWord(
- mSuggest.getUnigramDictionaries(), suggestion, true));
-
- Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE);
- if (!showingAddToDictionaryHint) {
- // If we're not showing the "Touch again to save", then show corrections again.
- // In case the cursor position doesn't change, make sure we show the suggestions again.
- updateBigramPredictions();
- // Updating the predictions right away may be slow and feel unresponsive on slower
- // terminals. On the other hand if we just postUpdateBigramPredictions() it will
- // take a noticeable delay to update them which may feel uneasy.
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
+
+ Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
+ mSuggestionStripView.showAddToDictionaryHint(
+ suggestion, mCurrentSettings.mHintToSaveText);
} else {
- if (mIsUserDictionaryAvailable) {
- mSuggestionsView.showAddToDictionaryHint(
- suggestion, mSettingsValues.mHintToSaveText);
- } else {
- mHandler.postUpdateSuggestions();
- }
+ // If we're not showing the "Touch again to save", then update the suggestion strip.
+ mHandler.postUpdateSuggestionStrip();
}
}
@@ -1999,23 +1948,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
private void commitChosenWord(final CharSequence chosenWord, final int commitType,
final int separatorCode) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- if (mSettingsValues.mEnableSuggestionSpanInsertion) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
- ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
- this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
- 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(chosenWord);
- }
- } else {
- ic.commitText(chosenWord, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(chosenWord);
- }
- }
- }
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
+ mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
+ this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
// Add the word to the user history dictionary
final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
// TODO: figure out here if this is an auto-correct or if the best word is actually
@@ -2026,42 +1961,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
separatorCode, prevWord);
}
- public void updateBigramPredictions() {
- if (mSuggest == null || !isSuggestionsRequested())
- return;
-
- if (!mSettingsValues.mBigramPredictionEnabled) {
- setPunctuationSuggestions();
- return;
- }
-
- final SuggestedWords suggestedWords;
- if (mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
- final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
- mSettingsValues.mWordSeparators);
- if (!TextUtils.isEmpty(prevWord)) {
- suggestedWords = mSuggest.getBigramPredictions(prevWord);
- } else {
- suggestedWords = null;
- }
- } else {
- suggestedWords = null;
- }
-
- if (null != suggestedWords && suggestedWords.size() > 0) {
- // Explicitly supply an empty typed word (the no-second-arg version of
- // showSuggestions will retrieve the word near the cursor, we don't want that here)
- showSuggestions(suggestedWords, "");
- } else {
- clearSuggestions();
- }
- }
-
- public void setPunctuationSuggestions() {
- if (mSettingsValues.mBigramPredictionEnabled) {
- clearSuggestions();
+ private void setPunctuationSuggestions() {
+ if (mCurrentSettings.mBigramPredictionEnabled) {
+ clearSuggestionStrip();
} else {
- setSuggestions(mSettingsValues.mSuggestPuncList, false);
+ setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
}
setAutoCorrectionIndicator(false);
setSuggestionStripShown(isSuggestionsStripVisible());
@@ -2069,25 +1973,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
if (TextUtils.isEmpty(suggestion)) return null;
+ if (mSuggest == null) return null;
- // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
- // adding words in situations where the user or application really didn't
- // want corrections enabled or learned.
- if (!(mCorrectionMode == Suggest.CORRECTION_FULL
- || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
- return null;
- }
+ // If correction is not enabled, we don't add words to the user history dictionary.
+ // That's to avoid unintended additions in some sensitive fields, or fields that
+ // expect to receive non-words.
+ if (!mCurrentSettings.mCorrectionEnabled) return null;
- if (mUserHistoryDictionary != null) {
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence prevWord;
- if (null != ic) {
- prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
- } else {
- prevWord = null;
- }
+ final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
+ if (userHistoryDictionary != null) {
+ final CharSequence prevWord
+ = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
final String secondWord;
- if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+ if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toString().toLowerCase(
mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
@@ -2098,95 +1996,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int maxFreq = AutoCorrection.getMaxFrequency(
mSuggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
- mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
+ userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
secondWord, maxFreq > 0);
return prevWord;
}
return null;
}
- public boolean isCursorTouchingWord() {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return false;
- CharSequence before = ic.getTextBeforeCursor(1, 0);
- CharSequence after = ic.getTextAfterCursor(1, 0);
- if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))
- && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
- return true;
- }
- if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0))
- && !mSettingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
- return true;
- }
- return false;
- }
-
- // "ic" must not be null
- private static boolean sameAsTextBeforeCursor(final InputConnection ic,
- final CharSequence text) {
- final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
- return TextUtils.equals(text, beforeText);
- }
-
- // "ic" must not be null
/**
* Check if the cursor is actually at the end of a word. If so, restart suggestions on this
* word, else do nothing.
*/
- private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
- final InputConnection ic) {
- // Bail out if the cursor is not at the end of a word (cursor must be preceded by
- // non-whitespace, non-separator, non-start-of-text)
- // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
- if (TextUtils.isEmpty(textBeforeCursor)
- || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
-
- // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
- // separator or end of line/text)
- // Example: "test|"<EOL> "te|st" get rejected here
- final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
- if (!TextUtils.isEmpty(textAfterCursor)
- && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
-
- // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
- // Example: " -|" gets rejected here but "e-|" and "e|" are okay
- CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
- // We don't suggest on leading single quotes, so we have to remove them from the word if
- // it starts with single quotes.
- while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
- word = word.subSequence(1, word.length());
- }
- if (TextUtils.isEmpty(word)) return;
- final char firstChar = word.charAt(0); // we just tested that word is not empty
- if (word.length() == 1 && !Character.isLetter(firstChar)) return;
-
- // We only suggest on words that start with a letter or a symbol that is excluded from
- // word separators (see #handleCharacterWhileInBatchEdit).
- if (!(isAlphabet(firstChar)
- || mSettingsValues.isSymbolExcludedFromWordSeparators(firstChar))) {
- return;
+ private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
+ final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings);
+ if (null != word) {
+ restartSuggestionsOnWordBeforeCursor(word);
}
-
- // Okay, we are at the end of a word. Restart suggestions.
- restartSuggestionsOnWordBeforeCursor(ic, word);
}
- // "ic" must not be null
- private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
- final CharSequence word) {
+ private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
final int length = word.length();
- ic.deleteSurroundingText(length, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(length);
- }
- ic.setComposingText(word, 1);
- mHandler.postUpdateSuggestions();
+ mConnection.deleteSurroundingText(length, 0);
+ mConnection.setComposingText(word, 1);
+ mHandler.postUpdateSuggestionStrip();
}
- // "ic" must not be null
- private void revertCommit(final InputConnection ic) {
+ private void revertCommit() {
final CharSequence previousWord = mLastComposedWord.mPrevWord;
final String originallyTypedWord = mLastComposedWord.mTypedWord;
final CharSequence committedWord = mLastComposedWord.mCommittedWord;
@@ -2200,7 +2036,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
throw new RuntimeException("revertCommit, but we are composing a word");
}
final String wordBeforeCursor =
- ic.getTextBeforeCursor(deleteLength, 0)
+ mConnection.getTextBeforeCursor(deleteLength, 0)
.subSequence(0, cancelLength).toString();
if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were "
@@ -2208,130 +2044,65 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
}
}
- ic.deleteSurroundingText(deleteLength, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(deleteLength);
- }
+ mConnection.deleteSurroundingText(deleteLength, 0);
if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
mUserHistoryDictionary.cancelAddingUserHistory(
previousWord.toString(), committedWord.toString());
}
- if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
- // This is the case when we cancel a manual pick.
- // We should restart suggestion on the word right away.
- mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
- ic.setComposingText(originallyTypedWord, 1);
- } else {
- ic.commitText(originallyTypedWord, 1);
- // Re-insert the separator
- sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
- Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertCommit(originallyTypedWord);
- }
- // Don't restart suggestion yet. We'll restart if the user deletes the
- // separator.
- }
- mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- mHandler.cancelUpdateBigramPredictions();
- mHandler.postUpdateSuggestions();
- }
-
- // "ic" must not be null
- private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
- mHandler.cancelDoubleSpacesTimer();
- // Here we test whether we indeed have a period and a space before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- if (!". ".equals(textBeforeCursor)) {
- // Theoretically we should not be coming here if there isn't ". " before the
- // cursor, but the application may be changing the text while we are typing, so
- // anything goes. We should not crash.
- Log.d(TAG, "Tried to revert double-space combo but we didn't find "
- + "\". \" just before the cursor.");
- return false;
- }
- ic.deleteSurroundingText(2, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(" ", 1);
+ mConnection.commitText(originallyTypedWord, 1);
+ // Re-insert the separator
+ sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
+ Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
+ ResearchLogger.latinIME_revertCommit(originallyTypedWord);
}
- return true;
- }
-
- private static boolean revertSwapPunctuation(final InputConnection ic) {
- // Here we test whether we indeed have a space and something else before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
- // enter surrogate pairs this code will have been removed.
- if (TextUtils.isEmpty(textBeforeCursor)
- || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
- // We may only come here if the application is changing the text while we are typing.
- // This is quite a broken case, but not logically impossible, so we shouldn't crash,
- // but some debugging log may be in order.
- Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
- + "find a space just before the cursor.");
- return false;
- }
- ic.beginBatchEdit();
- ic.deleteSurroundingText(2, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertSwapPunctuation();
- }
- ic.endBatchEdit();
- return true;
+ // 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.
+ mHandler.postUpdateSuggestionStrip();
}
+ // Used by the RingCharBuffer
public boolean isWordSeparator(int code) {
- return mSettingsValues.isWordSeparator(code);
- }
-
- public boolean preferCapitalization() {
- return mWordComposer.isFirstCharCapitalized();
+ return mCurrentSettings.isWordSeparator(code);
}
- // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
- // according to new language or mode.
- public void onRefreshKeyboard() {
+ // TODO: Make this private
+ // Outside LatinIME, only used by the {@link InputTestsBase} test suite.
+ /* package for test */
+ void loadKeyboard() {
// When the device locale is changed in SetupWizard etc., this method may get called via
// onConfigurationChanged before SoftInputWindow is shown.
- if (mKeyboardSwitcher.getKeyboardView() != null) {
- // Reload keyboard because the current language has been changed.
- mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
- }
initSuggest();
- updateCorrectionMode();
loadSettings();
+ if (mKeyboardSwitcher.getMainKeyboardView() != null) {
+ // Reload keyboard because the current language has been changed.
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
+ }
// Since we just changed languages, we should re-evaluate suggestions with whatever word
// we are currently composing. If we are not composing anything, we may want to display
- // predictions or punctuation signs (which is done by updateBigramPredictions anyway).
- if (isCursorTouchingWord()) {
- mHandler.postUpdateSuggestions();
- } else {
- mHandler.postUpdateBigramPredictions();
- }
+ // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway).
+ mHandler.postUpdateSuggestionStrip();
}
// TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
- // {@link KeyboardSwitcher}.
+ // {@link KeyboardSwitcher}. Called from KeyboardSwitcher
public void hapticAndAudioFeedback(final int primaryCode) {
- mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView());
+ mFeedbackManager.hapticAndAudioFeedback(
+ primaryCode, mKeyboardSwitcher.getMainKeyboardView());
}
+ // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
+ // key is depressed; release matching call is onReleaseKey below.
@Override
public void onPressKey(int primaryCode) {
mKeyboardSwitcher.onPressKey(primaryCode);
}
+ // Callback by PointerTracker through the KeyboardActionListener. This is called when a key
+ // is released; press matching call is onPressKey above.
@Override
public void onReleaseKey(int primaryCode, boolean withSliding) {
mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
@@ -2352,12 +2123,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
// In the future, we need to deprecate deteleSurroundingText() and have a surrogate
// pair-friendly way of deleting characters in InputConnection.
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) {
- final CharSequence lastChar = ic.getTextBeforeCursor(1, 0);
- if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
- ic.deleteSurroundingText(1, 0);
- }
+ final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
+ if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
+ mConnection.deleteSurroundingText(1, 0);
}
}
}
@@ -2375,37 +2143,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
};
- private void updateCorrectionMode() {
- // TODO: cleanup messy flags
- final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
- && !mInputAttributes.mInputTypeNoAutoCorrect;
- mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
- mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
- ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
- }
-
- private void updateSuggestionVisibility(final Resources res) {
- final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting;
- for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
- if (suggestionVisiblityStr.equals(res.getString(visibility))) {
- mSuggestionVisibility = visibility;
- break;
- }
- }
- }
-
private void launchSettings() {
- launchSettingsClass(SettingsActivity.class);
+ handleClose();
+ launchSubActivity(SettingsActivity.class);
}
+ // Called from debug code only
public void launchDebugSettings() {
- launchSettingsClass(DebugSettingsActivity.class);
+ handleClose();
+ launchSubActivity(DebugSettingsActivity.class);
}
- private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
- handleClose();
+ public void launchKeyboardedDialogActivity(Class<? extends Activity> activityClass) {
+ // Put the text in the attached EditText into a safe, saved state before switching to a
+ // new activity that will also use the soft keyboard.
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ launchSubActivity(activityClass);
+ }
+
+ private void launchSubActivity(Class<? extends Activity> activityClass) {
Intent intent = new Intent();
- intent.setClass(LatinIME.this, settingsClass);
+ intent.setClass(LatinIME.this, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
@@ -2440,12 +2198,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setItems(items, listener)
.setTitle(title);
- showOptionDialogInternal(builder.create());
+ showOptionDialog(builder.create());
}
- private void showOptionDialogInternal(AlertDialog dialog) {
- final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
- if (windowToken == null) return;
+ public void showOptionDialog(AlertDialog dialog) {
+ final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
+ if (windowToken == null) {
+ return;
+ }
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
@@ -2470,13 +2230,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
p.println(" Keyboard mode = " + keyboardMode);
- p.println(" mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
- p.println(" mCorrectionMode=" + mCorrectionMode);
+ p.println(" mIsSuggestionsSuggestionsRequested = "
+ + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation));
+ p.println(" mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled);
p.println(" isComposingWord=" + mWordComposer.isComposingWord());
- p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
- p.println(" mSoundOn=" + mSettingsValues.mSoundOn);
- p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn);
- p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
- p.println(" mInputAttributes=" + mInputAttributes.toString());
+ p.println(" mSoundOn=" + mCurrentSettings.mSoundOn);
+ p.println(" mVibrateOn=" + mCurrentSettings.mVibrateOn);
+ p.println(" mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
+ p.println(" inputAttributes=" + mCurrentSettings.getInputAttributesDebugString());
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index dc0868e7c..e843848bc 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -71,7 +71,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
public static void onStartSuggestion(CharSequence previousWords) {
}
- public static void onAddSuggestedWord(String word, int typeId, int dataType) {
+ public static void onAddSuggestedWord(String word, String sourceDictionaryId) {
}
public static void onSetKeyboard(Keyboard kb) {
diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java
index b938dd336..3b08cab01 100644
--- a/java/src/com/android/inputmethod/latin/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java
@@ -193,7 +193,7 @@ public class LocaleUtils {
}
}
- private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>();
+ private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap();
/**
* Creates a locale from a string specification.
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java
deleted file mode 100644
index 66d6d58b1..000000000
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.SharedPreferences;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.latin.define.ProductionFlag;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.charset.Charset;
-import java.util.Map;
-
-/**
- * Logs the use of the LatinIME keyboard.
- *
- * This class logs operations on the IME keyboard, including what the user has typed.
- * Data is stored locally in a file in app-specific storage.
- *
- * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
- */
-public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = ResearchLogger.class.getSimpleName();
- private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
- private static final boolean DEBUG = false;
-
- private static final ResearchLogger sInstance = new ResearchLogger(new LogFileManager());
- public static boolean sIsLogging = false;
- /* package */ final Handler mLoggingHandler;
- private InputMethodService mIms;
-
- /**
- * Isolates management of files. This variable should never be null, but can be changed
- * to support testing.
- */
- /* package */ LogFileManager mLogFileManager;
-
- /**
- * Manages the file(s) that stores the logs.
- *
- * Handles creation, deletion, and provides Readers, Writers, and InputStreams to access
- * the logs.
- */
- /* package */ static class LogFileManager {
- public static final String RESEARCH_LOG_FILENAME_KEY = "RESEARCH_LOG_FILENAME";
-
- private static final String DEFAULT_FILENAME = "researchLog.txt";
- private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24;
-
- protected InputMethodService mIms;
- protected File mFile;
- protected PrintWriter mPrintWriter;
-
- /* package */ LogFileManager() {
- }
-
- public void init(final InputMethodService ims) {
- mIms = ims;
- }
-
- public synchronized void createLogFile() throws IOException {
- createLogFile(DEFAULT_FILENAME);
- }
-
- public synchronized void createLogFile(final SharedPreferences prefs)
- throws IOException {
- final String filename =
- prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME);
- createLogFile(filename);
- }
-
- public synchronized void createLogFile(final String filename)
- throws IOException {
- if (mIms == null) {
- final String msg = "InputMethodService is not configured. Logging is off.";
- Log.w(TAG, msg);
- throw new IOException(msg);
- }
- final File filesDir = mIms.getFilesDir();
- if (filesDir == null || !filesDir.exists()) {
- final String msg = "Storage directory does not exist. Logging is off.";
- Log.w(TAG, msg);
- throw new IOException(msg);
- }
- close();
- final File file = new File(filesDir, filename);
- mFile = file;
- boolean append = true;
- if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL <
- System.currentTimeMillis()) {
- append = false;
- }
- mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true);
- }
-
- public synchronized boolean append(final String s) {
- PrintWriter printWriter = mPrintWriter;
- if (printWriter == null || !mFile.exists()) {
- if (DEBUG) {
- Log.w(TAG, "PrintWriter is null... attempting to create default log file");
- }
- try {
- createLogFile();
- printWriter = mPrintWriter;
- } catch (IOException e) {
- Log.w(TAG, "Failed to create log file. Not logging.");
- return false;
- }
- }
- printWriter.print(s);
- printWriter.flush();
- return !printWriter.checkError();
- }
-
- public synchronized void reset() {
- if (mPrintWriter != null) {
- mPrintWriter.close();
- mPrintWriter = null;
- if (DEBUG) {
- Log.d(TAG, "logfile closed");
- }
- }
- if (mFile != null) {
- mFile.delete();
- if (DEBUG) {
- Log.d(TAG, "logfile deleted");
- }
- mFile = null;
- }
- }
-
- public synchronized void close() {
- if (mPrintWriter != null) {
- mPrintWriter.close();
- mPrintWriter = null;
- mFile = null;
- if (DEBUG) {
- Log.d(TAG, "logfile closed");
- }
- }
- }
-
- /* package */ synchronized void flush() {
- if (mPrintWriter != null) {
- mPrintWriter.flush();
- }
- }
-
- /* package */ synchronized String getContents() {
- final File file = mFile;
- if (file == null) {
- return "";
- }
- if (mPrintWriter != null) {
- mPrintWriter.flush();
- }
- FileInputStream stream = null;
- FileChannel fileChannel = null;
- String s = "";
- try {
- stream = new FileInputStream(file);
- fileChannel = stream.getChannel();
- final ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
- fileChannel.read(byteBuffer);
- byteBuffer.rewind();
- CharBuffer charBuffer = Charset.defaultCharset().decode(byteBuffer);
- s = charBuffer.toString();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (fileChannel != null) {
- fileChannel.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return s;
- }
- }
-
- private ResearchLogger(final LogFileManager logFileManager) {
- final HandlerThread handlerThread = new HandlerThread("ResearchLogger logging task",
- Process.THREAD_PRIORITY_BACKGROUND);
- handlerThread.start();
- mLoggingHandler = new Handler(handlerThread.getLooper());
- mLogFileManager = logFileManager;
- }
-
- public static ResearchLogger getInstance() {
- return sInstance;
- }
-
- public static void init(final InputMethodService ims, final SharedPreferences prefs) {
- sInstance.initInternal(ims, prefs);
- }
-
- /* package */ void initInternal(final InputMethodService ims, final SharedPreferences prefs) {
- mIms = ims;
- final LogFileManager logFileManager = mLogFileManager;
- if (logFileManager != null) {
- logFileManager.init(ims);
- try {
- logFileManager.createLogFile(prefs);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if (prefs != null) {
- sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
- prefs.registerOnSharedPreferenceChangeListener(this);
- }
- }
-
- /**
- * Represents a category of logging events that share the same subfield structure.
- */
- private static enum LogGroup {
- MOTION_EVENT("m"),
- KEY("k"),
- CORRECTION("c"),
- STATE_CHANGE("s"),
- UNSTRUCTURED("u");
-
- private final String mLogString;
-
- private LogGroup(final String logString) {
- mLogString = logString;
- }
- }
-
- public void logMotionEvent(final int action, final long eventTime, final int id,
- final int x, final int y, final float size, final float pressure) {
- final String eventTag;
- switch (action) {
- case MotionEvent.ACTION_CANCEL: eventTag = "[Cancel]"; break;
- case MotionEvent.ACTION_UP: eventTag = "[Up]"; break;
- case MotionEvent.ACTION_DOWN: eventTag = "[Down]"; break;
- case MotionEvent.ACTION_POINTER_UP: eventTag = "[PointerUp]"; break;
- case MotionEvent.ACTION_POINTER_DOWN: eventTag = "[PointerDown]"; break;
- case MotionEvent.ACTION_MOVE: eventTag = "[Move]"; break;
- case MotionEvent.ACTION_OUTSIDE: eventTag = "[Outside]"; break;
- default: eventTag = "[Action" + action + "]"; break;
- }
- if (!TextUtils.isEmpty(eventTag)) {
- final StringBuilder sb = new StringBuilder();
- sb.append(eventTag);
- sb.append('\t'); sb.append(eventTime);
- sb.append('\t'); sb.append(id);
- sb.append('\t'); sb.append(x);
- sb.append('\t'); sb.append(y);
- sb.append('\t'); sb.append(size);
- sb.append('\t'); sb.append(pressure);
- write(LogGroup.MOTION_EVENT, sb.toString());
- }
- }
-
- public void logKeyEvent(final int code, final int x, final int y) {
- final StringBuilder sb = new StringBuilder();
- sb.append(Keyboard.printableCode(code));
- sb.append('\t'); sb.append(x);
- sb.append('\t'); sb.append(y);
- write(LogGroup.KEY, sb.toString());
- }
-
- public void logCorrection(final String subgroup, final String before, final String after,
- final int position) {
- final StringBuilder sb = new StringBuilder();
- sb.append(subgroup);
- sb.append('\t'); sb.append(before);
- sb.append('\t'); sb.append(after);
- sb.append('\t'); sb.append(position);
- write(LogGroup.CORRECTION, sb.toString());
- }
-
- public void logStateChange(final String subgroup, final String details) {
- write(LogGroup.STATE_CHANGE, subgroup + "\t" + details);
- }
-
- public static class UnsLogGroup {
- private static final boolean DEFAULT_ENABLED = true;
-
- private static final boolean KEYBOARDSTATE_ONCANCELINPUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONCODEINPUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONPRESSKEY_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONRELEASEKEY_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_COMMITTEXT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_DELETESURROUNDINGTEXT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONDISPLAYCOMPLETIONS_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONUPDATESELECTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PERFORMEDITORACTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKSUGGESTIONMANUALLY_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTCOMMIT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTSWAPPUNCTUATION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_SENDKEYCODEPOINT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean
- POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_ONDOWNEVENT_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_ONMOVEEVENT_ENABLED = DEFAULT_ENABLED;
- private static final boolean SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED = DEFAULT_ENABLED;
- }
-
- public static void logUnstructured(String logGroup, final String details) {
- // TODO: improve performance by making entire class static and/or implementing natively
- getInstance().write(LogGroup.UNSTRUCTURED, logGroup + "\t" + details);
- }
-
- private void write(final LogGroup logGroup, final String log) {
- // TODO: rewrite in native for better performance
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- final long currentTime = System.currentTimeMillis();
- final long upTime = SystemClock.uptimeMillis();
- final StringBuilder builder = new StringBuilder();
- builder.append(currentTime);
- builder.append('\t'); builder.append(upTime);
- builder.append('\t'); builder.append(logGroup.mLogString);
- builder.append('\t'); builder.append(log);
- builder.append('\n');
- if (DEBUG) {
- Log.d(TAG, "Write: " + '[' + logGroup.mLogString + ']' + log);
- }
- final String s = builder.toString();
- if (mLogFileManager.append(s)) {
- // success
- } else {
- if (DEBUG) {
- Log.w(TAG, "Unable to write to log.");
- }
- // perhaps logfile was deleted. try to recreate and relog.
- try {
- mLogFileManager.createLogFile(PreferenceManager
- .getDefaultSharedPreferences(mIms));
- mLogFileManager.append(s);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- });
- }
-
- public void clearAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "Delete log file.");
- }
- mLogFileManager.reset();
- }
- });
- }
-
- /* package */ LogFileManager getLogFileManager() {
- return mLogFileManager;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- if (key == null || prefs == null) {
- return;
- }
- sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
- }
-
- public static void keyboardState_onCancelInput(final boolean isSinglePointer,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONCANCELINPUT_ENABLED) {
- final String s = "onCancelInput: single=" + isSinglePointer + " " + keyboardState;
- logUnstructured("KeyboardState_onCancelInput", s);
- }
- }
-
- public static void keyboardState_onCodeInput(
- final int code, final boolean isSinglePointer, final int autoCaps,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONCODEINPUT_ENABLED) {
- final String s = "onCodeInput: code=" + Keyboard.printableCode(code)
- + " single=" + isSinglePointer
- + " autoCaps=" + autoCaps + " " + keyboardState;
- logUnstructured("KeyboardState_onCodeInput", s);
- }
- }
-
- public static void keyboardState_onLongPressTimeout(final int code,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED) {
- final String s = "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " "
- + keyboardState;
- logUnstructured("KeyboardState_onLongPressTimeout", s);
- }
- }
-
- public static void keyboardState_onPressKey(final int code,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONPRESSKEY_ENABLED) {
- final String s = "onPressKey: code=" + Keyboard.printableCode(code) + " "
- + keyboardState;
- logUnstructured("KeyboardState_onPressKey", s);
- }
- }
-
- public static void keyboardState_onReleaseKey(final KeyboardState keyboardState, final int code,
- final boolean withSliding) {
- if (UnsLogGroup.KEYBOARDSTATE_ONRELEASEKEY_ENABLED) {
- final String s = "onReleaseKey: code=" + Keyboard.printableCode(code)
- + " sliding=" + withSliding + " " + keyboardState;
- logUnstructured("KeyboardState_onReleaseKey", s);
- }
- }
-
- public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
- final String autoCorrection) {
- if (UnsLogGroup.LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED) {
- if (typedWord.equals(autoCorrection)) {
- getInstance().logCorrection("[----]", typedWord, autoCorrection, -1);
- } else {
- getInstance().logCorrection("[Auto]", typedWord, autoCorrection, -1);
- }
- }
- }
-
- public static void latinIME_commitText(final CharSequence typedWord) {
- if (UnsLogGroup.LATINIME_COMMITTEXT_ENABLED) {
- logUnstructured("LatinIME_commitText", typedWord.toString());
- }
- }
-
- public static void latinIME_deleteSurroundingText(final int length) {
- if (UnsLogGroup.LATINIME_DELETESURROUNDINGTEXT_ENABLED) {
- logUnstructured("LatinIME_deleteSurroundingText", String.valueOf(length));
- }
- }
-
- public static void latinIME_doubleSpaceAutoPeriod() {
- if (UnsLogGroup.LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED) {
- logUnstructured("LatinIME_doubleSpaceAutoPeriod", "");
- }
- }
-
- public static void latinIME_onDisplayCompletions(
- final CompletionInfo[] applicationSpecifiedCompletions) {
- if (UnsLogGroup.LATINIME_ONDISPLAYCOMPLETIONS_ENABLED) {
- final StringBuilder builder = new StringBuilder();
- builder.append("Received completions:");
- if (applicationSpecifiedCompletions != null) {
- for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
- builder.append(" #");
- builder.append(i);
- builder.append(": ");
- builder.append(applicationSpecifiedCompletions[i]);
- builder.append("\n");
- }
- }
- logUnstructured("LatinIME_onDisplayCompletions", builder.toString());
- }
- }
-
- public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
- final SharedPreferences prefs) {
- if (UnsLogGroup.LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED) {
- final StringBuilder builder = new StringBuilder();
- builder.append("onStartInputView: editorInfo:");
- builder.append("\tinputType=");
- builder.append(Integer.toHexString(editorInfo.inputType));
- builder.append("\timeOptions=");
- builder.append(Integer.toHexString(editorInfo.imeOptions));
- builder.append("\tdisplay="); builder.append(Build.DISPLAY);
- builder.append("\tmodel="); builder.append(Build.MODEL);
- for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
- builder.append("\t" + entry.getKey());
- Object value = entry.getValue();
- builder.append("=" + ((value == null) ? "<null>" : value.toString()));
- }
- logUnstructured("LatinIME_onStartInputViewInternal", builder.toString());
- }
- }
-
- public static void latinIME_onUpdateSelection(final int lastSelectionStart,
- final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
- final int newSelStart, final int newSelEnd, final int composingSpanStart,
- final int composingSpanEnd) {
- if (UnsLogGroup.LATINIME_ONUPDATESELECTION_ENABLED) {
- final String s = "onUpdateSelection: oss=" + oldSelStart
- + ", ose=" + oldSelEnd
- + ", lss=" + lastSelectionStart
- + ", lse=" + lastSelectionEnd
- + ", nss=" + newSelStart
- + ", nse=" + newSelEnd
- + ", cs=" + composingSpanStart
- + ", ce=" + composingSpanEnd;
- logUnstructured("LatinIME_onUpdateSelection", s);
- }
- }
-
- public static void latinIME_performEditorAction(final int imeActionNext) {
- if (UnsLogGroup.LATINIME_PERFORMEDITORACTION_ENABLED) {
- logUnstructured("LatinIME_performEditorAction", String.valueOf(imeActionNext));
- }
- }
-
- public static void latinIME_pickApplicationSpecifiedCompletion(final int index,
- final CharSequence text, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED) {
- final String s = String.valueOf(index) + '\t' + text + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickApplicationSpecifiedCompletion", s);
- }
- }
-
- public static void latinIME_pickSuggestionManually(final String replacedWord,
- final int index, CharSequence suggestion, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKSUGGESTIONMANUALLY_ENABLED) {
- final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickSuggestionManually", s);
- }
- }
-
- public static void latinIME_punctuationSuggestion(final int index,
- final CharSequence suggestion, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED) {
- final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickPunctuationSuggestion", s);
- }
- }
-
- public static void latinIME_revertDoubleSpaceWhileInBatchEdit() {
- if (UnsLogGroup.LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED) {
- logUnstructured("LatinIME_revertDoubleSpaceWhileInBatchEdit", "");
- }
- }
-
- public static void latinIME_revertSwapPunctuation() {
- if (UnsLogGroup.LATINIME_REVERTSWAPPUNCTUATION_ENABLED) {
- logUnstructured("LatinIME_revertSwapPunctuation", "");
- }
- }
-
- public static void latinIME_sendKeyCodePoint(final int code) {
- if (UnsLogGroup.LATINIME_SENDKEYCODEPOINT_ENABLED) {
- logUnstructured("LatinIME_sendKeyCodePoint", String.valueOf(code));
- }
- }
-
- public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() {
- if (UnsLogGroup.LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED) {
- logUnstructured("latinIME_swapSwapperAndSpaceWhileInBatchEdit", "");
- }
- }
-
- public static void latinIME_switchToKeyboardView() {
- if (UnsLogGroup.LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED) {
- final String s = "Switch to keyboard view.";
- logUnstructured("LatinIME_switchToKeyboardView", s);
- }
- }
-
- public static void latinKeyboardView_onLongPress() {
- if (UnsLogGroup.LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED) {
- final String s = "long press detected";
- logUnstructured("LatinKeyboardView_onLongPress", s);
- }
- }
-
- public static void latinKeyboardView_processMotionEvent(MotionEvent me, int action,
- long eventTime, int index, int id, int x, int y) {
- if (UnsLogGroup.LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED) {
- final float size = me.getSize(index);
- final float pressure = me.getPressure(index);
- if (action != MotionEvent.ACTION_MOVE) {
- getInstance().logMotionEvent(action, eventTime, id, x, y, size, pressure);
- }
- }
- }
-
- public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
- if (UnsLogGroup.LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED) {
- StringBuilder builder = new StringBuilder();
- builder.append("id=");
- builder.append(keyboard.mId);
- builder.append("\tw=");
- builder.append(keyboard.mOccupiedWidth);
- builder.append("\th=");
- builder.append(keyboard.mOccupiedHeight);
- builder.append("\tkeys=[");
- boolean first = true;
- for (Key key : keyboard.mKeys) {
- if (first) {
- first = false;
- } else {
- builder.append(",");
- }
- builder.append("{code:");
- builder.append(key.mCode);
- builder.append(",altCode:");
- builder.append(key.mAltCode);
- builder.append(",x:");
- builder.append(key.mX);
- builder.append(",y:");
- builder.append(key.mY);
- builder.append(",w:");
- builder.append(key.mWidth);
- builder.append(",h:");
- builder.append(key.mHeight);
- builder.append("}");
- }
- builder.append("]");
- logUnstructured("LatinKeyboardView_setKeyboard", builder.toString());
- }
- }
-
- public static void latinIME_revertCommit(final String originallyTypedWord) {
- if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) {
- logUnstructured("LatinIME_revertCommit", originallyTypedWord);
- }
- }
-
- public static void pointerTracker_callListenerOnCancelInput() {
- final String s = "onCancelInput";
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED) {
- logUnstructured("PointerTracker_callListenerOnCancelInput", s);
- }
- }
-
- public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
- final int y, final boolean ignoreModifierKey, final boolean altersCode,
- final int code) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED) {
- final String s = "onCodeInput: " + Keyboard.printableCode(code)
- + " text=" + key.mOutputText + " x=" + x + " y=" + y
- + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
- + " enabled=" + key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnCodeInput", s);
- }
- }
-
- public static void pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(
- final Key key, final boolean ignoreModifierKey) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED) {
- final String s = "onPress : " + KeyDetector.printableCode(key)
- + " ignoreModifier=" + ignoreModifierKey
- + " enabled=" + key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange", s);
- }
- }
-
- public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
- final boolean withSliding, final boolean ignoreModifierKey) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED) {
- final String s = "onRelease : " + Keyboard.printableCode(primaryCode)
- + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
- + " enabled="+ key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnRelease", s);
- }
- }
-
- public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
- if (UnsLogGroup.POINTERTRACKER_ONDOWNEVENT_ENABLED) {
- final String s = "onDownEvent: ignore potential noise: time=" + deltaT
- + " distance=" + distanceSquared;
- logUnstructured("PointerTracker_onDownEvent", s);
- }
- }
-
- public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
- final int lastY) {
- if (UnsLogGroup.POINTERTRACKER_ONMOVEEVENT_ENABLED) {
- final String s = String.format("onMoveEvent: sudden move is translated to "
- + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y);
- logUnstructured("PointerTracker_onMoveEvent", s);
- }
- }
-
- public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
- if (UnsLogGroup.SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED) {
- final String s = "onTouchEvent: ignore sudden jump " + me;
- logUnstructured("SuddenJumpingTouchEventHandler_onTouchEvent", s);
- }
- }
-
- public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) {
- if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) {
- logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString());
- }
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
new file mode 100644
index 000000000..c660f92c4
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.Arrays;
+
+// TODO: This class is not thread-safe.
+public class ResizableIntArray {
+ private int[] mArray;
+ private int mLength;
+
+ public ResizableIntArray(final int capacity) {
+ reset(capacity);
+ }
+
+ public int get(final int index) {
+ if (index < mLength) {
+ return mArray[index];
+ }
+ throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index);
+ }
+
+ public void add(final int index, final int val) {
+ if (index < mLength) {
+ mArray[index] = val;
+ } else {
+ mLength = index;
+ add(val);
+ }
+ }
+
+ public void add(final int val) {
+ final int currentLength = mLength;
+ ensureCapacity(currentLength + 1);
+ mArray[currentLength] = val;
+ mLength = currentLength + 1;
+ }
+
+ /**
+ * Calculate the new capacity of {@code mArray}.
+ * @param minimumCapacity the minimum capacity that the {@code mArray} should have.
+ * @return the new capacity that the {@code mArray} should have. Returns zero when there is no
+ * need to expand {@code mArray}.
+ */
+ private int calculateCapacity(final int minimumCapacity) {
+ final int currentCapcity = mArray.length;
+ if (currentCapcity < minimumCapacity) {
+ final int nextCapacity = currentCapcity * 2;
+ // The following is the same as return Math.max(minimumCapacity, nextCapacity);
+ return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity;
+ }
+ return 0;
+ }
+
+ private void ensureCapacity(final int minimumCapacity) {
+ final int newCapacity = calculateCapacity(minimumCapacity);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = Arrays.copyOf(mArray, newCapacity);
+ }
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public void setLength(final int newLength) {
+ ensureCapacity(newLength);
+ mLength = newLength;
+ }
+
+ public void reset(final int capacity) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[capacity];
+ mLength = 0;
+ }
+
+ public int[] getPrimitiveArray() {
+ return mArray;
+ }
+
+ public void set(final ResizableIntArray ip) {
+ // TODO: Implement primitive array pool.
+ mArray = ip.mArray;
+ mLength = ip.mLength;
+ }
+
+ public void copy(final ResizableIntArray ip) {
+ final int newCapacity = calculateCapacity(ip.mLength);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[newCapacity];
+ }
+ System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength);
+ mLength = ip.mLength;
+ }
+
+ public void append(final ResizableIntArray src, final int startPos, final int length) {
+ if (length == 0) {
+ return;
+ }
+ final int currentLength = mLength;
+ final int newLength = currentLength + length;
+ ensureCapacity(newLength);
+ System.arraycopy(src.mArray, startPos, mArray, currentLength, length);
+ mLength = newLength;
+ }
+
+ public void fill(final int value, final int startPos, final int length) {
+ if (startPos < 0 || length < 0) {
+ throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length);
+ }
+ final int endPos = startPos + length;
+ ensureCapacity(endPos);
+ Arrays.fill(mArray, startPos, endPos, value);
+ if (mLength < endPos) {
+ mLength = endPos;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mLength; i++) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(mArray[i]);
+ }
+ return "[" + sb + "]";
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
new file mode 100644
index 000000000..41e59e92d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.inputmethodservice.InputMethodService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
+
+import java.util.regex.Pattern;
+
+/**
+ * Wrapper for InputConnection to simplify interaction
+ */
+public class RichInputConnection {
+ private static final String TAG = RichInputConnection.class.getSimpleName();
+ private static final boolean DBG = false;
+ // Provision for a long word pair and a separator
+ private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1;
+ private static final Pattern spaceRegex = Pattern.compile("\\s+");
+ private static final int INVALID_CURSOR_POSITION = -1;
+
+ private final InputMethodService mParent;
+ InputConnection mIC;
+ int mNestLevel;
+ public RichInputConnection(final InputMethodService parent) {
+ mParent = parent;
+ mIC = null;
+ mNestLevel = 0;
+ }
+
+ public void beginBatchEdit() {
+ if (++mNestLevel == 1) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) {
+ mIC.beginBatchEdit();
+ }
+ } else {
+ if (DBG) {
+ throw new RuntimeException("Nest level too deep");
+ } else {
+ Log.e(TAG, "Nest level too deep : " + mNestLevel);
+ }
+ }
+ }
+ public void endBatchEdit() {
+ if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead
+ if (--mNestLevel == 0 && null != mIC) {
+ mIC.endBatchEdit();
+ }
+ }
+
+ private void checkBatchEdit() {
+ if (mNestLevel != 1) {
+ // TODO: exception instead
+ Log.e(TAG, "Batch edit level incorrect : " + mNestLevel);
+ Log.e(TAG, Utils.getStackTrace(4));
+ }
+ }
+
+ public void finishComposingText() {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.finishComposingText();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_finishComposingText();
+ }
+ }
+ }
+
+ public void commitText(final CharSequence text, final int i) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.commitText(text, i);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitText(text, i);
+ }
+ }
+ }
+
+ public int getCursorCapsMode(final int inputType) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
+ return mIC.getCursorCapsMode(inputType);
+ }
+
+ public CharSequence getTextBeforeCursor(final int i, final int j) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) return mIC.getTextBeforeCursor(i, j);
+ return null;
+ }
+
+ public CharSequence getTextAfterCursor(final int i, final int j) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) return mIC.getTextAfterCursor(i, j);
+ return null;
+ }
+
+ public void deleteSurroundingText(final int i, final int j) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.deleteSurroundingText(i, j);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_deleteSurroundingText(i, j);
+ }
+ }
+ }
+
+ public void performEditorAction(final int actionId) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) {
+ mIC.performEditorAction(actionId);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_performEditorAction(actionId);
+ }
+ }
+ }
+
+ public void sendKeyEvent(final KeyEvent keyEvent) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.sendKeyEvent(keyEvent);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_sendKeyEvent(keyEvent);
+ }
+ }
+ }
+
+ public void setComposingText(final CharSequence text, final int i) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.setComposingText(text, i);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_setComposingText(text, i);
+ }
+ }
+ }
+
+ public void setSelection(final int from, final int to) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.setSelection(from, to);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_setSelection(from, to);
+ }
+ }
+ }
+
+ public void commitCorrection(final CorrectionInfo correctionInfo) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.commitCorrection(correctionInfo);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitCorrection(correctionInfo);
+ }
+ }
+ }
+
+ public void commitCompletion(final CompletionInfo completionInfo) {
+ checkBatchEdit();
+ if (null != mIC) {
+ mIC.commitCompletion(completionInfo);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitCompletion(completionInfo);
+ }
+ }
+ }
+
+ public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null == mIC) return null;
+ final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
+ return getNthPreviousWord(prev, sentenceSeperators, n);
+ }
+
+ /**
+ * Represents a range of text, relative to the current cursor position.
+ */
+ public static class Range {
+ /** Characters before selection start */
+ public final int mCharsBefore;
+
+ /**
+ * Characters after selection start, including one trailing word
+ * separator.
+ */
+ public final int mCharsAfter;
+
+ /** The actual characters that make up a word */
+ public final String mWord;
+
+ public Range(int charsBefore, int charsAfter, String word) {
+ if (charsBefore < 0 || charsAfter < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.mCharsBefore = charsBefore;
+ this.mCharsAfter = charsAfter;
+ this.mWord = word;
+ }
+ }
+
+ private static boolean isSeparator(int code, String sep) {
+ return sep.indexOf(code) != -1;
+ }
+
+ // Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor,
+ // n = 2 retrieves the word before that, and so on. This splits on whitespace only.
+ // Also, it won't return words that end in a separator (if the nth word before the cursor
+ // ends in a separator, it returns null).
+ // Example :
+ // (n = 1) "abc def|" -> def
+ // (n = 1) "abc def |" -> def
+ // (n = 1) "abc def. |" -> null
+ // (n = 1) "abc def . |" -> null
+ // (n = 2) "abc def|" -> abc
+ // (n = 2) "abc def |" -> abc
+ // (n = 2) "abc def. |" -> abc
+ // (n = 2) "abc def . |" -> def
+ // (n = 2) "abc|" -> null
+ // (n = 2) "abc |" -> null
+ // (n = 2) "abc. def|" -> null
+ public static CharSequence getNthPreviousWord(final CharSequence prev,
+ final String sentenceSeperators, final int n) {
+ if (prev == null) return null;
+ String[] w = spaceRegex.split(prev);
+
+ // If we can't find n words, or we found an empty word, return null.
+ if (w.length < n || w[w.length - n].length() <= 0) return null;
+
+ // If ends in a separator, return null
+ char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1);
+ if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
+
+ return w[w.length - n];
+ }
+
+ /**
+ * @param separators characters which may separate words
+ * @return the word that surrounds the cursor, including up to one trailing
+ * separator. For example, if the field contains "he|llo world", where |
+ * represents the cursor, then "hello " will be returned.
+ */
+ public String getWordAtCursor(String separators) {
+ // getWordRangeAtCursor returns null if the connection is null
+ Range r = getWordRangeAtCursor(separators, 0);
+ return (r == null) ? null : r.mWord;
+ }
+
+ private int getCursorPosition() {
+ mIC = mParent.getCurrentInputConnection();
+ if (null == mIC) return INVALID_CURSOR_POSITION;
+ final ExtractedText extracted = mIC.getExtractedText(new ExtractedTextRequest(), 0);
+ if (extracted == null) {
+ return INVALID_CURSOR_POSITION;
+ }
+ return extracted.startOffset + extracted.selectionStart;
+ }
+
+ /**
+ * Returns the text surrounding the cursor.
+ *
+ * @param sep a string of characters that split words.
+ * @param additionalPrecedingWordsCount the number of words before the current word that should
+ * be included in the returned range
+ * @return a range containing the text surrounding the cursor
+ */
+ public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) {
+ mIC = mParent.getCurrentInputConnection();
+ if (mIC == null || sep == null) {
+ return null;
+ }
+ CharSequence before = mIC.getTextBeforeCursor(1000, 0);
+ CharSequence after = mIC.getTextAfterCursor(1000, 0);
+ if (before == null || after == null) {
+ return null;
+ }
+
+ // Going backward, alternate skipping non-separators and separators until enough words
+ // have been read.
+ int start = before.length();
+ boolean isStoppingAtWhitespace = true; // toggles to indicate what to stop at
+ while (true) { // see comments below for why this is guaranteed to halt
+ while (start > 0) {
+ final int codePoint = Character.codePointBefore(before, start);
+ if (isStoppingAtWhitespace == isSeparator(codePoint, sep)) {
+ break; // inner loop
+ }
+ --start;
+ if (Character.isSupplementaryCodePoint(codePoint)) {
+ --start;
+ }
+ }
+ // isStoppingAtWhitespace is true every other time through the loop,
+ // so additionalPrecedingWordsCount is guaranteed to become < 0, which
+ // guarantees outer loop termination
+ if (isStoppingAtWhitespace && (--additionalPrecedingWordsCount < 0)) {
+ break; // outer loop
+ }
+ isStoppingAtWhitespace = !isStoppingAtWhitespace;
+ }
+
+ // Find last word separator after the cursor
+ int end = -1;
+ while (++end < after.length()) {
+ final int codePoint = Character.codePointAt(after, end);
+ if (isSeparator(codePoint, sep)) {
+ break;
+ }
+ if (Character.isSupplementaryCodePoint(codePoint)) {
+ ++end;
+ }
+ }
+
+ int cursor = getCursorPosition();
+ if (start >= 0 && cursor + end <= after.length() + before.length()) {
+ String word = before.toString().substring(start, before.length())
+ + after.toString().substring(0, end);
+ return new Range(before.length() - start, end, word);
+ }
+
+ return null;
+ }
+
+ public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
+ CharSequence before = getTextBeforeCursor(1, 0);
+ CharSequence after = getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0))
+ && !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
+ return true;
+ }
+ if (!TextUtils.isEmpty(after) && !settingsValues.isWordSeparator(after.charAt(0))
+ && !settingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
+ return true;
+ }
+ return false;
+ }
+
+ public void removeTrailingSpace() {
+ checkBatchEdit();
+ final CharSequence lastOne = getTextBeforeCursor(1, 0);
+ if (lastOne != null && lastOne.length() == 1
+ && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
+ deleteSurroundingText(1, 0);
+ }
+ }
+
+ public boolean sameAsTextBeforeCursor(final CharSequence text) {
+ final CharSequence beforeText = getTextBeforeCursor(text.length(), 0);
+ return TextUtils.equals(text, beforeText);
+ }
+
+ /* (non-javadoc)
+ * Returns the word before the cursor if the cursor is at the end of a word, null otherwise
+ */
+ public CharSequence getWordBeforeCursorIfAtEndOfWord(final SettingsValues settings) {
+ // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
+ // separator or end of line/text)
+ // Example: "test|"<EOL> "te|st" get rejected here
+ final CharSequence textAfterCursor = getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(textAfterCursor)
+ && !settings.isWordSeparator(textAfterCursor.charAt(0))) return null;
+
+ // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
+ // Example: " -|" gets rejected here but "e-|" and "e|" are okay
+ CharSequence word = getWordAtCursor(settings.mWordSeparators);
+ // We don't suggest on leading single quotes, so we have to remove them from the word if
+ // it starts with single quotes.
+ while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
+ word = word.subSequence(1, word.length());
+ }
+ if (TextUtils.isEmpty(word)) return null;
+ // Find the last code point of the string
+ final int lastCodePoint = Character.codePointBefore(word, word.length());
+ // If for some reason the text field contains non-unicode binary data, or if the
+ // charsequence is exactly one char long and the contents is a low surrogate, return null.
+ if (!Character.isDefined(lastCodePoint)) return null;
+ // Bail out if the cursor is not at the end of a word (cursor must be preceded by
+ // non-whitespace, non-separator, non-start-of-text)
+ // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
+ if (settings.isWordSeparator(lastCodePoint)) return null;
+ final char firstChar = word.charAt(0); // we just tested that word is not empty
+ if (word.length() == 1 && !Character.isLetter(firstChar)) return null;
+
+ // We only suggest on words that start with a letter or a symbol that is excluded from
+ // word separators (see #handleCharacterWhileInBatchEdit).
+ if (!(Character.isLetter(firstChar)
+ || settings.isSymbolExcludedFromWordSeparators(firstChar))) {
+ return null;
+ }
+
+ return word;
+ }
+
+ public boolean revertDoubleSpace() {
+ checkBatchEdit();
+ // Here we test whether we indeed have a period and a space before us. This should not
+ // be needed, but it's there just in case something went wrong.
+ final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
+ if (!". ".equals(textBeforeCursor)) {
+ // Theoretically we should not be coming here if there isn't ". " before the
+ // cursor, but the application may be changing the text while we are typing, so
+ // anything goes. We should not crash.
+ Log.d(TAG, "Tried to revert double-space combo but we didn't find "
+ + "\". \" just before the cursor.");
+ return false;
+ }
+ deleteSurroundingText(2, 0);
+ commitText(" ", 1);
+ return true;
+ }
+
+ public boolean revertSwapPunctuation() {
+ checkBatchEdit();
+ // Here we test whether we indeed have a space and something else before us. This should not
+ // be needed, but it's there just in case something went wrong.
+ final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
+ // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
+ // enter surrogate pairs this code will have been removed.
+ if (TextUtils.isEmpty(textBeforeCursor)
+ || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
+ // We may only come here if the application is changing the text while we are typing.
+ // This is quite a broken case, but not logically impossible, so we shouldn't crash,
+ // but some debugging log may be in order.
+ Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
+ + "find a space just before the cursor.");
+ return false;
+ }
+ deleteSurroundingText(2, 0);
+ commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
+ return true;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 4bb21720b..6251c9acd 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -39,6 +39,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import com.android.inputmethodcommon.InputMethodSettingsFragment;
public class Settings extends InputMethodSettingsFragment
@@ -61,6 +62,7 @@ public class Settings extends InputMethodSettingsFragment
public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME =
"last_user_dictionary_write_time";
public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
+ public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
public static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
"pref_suppress_language_switch_key";
public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
@@ -68,14 +70,15 @@ public class Settings extends InputMethodSettingsFragment
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
"pref_key_preview_popup_dismiss_delay";
- public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
- public static final String PREF_BIGRAM_SUGGESTION = "next_word_suggestion";
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
- public static final String PREF_KEY_ENABLE_SPAN_INSERT = "enable_span_insert";
+ public static final String PREF_GESTURE_INPUT = "gesture_input";
public static final String PREF_VIBRATION_DURATION_SETTINGS =
"pref_vibration_duration_settings";
public static final String PREF_KEYPRESS_SOUND_VOLUME =
"pref_keypress_sound_volume";
+ public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
+ public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
+ "pref_gesture_floating_preview_text";
public static final String PREF_INPUT_LANGUAGE = "input_language";
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
@@ -87,23 +90,24 @@ public class Settings extends InputMethodSettingsFragment
private ListPreference mShowCorrectionSuggestionsPreference;
private ListPreference mAutoCorrectionThresholdPreference;
private ListPreference mKeyPreviewPopupDismissDelay;
- // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
- private CheckBoxPreference mBigramSuggestion;
- // Prediction: use bigrams to predict the next word when there is no input for it yet
+ // Use bigrams to predict the next word when there is no input for it yet
private CheckBoxPreference mBigramPrediction;
private Preference mDebugSettingsPreference;
private TextView mKeypressVibrationDurationSettingsTextView;
private TextView mKeypressSoundVolumeSettingsTextView;
+ private static void setPreferenceEnabled(Preference preference, boolean enabled) {
+ if (preference != null) {
+ preference.setEnabled(enabled);
+ }
+ }
+
private void ensureConsistencyOfAutoCorrectionSettings() {
final String autoCorrectionOff = getResources().getString(
R.string.auto_correction_threshold_mode_index_off);
final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
- mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
- if (null != mBigramPrediction) {
- mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
- }
+ setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff));
}
@Override
@@ -128,7 +132,6 @@ public class Settings extends InputMethodSettingsFragment
mAutoCorrectionThresholdPreference =
(ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
- mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTION);
mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
if (mDebugSettingsPreference != null) {
@@ -155,9 +158,6 @@ public class Settings extends InputMethodSettingsFragment
final PreferenceGroup advancedSettings =
(PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
- // Remove those meaningless options for now. TODO: delete them for good
- advancedSettings.removePreference(findPreference(PREF_BIGRAM_SUGGESTION));
- advancedSettings.removePreference(findPreference(PREF_KEY_ENABLE_SPAN_INSERT));
if (!VibratorUtils.getInstance(context).hasVibrator()) {
generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
@@ -165,43 +165,35 @@ public class Settings extends InputMethodSettingsFragment
}
}
- final boolean showPopupOption = res.getBoolean(
+ final boolean showKeyPreviewPopupOption = res.getBoolean(
R.bool.config_enable_show_popup_on_keypress_option);
- if (!showPopupOption) {
+ mKeyPreviewPopupDismissDelay =
+ (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+ if (!showKeyPreviewPopupOption) {
generalSettings.removePreference(findPreference(PREF_POPUP_ON));
- }
-
- final boolean showBigramSuggestionsOption = res.getBoolean(
- R.bool.config_enable_next_word_suggestions_option);
- if (!showBigramSuggestionsOption) {
- textCorrectionGroup.removePreference(mBigramSuggestion);
- if (null != mBigramPrediction) {
- textCorrectionGroup.removePreference(mBigramPrediction);
+ if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+ advancedSettings.removePreference(mKeyPreviewPopupDismissDelay);
}
+ } else {
+ final String[] entries = new String[] {
+ res.getString(R.string.key_preview_popup_dismiss_no_delay),
+ res.getString(R.string.key_preview_popup_dismiss_default_delay),
+ };
+ final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
+ R.integer.config_key_preview_linger_timeout));
+ mKeyPreviewPopupDismissDelay.setEntries(entries);
+ mKeyPreviewPopupDismissDelay.setEntryValues(
+ new String[] { "0", popupDismissDelayDefaultValue });
+ if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+ mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
+ }
+ setPreferenceEnabled(mKeyPreviewPopupDismissDelay,
+ SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
}
- final CheckBoxPreference includeOtherImesInLanguageSwitchList =
- (CheckBoxPreference)findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
- includeOtherImesInLanguageSwitchList.setEnabled(
+ setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
!SettingsValues.isLanguageSwitchKeySupressed(prefs));
- mKeyPreviewPopupDismissDelay =
- (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- final String[] entries = new String[] {
- res.getString(R.string.key_preview_popup_dismiss_no_delay),
- res.getString(R.string.key_preview_popup_dismiss_default_delay),
- };
- final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
- R.integer.config_key_preview_linger_timeout));
- mKeyPreviewPopupDismissDelay.setEntries(entries);
- mKeyPreviewPopupDismissDelay.setEntryValues(
- new String[] { "0", popupDismissDelayDefaultValue });
- if (null == mKeyPreviewPopupDismissDelay.getValue()) {
- mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
- }
- mKeyPreviewPopupDismissDelay.setEnabled(
- SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
-
final PreferenceScreen dictionaryLink =
(PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
final Intent intent = dictionaryLink.getIntent();
@@ -211,6 +203,21 @@ public class Settings extends InputMethodSettingsFragment
textCorrectionGroup.removePreference(dictionaryLink);
}
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ final Preference gesturePreviewTrail = findPreference(PREF_GESTURE_PREVIEW_TRAIL);
+ final Preference gestureFloatingPreviewText = findPreference(
+ PREF_GESTURE_FLOATING_PREVIEW_TEXT);
+ if (!gestureInputEnabledByBuildConfig) {
+ miscSettings.removePreference(findPreference(PREF_GESTURE_INPUT));
+ miscSettings.removePreference(gesturePreviewTrail);
+ miscSettings.removePreference(gestureFloatingPreviewText);
+ } else {
+ final boolean gestureInputEnabledByUser = prefs.getBoolean(PREF_GESTURE_INPUT, true);
+ setPreferenceEnabled(gesturePreviewTrail, gestureInputEnabledByUser);
+ setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser);
+ }
+
final boolean showUsabilityStudyModeOption =
res.getBoolean(R.bool.config_enable_usability_study_mode_option)
|| ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS;
@@ -223,7 +230,8 @@ public class Settings extends InputMethodSettingsFragment
if (ProductionFlag.IS_EXPERIMENTAL) {
if (usabilityStudyPref instanceof CheckBoxPreference) {
CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
- checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true));
+ checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
+ ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
checkbox.setSummary(R.string.settings_warning_researcher_mode);
}
}
@@ -283,17 +291,22 @@ public class Settings extends InputMethodSettingsFragment
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
(new BackupManager(getActivity())).dataChanged();
if (key.equals(PREF_POPUP_ON)) {
- final ListPreference popupDismissDelay =
- (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- if (null != popupDismissDelay) {
- popupDismissDelay.setEnabled(prefs.getBoolean(PREF_POPUP_ON, true));
- }
+ setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY),
+ prefs.getBoolean(PREF_POPUP_ON, true));
} else if (key.equals(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
- final CheckBoxPreference includeOtherImesInLanguageSwicthList =
- (CheckBoxPreference)findPreference(
- PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
- includeOtherImesInLanguageSwicthList.setEnabled(
+ setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
!SettingsValues.isLanguageSwitchKeySupressed(prefs));
+ } else if (key.equals(PREF_GESTURE_INPUT)) {
+ final boolean gestureInputEnabledByConfig = getResources().getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ if (gestureInputEnabledByConfig) {
+ final boolean gestureInputEnabledByUser = prefs.getBoolean(
+ PREF_GESTURE_INPUT, true);
+ setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL),
+ gestureInputEnabledByUser);
+ setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT),
+ gestureInputEnabledByUser);
+ }
}
ensureConsistencyOfAutoCorrectionSettings();
updateVoiceModeSummary();
@@ -327,28 +340,32 @@ public class Settings extends InputMethodSettingsFragment
private void updateKeyPreviewPopupDelaySummary() {
final ListPreference lp = mKeyPreviewPopupDismissDelay;
- lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
+ final CharSequence[] entries = lp.getEntries();
+ if (entries == null || entries.length <= 0) return;
+ lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
}
private void updateVoiceModeSummary() {
mVoicePreference.setSummary(
getResources().getStringArray(R.array.voice_input_modes_summary)
- [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
+ [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
}
private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
SharedPreferences sp, Resources res) {
if (mKeypressVibrationDurationSettingsPref != null) {
- final boolean hasVibrator = VibratorUtils.getInstance(getActivity()).hasVibrator();
- final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON,
+ final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity())
+ .hasVibrator();
+ final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON,
res.getBoolean(R.bool.config_default_vibration_enabled));
- mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn);
+ setPreferenceEnabled(mKeypressVibrationDurationSettingsPref,
+ hasVibratorHardware && vibrateOnByUser);
}
if (mKeypressSoundVolumeSettingsPref != null) {
final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
res.getBoolean(R.bool.config_default_sound_enabled));
- mKeypressSoundVolumeSettingsPref.setEnabled(soundOn);
+ setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn);
}
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index b07c3e59f..dcd2532c1 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
@@ -29,7 +30,6 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Map;
/**
* When you call the constructor of this class, you may want to change the current system locale by
@@ -38,6 +38,19 @@ import java.util.Map;
public class SettingsValues {
private static final String TAG = SettingsValues.class.getSimpleName();
+ private static final int SUGGESTION_VISIBILITY_SHOW_VALUE
+ = R.string.prefs_suggestion_visibility_show_value;
+ private static final int SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
+ = R.string.prefs_suggestion_visibility_show_only_portrait_value;
+ private static final int SUGGESTION_VISIBILITY_HIDE_VALUE
+ = R.string.prefs_suggestion_visibility_hide_value;
+
+ private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
+ SUGGESTION_VISIBILITY_SHOW_VALUE,
+ SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE,
+ SUGGESTION_VISIBILITY_HIDE_VALUE
+ };
+
// From resources:
public final int mDelayUpdateOldSuggestions;
public final String mWeakSpaceStrippers;
@@ -63,27 +76,33 @@ public class SettingsValues {
@SuppressWarnings("unused") // TODO: Use this
private final String mKeyPreviewPopupDismissDelayRawValue;
public final boolean mUseContactsDict;
- // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
- public final boolean mBigramSuggestionEnabled;
- // Prediction: use bigrams to predict the next word when there is no input for it yet
+ // Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled;
- public final boolean mEnableSuggestionSpanInsertion;
@SuppressWarnings("unused") // TODO: Use this
private final int mVibrationDurationSettingsRawValue;
@SuppressWarnings("unused") // TODO: Use this
private final float mKeypressSoundVolumeRawValue;
private final InputMethodSubtype[] mAdditionalSubtypes;
+ public final boolean mGestureInputEnabled;
+ public final boolean mGesturePreviewTrailEnabled;
+ public final boolean mGestureFloatingPreviewTextEnabled;
+
+ // From the input box
+ private final InputAttributes mInputAttributes;
// Deduced settings
public final int mKeypressVibrationDuration;
public final float mFxVolume;
public final int mKeyPreviewPopupDismissDelay;
- public final boolean mAutoCorrectEnabled;
+ private final boolean mAutoCorrectEnabled;
public final float mAutoCorrectionThreshold;
+ public final boolean mCorrectionEnabled;
+ public final int mSuggestionVisibility;
private final boolean mVoiceKeyEnabled;
private final boolean mVoiceKeyOnMain;
- public SettingsValues(final SharedPreferences prefs, final Context context) {
+ public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes,
+ final Context context) {
final Resources res = context.getResources();
// Get the resources
@@ -109,6 +128,13 @@ public class SettingsValues {
mSymbolsExcludedFromWordSeparators, res);
mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
+ // Store the input attributes
+ if (null == inputAttributes) {
+ mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+ } else {
+ mInputAttributes = inputAttributes;
+ }
+
// Get the settings preferences
mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
mVibrateOn = isVibrateOn(context, prefs, res);
@@ -131,12 +157,7 @@ public class SettingsValues {
Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout)));
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
- mBigramSuggestionEnabled = mAutoCorrectEnabled
- && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
- mBigramPredictionEnabled = mBigramSuggestionEnabled
- && isBigramPredictionEnabled(prefs, res);
- // TODO: remove mEnableSuggestionSpanInsertion. It's always true.
- mEnableSuggestionSpanInsertion = true;
+ mBigramPredictionEnabled = isBigramPredictionEnabled(prefs, res);
mVibrationDurationSettingsRawValue =
prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
mKeypressSoundVolumeRawValue = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
@@ -151,22 +172,30 @@ public class SettingsValues {
mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray(
getPrefAdditionalSubtypes(prefs, res));
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ mGestureInputEnabled = gestureInputEnabledByBuildConfig
+ && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
+ mGesturePreviewTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
+ mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
+ Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
+ mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+ mSuggestionVisibility = createSuggestionVisibility(res);
}
// Helper functions to create member values.
private static SuggestedWords createSuggestPuncList(final String[] puncs) {
- final ArrayList<SuggestedWords.SuggestedWordInfo> puncList =
- new ArrayList<SuggestedWords.SuggestedWordInfo>();
+ final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList();
if (puncs != null) {
for (final String puncSpec : puncs) {
- puncList.add(new SuggestedWords.SuggestedWordInfo(
- KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE));
+ puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
+ Dictionary.TYPE_HARDCODED));
}
}
return new SuggestedWords(puncList,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
true /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -184,6 +213,16 @@ public class SettingsValues {
return wordSeparators;
}
+ private int createSuggestionVisibility(final Resources res) {
+ final String suggestionVisiblityStr = mShowSuggestionsSetting;
+ for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
+ if (suggestionVisiblityStr.equals(res.getString(visibility))) {
+ return visibility;
+ }
+ }
+ throw new RuntimeException("Bug: visibility string is not configured correctly");
+ }
+
private static boolean isVibrateOn(final Context context, final SharedPreferences prefs,
final Resources res) {
final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator();
@@ -191,6 +230,22 @@ public class SettingsValues {
res.getBoolean(R.bool.config_default_vibration_enabled));
}
+ public boolean isApplicationSpecifiedCompletionsOn() {
+ return mInputAttributes.mApplicationSpecifiedCompletionOn;
+ }
+
+ public boolean isSuggestionsRequested(final int displayOrientation) {
+ return mInputAttributes.mIsSettingsSuggestionStripOn
+ && (mCorrectionEnabled
+ || isSuggestionStripVisibleInOrientation(displayOrientation));
+ }
+
+ public boolean isSuggestionStripVisibleInOrientation(final int orientation) {
+ return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE)
+ || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
+ && orientation == Configuration.ORIENTATION_PORTRAIT);
+ }
+
public boolean isWordSeparator(int code) {
return mWordSeparators.contains(String.valueOf((char)code));
}
@@ -240,12 +295,6 @@ public class SettingsValues {
R.integer.config_key_preview_linger_timeout))));
}
- private static boolean isBigramSuggestionEnabled(final SharedPreferences sp,
- final Resources resources, final boolean autoCorrectEnabled) {
- // TODO: remove this method. Bigram suggestion is always true.
- return true;
- }
-
private static boolean isBigramPredictionEnabled(final SharedPreferences sp,
final Resources resources) {
return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean(
@@ -367,4 +416,13 @@ public class SettingsValues {
final String newStr = Utils.localeAndTimeHashMapToStr(map);
prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
}
+
+ public boolean isSameInputType(final EditorInfo editorInfo) {
+ return mInputAttributes.isSameInputType(editorInfo);
+ }
+
+ // For debug.
+ public String getInputAttributesDebugString() {
+ return mInputAttributes.toString();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index a43b90525..39c59b44c 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -53,7 +53,7 @@ public class StringUtils {
if (TextUtils.isEmpty(csv)) return "";
final String[] elements = csv.split(",");
if (!containsInArray(key, elements)) return csv;
- final ArrayList<String> result = new ArrayList<String>(elements.length - 1);
+ final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
for (final String element : elements) {
if (!key.equals(element)) result.add(element);
}
@@ -184,6 +184,9 @@ public class StringUtils {
final char[] characters = string.toCharArray();
final int length = characters.length;
final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ if (length <= 0) {
+ return new int[0];
+ }
int codePoint = Character.codePointAt(characters, 0);
int dsti = 0;
for (int srci = Character.charCount(codePoint);
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 21c9c0d1e..de5f515b0 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -45,13 +45,13 @@ public class SubtypeLocale {
private static String[] sPredefinedKeyboardLayoutSet;
// Keyboard layout to its display name map.
private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
- new HashMap<String, String>();
+ CollectionUtils.newHashMap();
// Keyboard layout to subtype name resource id map.
private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
- new HashMap<String, Integer>();
+ CollectionUtils.newHashMap();
// Exceptional locale to subtype name resource id map.
private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
- new HashMap<String, Integer>();
+ CollectionUtils.newHashMap();
private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
"string/subtype_generic_";
private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX =
@@ -60,11 +60,11 @@ public class SubtypeLocale {
"string/subtype_no_language_";
// Exceptional locales to display name map.
private static final HashMap<String, String> sExceptionalDisplayNamesMap =
- new HashMap<String, String>();
+ CollectionUtils.newHashMap();
// Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
// This is for compatibility to keep the same subtype ids as pre-JellyBean.
private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
- new HashMap<String,String>();
+ CollectionUtils.newHashMap();
private SubtypeLocale() {
// Intentional empty constructor for utility class.
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 664de6774..c693edcca 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
@@ -42,7 +43,6 @@ public class SubtypeSwitcher {
private static final String TAG = SubtypeSwitcher.class.getSimpleName();
private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
- private /* final */ LatinIME mService;
private /* final */ InputMethodManager mImm;
private /* final */ Resources mResources;
private /* final */ ConnectivityManager mConnectivityManager;
@@ -68,11 +68,11 @@ public class SubtypeSwitcher {
return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
}
- public void updateEnabledSubtypeCount(int count) {
+ public void updateEnabledSubtypeCount(final int count) {
mEnabledSubtypeCount = count;
}
- public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) {
+ public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
mIsSystemLanguageSameAsInputLanguage = isSame;
}
}
@@ -81,26 +81,25 @@ public class SubtypeSwitcher {
return sInstance;
}
- public static void init(LatinIME service) {
- SubtypeLocale.init(service);
- sInstance.initialize(service);
- sInstance.updateAllParameters();
+ public static void init(final Context context) {
+ SubtypeLocale.init(context);
+ sInstance.initialize(context);
+ sInstance.updateAllParameters(context);
}
private SubtypeSwitcher() {
// Intentional empty constructor for singleton.
}
- private void initialize(LatinIME service) {
- mService = service;
+ private void initialize(final Context service) {
mResources = service.getResources();
mImm = ImfUtils.getInputMethodManager(service);
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
mCurrentSystemLocale = mResources.getConfiguration().locale;
- mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
+ mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype);
if (mNoLanguageSubtype == null) {
throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
}
@@ -111,39 +110,46 @@ public class SubtypeSwitcher {
// Update all parameters stored in SubtypeSwitcher.
// Only configuration changed event is allowed to call this because this is heavy.
- private void updateAllParameters() {
+ private void updateAllParameters(final Context context) {
mCurrentSystemLocale = mResources.getConfiguration().locale;
- updateSubtype(mImm.getCurrentInputMethodSubtype());
- updateParametersOnStartInputView();
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype));
+ updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
}
- // Update parameters which are changed outside LatinIME. This parameters affect UI so they
- // should be updated every time onStartInputview.
- public void updateParametersOnStartInputView() {
- updateEnabledSubtypes();
+ /**
+ * Update parameters which are changed outside LatinIME. This parameters affect UI so they
+ * should be updated every time onStartInputView.
+ *
+ * @return true if the current subtype is enabled.
+ */
+ public boolean updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled() {
+ final boolean currentSubtypeEnabled =
+ updateEnabledSubtypesAndReturnIfEnabled(mCurrentSubtype);
updateShortcutIME();
+ return currentSubtypeEnabled;
}
- // Reload enabledSubtypes from the framework.
- private void updateEnabledSubtypes() {
- final InputMethodSubtype currentSubtype = mCurrentSubtype;
- boolean foundCurrentSubtypeBecameDisabled = true;
+ /**
+ * Update enabled subtypes from the framework.
+ *
+ * @param subtype the subtype to be checked
+ * @return true if the {@code subtype} is enabled.
+ */
+ private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) {
final List<InputMethodSubtype> enabledSubtypesOfThisIme =
mImm.getEnabledInputMethodSubtypeList(null, true);
- for (InputMethodSubtype ims : enabledSubtypesOfThisIme) {
- if (ims.equals(currentSubtype)) {
- foundCurrentSubtypeBecameDisabled = false;
- }
- }
mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
- if (foundCurrentSubtypeBecameDisabled) {
- if (DBG) {
- Log.w(TAG, "Last subtype: "
- + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
- Log.w(TAG, "Last subtype was disabled. Update to the current one.");
+
+ for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) {
+ if (ims.equals(subtype)) {
+ return true;
}
- updateSubtype(mImm.getCurrentInputMethodSubtype());
}
+ if (DBG) {
+ Log.w(TAG, "Subtype: " + subtype.getLocale() + "/" + subtype.getExtraValue()
+ + " was disabled");
+ }
+ return false;
}
private void updateShortcutIME() {
@@ -159,8 +165,8 @@ public class SubtypeSwitcher {
mImm.getShortcutInputMethodsAndSubtypes();
mShortcutInputMethodInfo = null;
mShortcutSubtype = null;
- for (InputMethodInfo imi : shortcuts.keySet()) {
- List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+ for (final InputMethodInfo imi : shortcuts.keySet()) {
+ final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
// TODO: Returns the first found IMI for now. Should handle all shortcuts as
// appropriate.
mShortcutInputMethodInfo = imi;
@@ -194,24 +200,24 @@ public class SubtypeSwitcher {
mCurrentSubtype = newSubtype;
updateShortcutIME();
- mService.onRefreshKeyboard();
}
////////////////////////////
// Shortcut IME functions //
////////////////////////////
- public void switchToShortcutIME() {
+ public void switchToShortcutIME(final InputMethodService context) {
if (mShortcutInputMethodInfo == null) {
return;
}
final String imiId = mShortcutInputMethodInfo.getId();
- switchToTargetIME(imiId, mShortcutSubtype);
+ switchToTargetIME(imiId, mShortcutSubtype, context);
}
- private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) {
- final IBinder token = mService.getWindow().getWindow().getAttributes().token;
+ private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
+ final InputMethodService context) {
+ final IBinder token = context.getWindow().getWindow().getAttributes().token;
if (token == null) {
return;
}
@@ -253,7 +259,7 @@ public class SubtypeSwitcher {
return true;
}
- public void onNetworkStateChanged(Intent intent) {
+ public void onNetworkStateChanged(final Intent intent) {
final boolean noConnection = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
mIsNetworkConnected = !noConnection;
@@ -265,7 +271,7 @@ public class SubtypeSwitcher {
// Subtype Switching functions //
//////////////////////////////////
- public boolean needsToDisplayLanguage(Locale keyboardLocale) {
+ public boolean needsToDisplayLanguage(final Locale keyboardLocale) {
if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) {
return true;
}
@@ -279,12 +285,14 @@ public class SubtypeSwitcher {
return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
}
- public void onConfigurationChanged(Configuration conf) {
+ public boolean onConfigurationChanged(final Configuration conf, final Context context) {
final Locale systemLocale = conf.locale;
+ final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale);
// If system configuration was changed, update all parameters.
- if (!systemLocale.equals(mCurrentSystemLocale)) {
- updateAllParameters();
+ if (systemLocaleChanged) {
+ updateAllParameters(context);
}
+ return systemLocaleChanged;
}
public InputMethodSubtype getCurrentSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 336a76f4b..51ed09604 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
-import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
@@ -26,6 +25,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.io.File;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
@@ -34,87 +34,50 @@ import java.util.concurrent.ConcurrentHashMap;
* This class loads a dictionary and provides a list of suggestions for a given sequence of
* characters. This includes corrections and completions.
*/
-public class Suggest implements Dictionary.WordCallback {
+public class Suggest {
public static final String TAG = Suggest.class.getSimpleName();
- public static final int APPROX_MAX_WORD_LENGTH = 32;
-
+ // 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 CORRECTION_FULL_BIGRAM = 2;
-
- // It seems the following values are only used for logging.
- public static final int DIC_USER_TYPED = 0;
- public static final int DIC_MAIN = 1;
- public static final int DIC_USER = 2;
- public static final int DIC_USER_HISTORY = 3;
- public static final int DIC_CONTACTS = 4;
- public static final int DIC_WHITELIST = 6;
- // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
- // TODO: this value seems unused. Remove it?
- public static final int DIC_TYPE_LAST_ID = 6;
- public static final String DICT_KEY_MAIN = "main";
- public static final String DICT_KEY_CONTACTS = "contacts";
- // User dictionary, the system-managed one.
- public static final String DICT_KEY_USER = "user";
- // User history dictionary for the unigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram";
- // User history dictionary for the bigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram";
- public static final String DICT_KEY_WHITELIST ="whitelist";
- private static final boolean DBG = LatinImeLogger.sDBG;
+ public interface SuggestInitializationListener {
+ public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
+ }
- private boolean mHasMainDictionary;
- private Dictionary mContactsDict;
- private WhitelistDictionary mWhiteListDictionary;
- private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
- private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
+ private static final boolean DBG = LatinImeLogger.sDBG;
- private int mPrefMaxSuggestions = 18;
+ private Dictionary mMainDictionary;
+ private ContactsBinaryDictionary mContactsDict;
+ private final ConcurrentHashMap<String, Dictionary> mDictionaries =
+ CollectionUtils.newConcurrentHashMap();
- private static final int PREF_MAX_BIGRAMS = 60;
+ public static final int MAX_SUGGESTIONS = 18;
private float mAutoCorrectionThreshold;
- private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>();
- private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>();
- private CharSequence mConsideredWord;
-
- // TODO: Remove these member variables by passing more context to addWord() callback method
- private boolean mIsFirstCharCapitalized;
- private boolean mIsAllUpperCase;
- private int mTrailingSingleQuotesCount;
-
- private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
+ // Locale used for upper- and title-casing words
+ private final Locale mLocale;
- public Suggest(final Context context, final Locale locale) {
- initAsynchronously(context, locale);
+ public Suggest(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ initAsynchronously(context, locale, listener);
+ mLocale = locale;
}
/* package for test */ Suggest(final Context context, final File dictionary,
final long startOffset, final long length, final Locale locale) {
final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary,
startOffset, length /* useFullEditDistance */, false, locale);
- mHasMainDictionary = null != mainDict;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
- initWhitelistAndAutocorrectAndPool(context, locale);
+ mLocale = locale;
+ mMainDictionary = mainDict;
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
}
- private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
- mWhiteListDictionary = new WhitelistDictionary(context, locale);
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
- }
-
- private void initAsynchronously(final Context context, final Locale locale) {
- resetMainDict(context, locale);
-
- // TODO: read the whitelist and init the pool asynchronously too.
- // initPool should be done asynchronously now that the pool is thread-safe.
- initWhitelistAndAutocorrectAndPool(context, locale);
+ private void initAsynchronously(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ resetMainDict(context, locale, listener);
}
private static void addOrReplaceDictionary(
@@ -128,16 +91,22 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- public void resetMainDict(final Context context, final Locale locale) {
- mHasMainDictionary = false;
+ public void resetMainDict(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ mMainDictionary = null;
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ }
new Thread("InitializeBinaryDictionary") {
@Override
public void run() {
final DictionaryCollection newMainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
- mHasMainDictionary = null != newMainDict && !newMainDict.isEmpty();
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict);
+ mMainDictionary = newMainDict;
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ }
}
}.start();
}
@@ -145,27 +114,27 @@ public class Suggest implements Dictionary.WordCallback {
// The main dictionary could have been loaded asynchronously. Don't cache the return value
// of this method.
public boolean hasMainDictionary() {
- return mHasMainDictionary;
+ return null != mMainDictionary && mMainDictionary.isInitialized();
}
- public Dictionary getContactsDictionary() {
- return mContactsDict;
+ public Dictionary getMainDictionary() {
+ return mMainDictionary;
}
- public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
- return mUnigramDictionaries;
+ public ContactsBinaryDictionary getContactsDictionary() {
+ return mContactsDict;
}
- public static int getApproxMaxWordLength() {
- return APPROX_MAX_WORD_LENGTH;
+ public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
+ return mDictionaries;
}
/**
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
- public void setUserDictionary(Dictionary userDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
+ public void setUserDictionary(UserBinaryDictionary userDictionary) {
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
}
/**
@@ -173,236 +142,194 @@ public class Suggest implements Dictionary.WordCallback {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
- public void setContactsDictionary(Dictionary contactsDictionary) {
+ public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
}
- public void setUserHistoryDictionary(Dictionary userHistoryDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
- userHistoryDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
- userHistoryDictionary);
+ public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
public void setAutoCorrectionThreshold(float threshold) {
mAutoCorrectionThreshold = threshold;
}
- private static CharSequence capitalizeWord(final boolean all, final boolean first,
- final CharSequence word) {
- if (TextUtils.isEmpty(word) || !(all || first)) return word;
- final int wordLength = word.length();
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (all) {
- sb.append(word.toString().toUpperCase());
- } else if (first) {
- sb.append(Character.toUpperCase(word.charAt(0)));
- if (wordLength > 1) {
- sb.append(word.subSequence(1, wordLength));
- }
- }
- return sb;
- }
-
- protected void addBigramToSuggestions(SuggestedWordInfo bigram) {
- mSuggestions.add(bigram);
+ public SuggestedWords getSuggestedWords(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ return getSuggestedWordsWithSessionId(
+ wordComposer, prevWordForBigram, proximityInfo, isCorrectionEnabled, 0);
}
- private static final WordComposer sEmptyWordComposer = new WordComposer();
- public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) {
+ public SuggestedWords getSuggestedWordsWithSessionId(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = false;
- mIsAllUpperCase = false;
- mTrailingSingleQuotesCount = 0;
- mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
-
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = "";
-
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
- getAllBigrams(prevWordForBigram, sEmptyWordComposer);
-
- // Nothing entered: return all bigrams for the previous word
- int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
- for (int i = 0; i < insertCount; ++i) {
- addBigramToSuggestions(mBigramSuggestions.get(i));
+ if (wordComposer.isBatchMode()) {
+ return getSuggestedWordsForBatchInput(
+ wordComposer, prevWordForBigram, proximityInfo, sessionId);
+ } else {
+ return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
+ isCorrectionEnabled);
}
-
- SuggestedWordInfo.removeDups(mSuggestions);
-
- return new SuggestedWords(mSuggestions,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
- false /* isPunctuationSuggestions */,
- false /* isObsoleteSuggestions */,
- true /* isPrediction */);
}
- // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
- public SuggestedWords getSuggestedWords(
+ // Retrieves suggestions for the typing input.
+ private SuggestedWords getSuggestedWordsForTypingInput(
final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final int correctionMode) {
- LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
- mIsAllUpperCase = wordComposer.isAllUpperCase();
- mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
- mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
+ final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
+ MAX_SUGGESTIONS);
final String typedWord = wordComposer.getTypedWord();
- final String consideredWord = mTrailingSingleQuotesCount > 0
- ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount)
+ final String consideredWord = trailingSingleQuotesCount > 0
+ ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
: typedWord;
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = consideredWord;
-
- if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
- // At first character typed, search only the bigrams
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
- if (!TextUtils.isEmpty(prevWordForBigram)) {
- getAllBigrams(prevWordForBigram, wordComposer);
- if (TextUtils.isEmpty(consideredWord)) {
- // Nothing entered: return all bigrams for the previous word
- int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
- for (int i = 0; i < insertCount; ++i) {
- addBigramToSuggestions(mBigramSuggestions.get(i));
- }
- } else {
- // Word entered: return only bigrams that match the first char of the typed word
- final char currentChar = consideredWord.charAt(0);
- // TODO: Must pay attention to locale when changing case.
- // TODO: Use codepoint instead of char
- final char currentCharUpper = Character.toUpperCase(currentChar);
- int count = 0;
- final int bigramSuggestionSize = mBigramSuggestions.size();
- for (int i = 0; i < bigramSuggestionSize; i++) {
- final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i);
- final char bigramSuggestionFirstChar =
- (char)bigramSuggestion.codePointAt(0);
- if (bigramSuggestionFirstChar == currentChar
- || bigramSuggestionFirstChar == currentCharUpper) {
- addBigramToSuggestions(bigramSuggestion);
- if (++count > mPrefMaxSuggestions) break;
- }
- }
- }
- }
+ LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
- } else if (wordComposer.size() > 1) {
- final WordComposer wordComposerForLookup;
- if (mTrailingSingleQuotesCount > 0) {
- wordComposerForLookup = new WordComposer(wordComposer);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
- wordComposerForLookup.deleteLast();
- }
- } else {
- wordComposerForLookup = wordComposer;
- }
- // At second character typed, search the unigrams (scores being affected by bigrams)
- for (final String key : mUnigramDictionaries.keySet()) {
- // Skip UserUnigramDictionary and WhitelistDictionary to lookup
- if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
- continue;
- final Dictionary dictionary = mUnigramDictionaries.get(key);
- dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo);
+ final WordComposer wordComposerForLookup;
+ if (trailingSingleQuotesCount > 0) {
+ wordComposerForLookup = new WordComposer(wordComposer);
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+ wordComposerForLookup.deleteLast();
}
+ } else {
+ wordComposerForLookup = wordComposer;
}
- final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase,
- mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord));
+ for (final String key : mDictionaries.keySet()) {
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestions(
+ wordComposerForLookup, prevWordForBigram, proximityInfo));
+ }
- final boolean hasAutoCorrection;
- if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) {
- final CharSequence autoCorrection =
- AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
- mSuggestions, consideredWord, mAutoCorrectionThreshold,
- whitelistedWord);
- hasAutoCorrection = (null != autoCorrection);
+ final CharSequence whitelistedWord;
+ if (suggestionsSet.isEmpty()) {
+ whitelistedWord = null;
+ } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
+ whitelistedWord = null;
} else {
+ whitelistedWord = suggestionsSet.first().mWord;
+ }
+
+ final boolean allowsToBeAutoCorrected = (null != whitelistedWord
+ && !whitelistedWord.equals(consideredWord))
+ || AutoCorrection.isNotAWord(mDictionaries, consideredWord,
+ wordComposer.isFirstCharCapitalized());
+
+ final boolean hasAutoCorrection;
+ // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
+ // any attempt to do auto-correction is already shielded with a test for this flag; at the
+ // same time, it feels wrong that the SuggestedWord object includes information about
+ // the current settings. It may also be useful to know, when the setting is off, whether
+ // the word *would* have been auto-corrected.
+ if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord()
+ || suggestionsSet.isEmpty() || wordComposer.hasDigits()
+ || wordComposer.isMostlyCaps() || wordComposer.isResumed()
+ || !hasMainDictionary()) {
+ // If we don't have a main dictionary, we never want to auto-correct. The reason for
+ // this is, the user may have a contact whose name happens to match a valid word in
+ // their language, and it will unexpectedly auto-correct. For example, if the user
+ // types in English with no dictionary and has a "Will" in their contact list, "will"
+ // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
+ // auto-correct.
hasAutoCorrection = false;
+ } else {
+ hasAutoCorrection = AutoCorrection.suggestionExceedsAutoCorrectionThreshold(
+ suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold);
}
- if (whitelistedWord != null) {
- if (mTrailingSingleQuotesCount > 0) {
- final StringBuilder sb = new StringBuilder(whitelistedWord);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
- sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
- }
- mSuggestions.add(0, new SuggestedWordInfo(
- sb.toString(), SuggestedWordInfo.MAX_SCORE));
- } else {
- mSuggestions.add(0, new SuggestedWordInfo(
- whitelistedWord, SuggestedWordInfo.MAX_SCORE));
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ CollectionUtils.newArrayList(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+ final boolean isAllUpperCase = wordComposer.isAllUpperCase();
+ if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
+ wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
+ trailingSingleQuotesCount);
+ suggestionsContainer.set(i, transformedWordInfo);
}
}
- mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
- SuggestedWordInfo.removeDups(mSuggestions);
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+ }
+
+ if (!TextUtils.isEmpty(typedWord)) {
+ suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
+ Dictionary.TYPE_USER_TYPED));
+ }
+ SuggestedWordInfo.removeDups(suggestionsContainer);
final ArrayList<SuggestedWordInfo> suggestionsList;
- if (DBG) {
- suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
+ if (DBG && !suggestionsContainer.isEmpty()) {
+ suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer);
} else {
- suggestionsList = mSuggestions;
+ suggestionsList = suggestionsContainer;
}
- // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
- // but still autocorrected from - in the case the whitelist only capitalizes the word.
- // The whitelist should be case-insensitive, so it's not possible to be consistent with
- // a boolean flag. Right now this is handled with a slight hack in
- // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
- final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
- getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized())
- // If we don't have a main dictionary, we never want to auto-correct. The reason for this
- // is, the user may have a contact whose name happens to match a valid word in their
- // language, and it will unexpectedly auto-correct. For example, if the user types in
- // English with no dictionary and has a "Will" in their contact list, "will" would
- // always auto-correct to "Will" which is unwanted. Hence, no main dict => no auto-correct.
- && mHasMainDictionary;
-
- boolean autoCorrectionAvailable = hasAutoCorrection;
- if (correctionMode == CORRECTION_FULL || correctionMode == CORRECTION_FULL_BIGRAM) {
- autoCorrectionAvailable |= !allowsToBeAutoCorrected;
- }
- // Don't auto-correct words with multiple capital letter
- autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
- autoCorrectionAvailable &= !wordComposer.isResumed();
- if (allowsToBeAutoCorrected && suggestionsList.size() > 1 && mAutoCorrectionThreshold > 0
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(typedWord,
- suggestionsList.get(1).mWord)) {
- autoCorrectionAvailable = false;
- }
return new SuggestedWords(suggestionsList,
+ // TODO: this first argument is lying. If this is a whitelisted word which is an
+ // actual word, it says typedWordValid = false, which looks wrong. We should either
+ // rename the attribute or change the value.
!allowsToBeAutoCorrected /* typedWordValid */,
- autoCorrectionAvailable /* hasAutoCorrectionCandidate */,
- allowsToBeAutoCorrected /* allowsToBeAutoCorrected */,
+ hasAutoCorrection, /* willAutoCorrect */
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
- false /* isPrediction */);
+ !wordComposer.isComposingWord() /* isPrediction */);
}
- /**
- * Adds all bigram predictions for prevWord. Also checks the lower case version of prevWord if
- * it contains any upper case characters.
- */
- private void getAllBigrams(final CharSequence prevWord, final WordComposer wordComposer) {
- if (StringUtils.hasUpperCase(prevWord)) {
- // TODO: Must pay attention to locale when changing case.
- final CharSequence lowerPrevWord = prevWord.toString().toLowerCase();
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, lowerPrevWord, this);
+ // Retrieves suggestions for the batch input.
+ private SuggestedWords getSuggestedWordsForBatchInput(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, int sessionId) {
+ final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
+ MAX_SUGGESTIONS);
+
+ // At second character typed, search the unigrams (scores being affected by bigrams)
+ for (final String key : mDictionaries.keySet()) {
+ // Skip User history dictionary for lookup
+ // TODO: The user history dictionary should just override getSuggestionsWithSessionId
+ // to make sure it doesn't return anything and we should remove this test
+ if (key.equals(Dictionary.TYPE_USER_HISTORY)) {
+ continue;
}
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(
+ wordComposer, prevWordForBigram, proximityInfo, sessionId));
}
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, prevWord, this);
+
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ CollectionUtils.newArrayList(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
+ final boolean isAllUpperCase = wordComposer.isAllUpperCase();
+ if (isFirstCharCapitalized || isAllUpperCase) {
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
+ wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
+ 0 /* trailingSingleQuotesCount */);
+ suggestionsContainer.set(i, transformedWordInfo);
+ }
}
+
+ SuggestedWordInfo.removeDups(suggestionsContainer);
+ // 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).
+ return new SuggestedWords(suggestionsContainer,
+ true /* typedWordValid */,
+ false /* willAutoCorrect */,
+ false /* isPunctuationSuggestions */,
+ false /* isObsoleteSuggestions */,
+ false /* isPrediction */);
}
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
@@ -411,7 +338,7 @@ public class Suggest implements Dictionary.WordCallback {
typedWordInfo.setDebugString("+");
final int suggestionsSize = suggestions.size();
final ArrayList<SuggestedWordInfo> suggestionsList =
- new ArrayList<SuggestedWordInfo>(suggestionsSize);
+ CollectionUtils.newArrayList(suggestionsSize);
suggestionsList.add(typedWordInfo);
// Note: i here is the index in mScores[], but the index in mSuggestions is one more
// than i because we added the typed word to mSuggestions without touching mScores.
@@ -431,119 +358,44 @@ public class Suggest implements Dictionary.WordCallback {
return suggestionsList;
}
- // TODO: Use codepoint instead of char
- @Override
- public boolean addWord(final char[] word, final int offset, final int length, int score,
- final int dicTypeId, final int dataType) {
- int dataTypeForLog = dataType;
- final ArrayList<SuggestedWordInfo> suggestions;
- final int prefMaxSuggestions;
- if (dataType == Dictionary.BIGRAM) {
- suggestions = mBigramSuggestions;
- prefMaxSuggestions = PREF_MAX_BIGRAMS;
- } else {
- suggestions = mSuggestions;
- prefMaxSuggestions = mPrefMaxSuggestions;
+ private static class SuggestedWordInfoComparator implements Comparator<SuggestedWordInfo> {
+ // This comparator ranks the word info with the higher frequency first. That's because
+ // that's the order we want our elements in.
+ @Override
+ public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
+ if (o1.mScore > o2.mScore) return -1;
+ if (o1.mScore < o2.mScore) return 1;
+ if (o1.mCodePointCount < o2.mCodePointCount) return -1;
+ if (o1.mCodePointCount > o2.mCodePointCount) return 1;
+ return o1.mWord.toString().compareTo(o2.mWord.toString());
}
-
- int pos = 0;
-
- // Check if it's the same word, only caps are different
- if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) {
- // TODO: remove this surrounding if clause and move this logic to
- // getSuggestedWordBuilder.
- if (suggestions.size() > 0) {
- final SuggestedWordInfo currentHighestWord = suggestions.get(0);
- // If the current highest word is also equal to typed word, we need to compare
- // frequency to determine the insertion position. This does not ensure strictly
- // correct ordering, but ensures the top score is on top which is enough for
- // removing duplicates correctly.
- if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length)
- && score <= currentHighestWord.mScore) {
- pos = 1;
- }
- }
- } else {
- // Check the last one's score and bail
- if (suggestions.size() >= prefMaxSuggestions
- && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true;
- while (pos < suggestions.size()) {
- final int curScore = suggestions.get(pos).mScore;
- if (curScore < score
- || (curScore == score && length < suggestions.get(pos).codePointCount())) {
- break;
- }
- pos++;
- }
- }
- if (pos >= prefMaxSuggestions) {
- return true;
- }
-
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (mIsAllUpperCase) {
- sb.append(new String(word, offset, length).toUpperCase());
- } else if (mIsFirstCharCapitalized) {
- sb.append(Character.toUpperCase(word[offset]));
- if (length > 1) {
- sb.append(word, offset + 1, length - 1);
- }
+ }
+ private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
+ new SuggestedWordInfoComparator();
+
+ private static SuggestedWordInfo getTransformedSuggestedWordInfo(
+ final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
+ final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
+ final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
+ if (isAllUpperCase) {
+ sb.append(wordInfo.mWord.toString().toUpperCase(locale));
+ } else if (isFirstCharCapitalized) {
+ sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale));
} else {
- sb.append(word, offset, length);
+ sb.append(wordInfo.mWord);
}
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
}
- suggestions.add(pos, new SuggestedWordInfo(sb, score));
- if (suggestions.size() > prefMaxSuggestions) {
- suggestions.remove(prefMaxSuggestions);
- } else {
- LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
- }
- return true;
+ return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
}
public void close() {
- final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>();
- dictionaries.addAll(mUnigramDictionaries.values());
- dictionaries.addAll(mBigramDictionaries.values());
+ final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet();
+ dictionaries.addAll(mDictionaries.values());
for (final Dictionary dictionary : dictionaries) {
dictionary.close();
}
- mHasMainDictionary = false;
- }
-
- // TODO: Resolve the inconsistencies between the native auto correction algorithms and
- // this safety net
- public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final CharSequence suggestion) {
- // Safety net for auto correction.
- // Actually if we hit this safety net, it's a bug.
- // If user selected aggressive auto correction mode, there is no need to use the safety
- // net.
- // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
- // we should not use net because relatively edit distance can be big.
- final int typedWordLength = typedWord.length();
- if (typedWordLength < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) {
- return false;
- }
- final int maxEditDistanceOfNativeDictionary =
- (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
- final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
- if (DBG) {
- Log.d(TAG, "Autocorrected edit distance = " + distance
- + ", " + maxEditDistanceOfNativeDictionary);
- }
- if (distance > maxEditDistanceOfNativeDictionary) {
- if (DBG) {
- Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
- Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
- + "Turning off auto-correction.");
- }
- return true;
- } else {
- return false;
- }
+ mMainDictionary = null;
}
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 497fd3bfa..68ecfa0d7 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -24,28 +24,30 @@ import java.util.Arrays;
import java.util.HashSet;
public class SuggestedWords {
+ private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST =
+ CollectionUtils.newArrayList(0);
public static final SuggestedWords EMPTY = new SuggestedWords(
- new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false);
+ EMPTY_WORD_INFO_LIST, false, false, false, false, false);
public final boolean mTypedWordValid;
- public final boolean mHasAutoCorrectionCandidate;
+ // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition
+ // of what this flag means would be "the top suggestion is strong enough to auto-correct",
+ // whether this exactly matches the user entry or not.
+ public final boolean mWillAutoCorrect;
public final boolean mIsPunctuationSuggestions;
- public final boolean mAllowsToBeAutoCorrected;
public final boolean mIsObsoleteSuggestions;
public final boolean mIsPrediction;
private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
final boolean typedWordValid,
- final boolean hasAutoCorrectionCandidate,
- final boolean allowsToBeAutoCorrected,
+ final boolean willAutoCorrect,
final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions,
final boolean isPrediction) {
mSuggestedWordInfoList = suggestedWordInfoList;
mTypedWordValid = typedWordValid;
- mHasAutoCorrectionCandidate = hasAutoCorrectionCandidate;
- mAllowsToBeAutoCorrected = allowsToBeAutoCorrected;
+ mWillAutoCorrect = willAutoCorrect;
mIsPunctuationSuggestions = isPunctuationSuggestions;
mIsObsoleteSuggestions = isObsoleteSuggestions;
mIsPrediction = isPrediction;
@@ -55,7 +57,7 @@ public class SuggestedWords {
return mSuggestedWordInfoList.size();
}
- public CharSequence getWord(int pos) {
+ public String getWord(int pos) {
return mSuggestedWordInfoList.get(pos).mWord;
}
@@ -67,12 +69,8 @@ public class SuggestedWords {
return mSuggestedWordInfoList.get(pos);
}
- public boolean hasAutoCorrectionWord() {
- return mHasAutoCorrectionCandidate && size() > 1 && !mTypedWordValid;
- }
-
public boolean willAutoCorrect() {
- return !mTypedWordValid && mHasAutoCorrectionCandidate;
+ return mWillAutoCorrect;
}
@Override
@@ -80,18 +78,18 @@ public class SuggestedWords {
// Pretty-print method to help debug
return "SuggestedWords:"
+ " mTypedWordValid=" + mTypedWordValid
- + " mHasAutoCorrectionCandidate=" + mHasAutoCorrectionCandidate
- + " mAllowsToBeAutoCorrected=" + mAllowsToBeAutoCorrected
+ + " mWillAutoCorrect=" + mWillAutoCorrect
+ " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
}
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) {
- final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
+ final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList();
for (CompletionInfo info : infos) {
if (null != info && info.getText() != null) {
- result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE));
+ result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED));
}
}
return result;
@@ -101,9 +99,10 @@ public class SuggestedWords {
// and replace it with what the user currently typed.
public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
final CharSequence typedWord, final SuggestedWords previousSuggestions) {
- final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>();
- final HashSet<String> alreadySeen = new HashSet<String>();
- suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
+ final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
+ final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
+ suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED));
alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
@@ -120,17 +119,29 @@ public class SuggestedWords {
public static class SuggestedWordInfo {
public static final int MAX_SCORE = Integer.MAX_VALUE;
- private final String mWordStr;
- public final CharSequence mWord;
+ public static final int KIND_TYPED = 0; // What user typed
+ public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
+ public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
+ public static final int KIND_WHITELIST = 3; // Whitelisted word
+ public static final int KIND_BLACKLIST = 4; // Blacklisted word
+ public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
+ public static final int KIND_APP_DEFINED = 6; // Suggested by the application
+ public static final int KIND_SHORTCUT = 7; // A shortcut
+ public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
+ public final String mWord;
public final int mScore;
+ public final int mKind; // one of the KIND_* constants above
public final int mCodePointCount;
+ public final String mSourceDict;
private String mDebugString = "";
- public SuggestedWordInfo(final CharSequence word, final int score) {
- mWordStr = word.toString();
- mWord = word;
+ public SuggestedWordInfo(final CharSequence word, final int score, final int kind,
+ final String sourceDict) {
+ mWord = word.toString();
mScore = score;
- mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
+ mKind = kind;
+ mSourceDict = sourceDict;
+ mCodePointCount = StringUtils.codePointCount(mWord);
}
@@ -148,15 +159,15 @@ public class SuggestedWords {
}
public int codePointAt(int i) {
- return mWordStr.codePointAt(i);
+ return mWord.codePointAt(i);
}
@Override
public String toString() {
if (TextUtils.isEmpty(mDebugString)) {
- return mWordStr;
+ return mWord;
} else {
- return mWordStr + " (" + mDebugString.toString() + ")";
+ return mWord + " (" + mDebugString.toString() + ")";
}
}
@@ -170,7 +181,7 @@ public class SuggestedWords {
final SuggestedWordInfo cur = candidates.get(i);
for (int j = 0; j < i; ++j) {
final SuggestedWordInfo previous = candidates.get(j);
- if (TextUtils.equals(cur.mWord, previous.mWord)) {
+ if (cur.mWord.equals(previous.mWord)) {
candidates.remove(cur.mScore < previous.mScore ? i : j);
--i;
break;
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 673b54500..bdd988df2 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -19,22 +19,23 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Locale;
public class SynchronouslyLoadedContactsBinaryDictionary extends ContactsBinaryDictionary {
private boolean mClosed;
public SynchronouslyLoadedContactsBinaryDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_CONTACTS, locale);
+ super(context, locale);
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java
deleted file mode 100644
index a8b871cdf..000000000
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-public class SynchronouslyLoadedContactsDictionary extends ContactsDictionary {
- private boolean mClosed;
-
- public SynchronouslyLoadedContactsDictionary(final Context context) {
- super(context, Suggest.DIC_CONTACTS);
- mClosed = false;
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- blockingReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- blockingReloadDictionaryIfRequired();
- return getWordFrequency(word) > -1;
- }
-
- // Protect against multiple closing
- @Override
- public synchronized void close() {
- // Actually with the current implementation of ContactsDictionary it's safe to close
- // several times, so the following protection is really only for foolproofing
- if (mClosed) return;
- mClosed = true;
- super.close();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 1606a34e0..b8cfddd4e 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -19,6 +19,9 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary {
@@ -32,11 +35,10 @@ public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionar
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java
deleted file mode 100644
index 23a49c192..000000000
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-public class SynchronouslyLoadedUserDictionary extends UserDictionary {
- private boolean mClosed;
-
- public SynchronouslyLoadedUserDictionary(final Context context, final String locale) {
- this(context, locale, false);
- }
-
- public SynchronouslyLoadedUserDictionary(final Context context, final String locale,
- final boolean alsoUseMoreRestrictiveLocales) {
- super(context, locale, alsoUseMoreRestrictiveLocales);
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- blockingReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- blockingReloadDictionaryIfRequired();
- return super.isValidWord(word);
- }
-
- // Protect against multiple closing
- @Override
- public synchronized void close() {
- if (mClosed) return;
- mClosed = true;
- super.close();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 6fa1a25a1..60e6fa127 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -34,13 +34,27 @@ import java.util.Arrays;
*/
public class UserBinaryDictionary extends ExpandableBinaryDictionary {
- // TODO: use Words.SHORTCUT when it's public in the SDK
+ // The user dictionary provider uses an empty string to mean "all languages".
+ private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
+
+ // TODO: use Words.SHORTCUT when we target JellyBean or above
final static String SHORTCUT = "shortcut";
- private static final String[] PROJECTION_QUERY = {
- Words.WORD,
- SHORTCUT,
- Words.FREQUENCY,
- };
+ private static final String[] PROJECTION_QUERY;
+ static {
+ // 16 is JellyBean, but we want this to compile against ICS.
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ PROJECTION_QUERY = new String[] {
+ Words.WORD,
+ SHORTCUT,
+ Words.FREQUENCY,
+ };
+ } else {
+ PROJECTION_QUERY = new String[] {
+ Words.WORD,
+ Words.FREQUENCY,
+ };
+ }
+ }
private static final String NAME = "userunigram";
@@ -58,9 +72,14 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getFilenameWithLocale(NAME, locale), Suggest.DIC_USER);
+ super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
- mLocale = locale;
+ if (SubtypeLocale.NO_LANGUAGE.equals(locale)) {
+ // If we don't have a locale, insert into the "all locales" user dictionary.
+ mLocale = USER_DICTIONARY_ALL_LANGUAGES;
+ } else {
+ mLocale = locale;
+ }
mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
// Perform a managed query. The Activity will handle closing and re-querying the cursor
// when needed.
@@ -136,7 +155,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
requestArguments = localeElements;
}
final Cursor cursor = mContext.getContentResolver().query(
- Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
+ Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
try {
addWords(cursor);
} finally {
@@ -182,16 +201,18 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
}
private void addWords(Cursor cursor) {
+ // 16 is JellyBean, but we want this to compile against ICS.
+ final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16;
clearFusionDictionary();
if (cursor == null) return;
if (cursor.moveToFirst()) {
final int indexWord = cursor.getColumnIndex(Words.WORD);
- final int indexShortcut = cursor.getColumnIndex(SHORTCUT);
+ final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(SHORTCUT) : 0;
final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
while (!cursor.isAfterLast()) {
- String word = cursor.getString(indexWord);
- String shortcut = cursor.getString(indexShortcut);
- int frequency = cursor.getInt(indexFrequency);
+ final String word = cursor.getString(indexWord);
+ final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null;
+ final int frequency = cursor.getInt(indexFrequency);
// Safeguard against adding really long words.
if (word.length() < MAX_WORD_LENGTH) {
super.addWord(word, null, frequency);
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
deleted file mode 100644
index c1efadd44..000000000
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.provider.UserDictionary.Words;
-import android.text.TextUtils;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-import java.util.Arrays;
-
-// TODO: This class is superseded by {@link UserBinaryDictionary}. Should be cleaned up.
-/**
- * An expandable dictionary that stores the words in the user unigram dictionary.
- *
- * @deprecated Use {@link UserBinaryDictionary}.
- */
-@Deprecated
-public class UserDictionary extends ExpandableDictionary {
-
- // TODO: use Words.SHORTCUT when it's public in the SDK
- final static String SHORTCUT = "shortcut";
- private static final String[] PROJECTION_QUERY = {
- Words.WORD,
- SHORTCUT,
- Words.FREQUENCY,
- };
-
- // This is not exported by the framework so we pretty much have to write it here verbatim
- private static final String ACTION_USER_DICTIONARY_INSERT =
- "com.android.settings.USER_DICTIONARY_INSERT";
-
- private ContentObserver mObserver;
- final private String mLocale;
- final private boolean mAlsoUseMoreRestrictiveLocales;
-
- public UserDictionary(final Context context, final String locale) {
- this(context, locale, false);
- }
-
- public UserDictionary(final Context context, final String locale,
- final boolean alsoUseMoreRestrictiveLocales) {
- super(context, Suggest.DIC_USER);
- if (null == locale) throw new NullPointerException(); // Catch the error earlier
- mLocale = locale;
- mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
- // Perform a managed query. The Activity will handle closing and re-querying the cursor
- // when needed.
- ContentResolver cres = context.getContentResolver();
-
- mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- setRequiresReload(true);
- }
- };
- cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
-
- loadDictionary();
- }
-
- @Override
- public synchronized void close() {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- super.close();
- }
-
- @Override
- public void loadDictionaryAsync() {
- // Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"],
- // "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3.
- // This is correct for locale processing.
- // For this example, we'll look at the "en_US_POSIX" case.
- final String[] localeElements =
- TextUtils.isEmpty(mLocale) ? new String[] {} : mLocale.split("_", 3);
- final int length = localeElements.length;
-
- final StringBuilder request = new StringBuilder("(locale is NULL)");
- String localeSoFar = "";
- // At start, localeElements = ["en", "US", "POSIX"] ; localeSoFar = "" ;
- // and request = "(locale is NULL)"
- for (int i = 0; i < length; ++i) {
- // i | localeSoFar | localeElements
- // 0 | "" | ["en", "US", "POSIX"]
- // 1 | "en_" | ["en", "US", "POSIX"]
- // 2 | "en_US_" | ["en", "en_US", "POSIX"]
- localeElements[i] = localeSoFar + localeElements[i];
- localeSoFar = localeElements[i] + "_";
- // i | request
- // 0 | "(locale is NULL)"
- // 1 | "(locale is NULL) or (locale=?)"
- // 2 | "(locale is NULL) or (locale=?) or (locale=?)"
- request.append(" or (locale=?)");
- }
- // At the end, localeElements = ["en", "en_US", "en_US_POSIX"]; localeSoFar = en_US_POSIX_"
- // and request = "(locale is NULL) or (locale=?) or (locale=?) or (locale=?)"
-
- final String[] requestArguments;
- // If length == 3, we already have all the arguments we need (common prefix is meaningless
- // inside variants
- if (mAlsoUseMoreRestrictiveLocales && length < 3) {
- request.append(" or (locale like ?)");
- // The following creates an array with one more (null) position
- final String[] localeElementsWithMoreRestrictiveLocalesIncluded =
- Arrays.copyOf(localeElements, length + 1);
- localeElementsWithMoreRestrictiveLocalesIncluded[length] =
- localeElements[length - 1] + "_%";
- requestArguments = localeElementsWithMoreRestrictiveLocalesIncluded;
- // If for example localeElements = ["en"]
- // then requestArguments = ["en", "en_%"]
- // and request = (locale is NULL) or (locale=?) or (locale like ?)
- // If localeElements = ["en", "en_US"]
- // then requestArguments = ["en", "en_US", "en_US_%"]
- } else {
- requestArguments = localeElements;
- }
- final Cursor cursor = getContext().getContentResolver()
- .query(Words.CONTENT_URI, PROJECTION_QUERY, request.toString(),
- requestArguments, null);
- try {
- addWords(cursor);
- } finally {
- if (null != cursor) cursor.close();
- }
- }
-
- public boolean isEnabled() {
- final ContentResolver cr = getContext().getContentResolver();
- final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI);
- if (client != null) {
- client.release();
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Adds a word to the user dictionary and makes it persistent.
- *
- * This will call upon the system interface to do the actual work through the intent
- * readied by the system to this effect.
- *
- * @param word the word to add. If the word is capitalized, then the dictionary will
- * recognize it as a capitalized word when searched.
- * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
- * the highest.
- * @TODO use a higher or float range for frequency
- */
- public synchronized void addWordToUserDictionary(final String word, final int frequency) {
- // Force load the dictionary here synchronously
- if (getRequiresReload()) loadDictionaryAsync();
- // TODO: do something for the UI. With the following, any sufficiently long word will
- // look like it will go to the user dictionary but it won't.
- // Safeguard against adding long words. Can cause stack overflow.
- if (word.length() >= getMaxWordLength()) return;
-
- // TODO: Add an argument to the intent to specify the frequency.
- Intent intent = new Intent(ACTION_USER_DICTIONARY_INSERT);
- intent.putExtra(Words.WORD, word);
- intent.putExtra(Words.LOCALE, mLocale);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- super.getWords(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- return super.isValidWord(word);
- }
-
- private void addWords(Cursor cursor) {
- clearDictionary();
- if (cursor == null) return;
- final int maxWordLength = getMaxWordLength();
- if (cursor.moveToFirst()) {
- final int indexWord = cursor.getColumnIndex(Words.WORD);
- final int indexShortcut = cursor.getColumnIndex(SHORTCUT);
- final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
- while (!cursor.isAfterLast()) {
- String word = cursor.getString(indexWord);
- String shortcut = cursor.getString(indexShortcut);
- int frequency = cursor.getInt(indexFrequency);
- // Safeguard against adding really long words. Stack may overflow due
- // to recursion
- if (word.length() < maxWordLength) {
- super.addWord(word, null, frequency);
- }
- if (null != shortcut && shortcut.length() < maxWordLength) {
- super.addWord(shortcut, word, frequency);
- }
- cursor.moveToNext();
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 5095f6582..6c9d1c250 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -27,9 +27,12 @@ import android.os.AsyncTask;
import android.provider.BaseColumns;
import android.util.Log;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
import java.lang.ref.SoftReference;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -49,14 +52,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
private static final int FREQUENCY_FOR_TYPED = 2;
/** Maximum number of pairs. Pruning will start when databases goes above this number. */
- private static int sMaxHistoryBigrams = 10000;
+ public static final int sMaxHistoryBigrams = 10000;
/**
* When it hits maximum bigram pair, it will delete until you are left with
* only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs.
* Do not keep this number small to avoid deleting too often.
*/
- private static int sDeleteHistoryBigrams = 1000;
+ public static final int sDeleteHistoryBigrams = 1000;
/**
* Database version should increase if the database structure changes
@@ -90,10 +93,10 @@ public class UserHistoryDictionary extends ExpandableDictionary {
private final static HashMap<String, String> sDictProjectionMap;
private final static ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
- sLangDictCache = new ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>();
+ sLangDictCache = CollectionUtils.newConcurrentHashMap();
static {
- sDictProjectionMap = new HashMap<String, String>();
+ sDictProjectionMap = CollectionUtils.newHashMap();
sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID);
sDictProjectionMap.put(MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD1);
sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2);
@@ -106,17 +109,12 @@ public class UserHistoryDictionary extends ExpandableDictionary {
private static DatabaseHelper sOpenHelper = null;
- public void setDatabaseMax(int maxHistoryBigram) {
- sMaxHistoryBigrams = maxHistoryBigram;
- }
-
- public void setDatabaseDelete(int deleteHistoryBigram) {
- sDeleteHistoryBigrams = deleteHistoryBigram;
+ public String getLocale() {
+ return mLocale;
}
public synchronized static UserHistoryDictionary getInstance(
- final Context context, final String locale,
- final int dictTypeId, final SharedPreferences sp) {
+ final Context context, final String locale, final SharedPreferences sp) {
if (sLangDictCache.containsKey(locale)) {
final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
@@ -128,14 +126,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
}
final UserHistoryDictionary dict =
- new UserHistoryDictionary(context, locale, dictTypeId, sp);
+ new UserHistoryDictionary(context, locale, sp);
sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
return dict;
}
- private UserHistoryDictionary(final Context context, final String locale, final int dicTypeId,
- SharedPreferences sp) {
- super(context, dicTypeId);
+ private UserHistoryDictionary(final Context context, final String locale,
+ final SharedPreferences sp) {
+ super(context, Dictionary.TYPE_USER_HISTORY);
mLocale = locale;
mPrefs = sp;
if (sOpenHelper == null) {
@@ -158,6 +156,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
// super.close();
}
+ @Override
+ protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ // Inhibit suggestions (not predictions) for user history for now. Removing this method
+ // is enough to use it through the standard ExpandableDictionary way.
+ return null;
+ }
+
/**
* Return whether the passed charsequence is in the dictionary.
*/
@@ -492,9 +498,11 @@ public class UserHistoryDictionary extends ExpandableDictionary {
needsToSave(fc, isValid, addLevel0Bigram)) {
freq = fc;
} else {
+ // Delete this entry
freq = -1;
}
} else {
+ // Delete this entry
freq = -1;
}
}
@@ -531,6 +539,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
getContentValues(word1, word2, mLocale));
pairId = pairIdLong.intValue();
}
+ // Eliminate freq == 0 because that word is profanity.
if (freq > 0) {
if (PROFILE_SAVE_RESTORE) {
++profInsert;
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
index 28847745e..bb0f5429a 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
@@ -29,9 +29,8 @@ import java.util.Set;
public class UserHistoryDictionaryBigramList {
public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0;
private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName();
- private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = new HashMap<String, Byte>();
- private final HashMap<String, HashMap<String, Byte>> mBigramMap =
- new HashMap<String, HashMap<String, Byte>>();
+ private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = CollectionUtils.newHashMap();
+ private final HashMap<String, HashMap<String, Byte>> mBigramMap = CollectionUtils.newHashMap();
private int mSize = 0;
public void evictAll() {
@@ -57,7 +56,7 @@ public class UserHistoryDictionaryBigramList {
if (mBigramMap.containsKey(word1)) {
map = mBigramMap.get(word1);
} else {
- map = new HashMap<String, Byte>();
+ map = CollectionUtils.newHashMap();
mBigramMap.put(word1, map);
}
if (!map.containsKey(word2)) {
@@ -98,11 +97,11 @@ public class UserHistoryDictionaryBigramList {
}
public HashMap<String, Byte> getBigrams(String word1) {
- if (!mBigramMap.containsKey(word1)) {
- return EMPTY_BIGRAM_MAP;
- } else {
- return mBigramMap.get(word1);
- }
+ if (mBigramMap.containsKey(word1)) return mBigramMap.get(word1);
+ // TODO: lower case according to locale
+ final String lowerWord1 = word1.toLowerCase();
+ if (mBigramMap.containsKey(lowerWord1)) return mBigramMap.get(lowerWord1);
+ return EMPTY_BIGRAM_MAP;
}
public boolean removeBigram(String word1, String word2) {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
index e5516dc62..5a2fdf48e 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
@@ -50,7 +50,7 @@ public class UserHistoryForgettingCurveUtils {
}
private ForgettingCurveParams(long now, boolean isValid) {
- this((int)pushCount((byte)0, isValid), now, now, isValid);
+ this(pushCount((byte)0, isValid), now, now, isValid);
}
/** This constructor is called when the user history bigram dictionary is being restored. */
@@ -199,20 +199,20 @@ public class UserHistoryForgettingCurveUtils {
public static final int[][] SCORE_TABLE = new int[FC_LEVEL_MAX][ELAPSED_TIME_MAX + 1];
static {
for (int i = 0; i < FC_LEVEL_MAX; ++i) {
- final double initialFreq;
+ final float initialFreq;
if (i >= 2) {
- initialFreq = (double)FC_FREQ_MAX;
+ initialFreq = FC_FREQ_MAX;
} else if (i == 1) {
- initialFreq = (double)FC_FREQ_MAX / 2;
+ initialFreq = FC_FREQ_MAX / 2;
} else if (i == 0) {
- initialFreq = (double)FC_FREQ_MAX / 4;
+ initialFreq = FC_FREQ_MAX / 4;
} else {
continue;
}
for (int j = 0; j < ELAPSED_TIME_MAX; ++j) {
- final double elapsedHour = j * ELAPSED_TIME_INTERVAL_HOURS;
- final double freq =
- initialFreq * Math.pow(initialFreq, elapsedHour / HALF_LIFE_HOURS);
+ final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS;
+ final float freq = initialFreq
+ * (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS);
final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq));
SCORE_TABLE[i][j] = intFreq;
}
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 4178955bc..fc7a42100 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -44,10 +44,8 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
-import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
-import java.util.Map;
public class Utils {
private Utils() {
@@ -67,44 +65,6 @@ public class Utils {
}
}
- public static class GCUtils {
- private static final String GC_TAG = GCUtils.class.getSimpleName();
- public static final int GC_TRY_COUNT = 2;
- // GC_TRY_LOOP_MAX is used for the hard limit of GC wait,
- // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT.
- public static final int GC_TRY_LOOP_MAX = 5;
- private static final long GC_INTERVAL = DateUtils.SECOND_IN_MILLIS;
- private static GCUtils sInstance = new GCUtils();
- private int mGCTryCount = 0;
-
- public static GCUtils getInstance() {
- return sInstance;
- }
-
- public void reset() {
- mGCTryCount = 0;
- }
-
- public boolean tryGCOrWait(String metaData, Throwable t) {
- if (mGCTryCount == 0) {
- System.gc();
- }
- if (++mGCTryCount > GC_TRY_COUNT) {
- LatinImeLogger.logOnException(metaData, t);
- return false;
- } else {
- try {
- Thread.sleep(GC_INTERVAL);
- return true;
- } catch (InterruptedException e) {
- Log.e(GC_TAG, "Sleep was interrupted.");
- LatinImeLogger.logOnException(metaData, t);
- return false;
- }
- }
- }
- }
-
/* package */ static class RingCharBuffer {
private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
@@ -206,18 +166,24 @@ public class Utils {
}
// Get the current stack trace
- public static String getStackTrace() {
+ public static String getStackTrace(final int limit) {
StringBuilder sb = new StringBuilder();
try {
throw new RuntimeException();
} catch (RuntimeException e) {
StackTraceElement[] frames = e.getStackTrace();
// Start at 1 because the first frame is here and we don't care about it
- for (int j = 1; j < frames.length; ++j) sb.append(frames[j].toString() + "\n");
+ for (int j = 1; j < frames.length && j < limit + 1; ++j) {
+ sb.append(frames[j].toString() + "\n");
+ }
}
return sb.toString();
}
+ public static String getStackTrace() {
+ return getStackTrace(Integer.MAX_VALUE - 1);
+ }
+
public static class UsabilityStudyLogUtils {
// TODO: remove code duplication with ResearchLog class
private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName();
@@ -473,7 +439,7 @@ public class Utils {
private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
private static final HashMap<String, String> sDeviceOverrideValueMap =
- new HashMap<String, String>();
+ CollectionUtils.newHashMap();
public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) {
final int orientation = res.getConfiguration().orientation;
@@ -491,7 +457,7 @@ public class Utils {
return sDeviceOverrideValueMap.get(key);
}
- private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = new HashMap<String, Long>();
+ private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap();
private static final String LOCALE_AND_TIME_STR_SEPARATER = ",";
public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) {
if (TextUtils.isEmpty(str)) {
@@ -502,7 +468,7 @@ public class Utils {
if (N < 2 || N % 2 != 0) {
return EMPTY_LT_HASH_MAP;
}
- final HashMap<String, Long> retval = new HashMap<String, Long>();
+ final HashMap<String, Long> retval = CollectionUtils.newHashMap();
for (int i = 0; i < N / 2; ++i) {
final String localeStr = ss[i * 2];
final long time = Long.valueOf(ss[i * 2 + 1]);
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
deleted file mode 100644
index a0de2f970..000000000
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-public class WhitelistDictionary extends ExpandableDictionary {
-
- private static final boolean DBG = LatinImeLogger.sDBG;
- private static final String TAG = WhitelistDictionary.class.getSimpleName();
-
- private final HashMap<String, Pair<Integer, String>> mWhitelistWords =
- new HashMap<String, Pair<Integer, String>>();
-
- // TODO: Conform to the async load contact of ExpandableDictionary
- public WhitelistDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_WHITELIST);
- // TODO: Move whitelist dictionary into main dictionary.
- final RunInLocale<Void> job = new RunInLocale<Void>() {
- @Override
- protected Void job(Resources res) {
- initWordlist(res.getStringArray(R.array.wordlist_whitelist));
- return null;
- }
- };
- job.runInLocale(context.getResources(), locale);
- }
-
- private void initWordlist(String[] wordlist) {
- mWhitelistWords.clear();
- final int N = wordlist.length;
- if (N % 3 != 0) {
- if (DBG) {
- Log.d(TAG, "The number of the whitelist is invalid.");
- }
- return;
- }
- try {
- for (int i = 0; i < N; i += 3) {
- final int score = Integer.valueOf(wordlist[i]);
- final String before = wordlist[i + 1];
- final String after = wordlist[i + 2];
- if (before != null && after != null) {
- mWhitelistWords.put(
- before.toLowerCase(), new Pair<Integer, String>(score, after));
- addWord(after, null /* shortcut */, score);
- }
- }
- } catch (NumberFormatException e) {
- if (DBG) {
- Log.d(TAG, "The score of the word is invalid.");
- }
- }
- }
-
- public String getWhitelistedWord(String before) {
- if (before == null) return null;
- final String lowerCaseBefore = before.toLowerCase();
- if(mWhitelistWords.containsKey(lowerCaseBefore)) {
- if (DBG) {
- Log.d(TAG, "--- found whitelistedWord: " + lowerCaseBefore);
- }
- return mWhitelistWords.get(lowerCaseBefore).second;
- }
- return null;
- }
-
- // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist
- // lists that word a should autocorrect to word b, and word c would autocorrect to
- // an upper-cased version of a. In this case, the way this return value is used would
- // remove the first candidate when the user typed the upper-cased version of A.
- // Example : abc -> def and xyz -> Abc
- // A user typing Abc would experience it being autocorrected to something else (not
- // necessarily def).
- // There is no such combination in the whitelist at the time and there probably won't
- // ever be - it doesn't make sense. But still.
- public boolean shouldForciblyAutoCorrectFrom(CharSequence word) {
- if (TextUtils.isEmpty(word)) return false;
- final String correction = getWhitelistedWord(word.toString());
- if (TextUtils.isEmpty(correction)) return false;
- return !correction.equals(word);
- }
-
- // Leave implementation of getWords and isValidWord to the superclass.
- // The words have been added to the ExpandableDictionary with addWord() inside initWordlist.
-}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index ca9caa1d3..ecec60f89 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -17,9 +17,7 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
import java.util.Arrays;
@@ -27,22 +25,27 @@ import java.util.Arrays;
* A place to store the currently composing word with information such as adjacent key codes as well
*/
public class WordComposer {
-
- public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
- public static final int NOT_A_COORDINATE = -1;
-
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
+ public static final int CAPS_MODE_OFF = 0;
+ // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
+ // aren't used anywhere in the code
+ public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
+ public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
+ public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
+ public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
+
private int[] mPrimaryKeyCodes;
- private int[] mXCoordinates;
- private int[] mYCoordinates;
- private StringBuilder mTypedWord;
+ private final InputPointers mInputPointers = new InputPointers(N);
+ private final StringBuilder mTypedWord;
private CharSequence mAutoCorrection;
private boolean mIsResumed;
+ private boolean mIsBatchMode;
// Cache these values for performance
private int mCapsCount;
- private boolean mAutoCapitalized;
+ private int mDigitsCount;
+ private int mCapitalizedMode;
private int mTrailingSingleQuotesCount;
private int mCodePointSize;
@@ -54,28 +57,24 @@ public class WordComposer {
public WordComposer() {
mPrimaryKeyCodes = new int[N];
mTypedWord = new StringBuilder(N);
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
mAutoCorrection = null;
mTrailingSingleQuotesCount = 0;
mIsResumed = false;
+ mIsBatchMode = false;
refreshSize();
}
public WordComposer(WordComposer source) {
- init(source);
- }
-
- public void init(WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
- mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
- mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
+ mInputPointers.copy(source.mInputPointers);
mCapsCount = source.mCapsCount;
+ mDigitsCount = source.mDigitsCount;
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
- mAutoCapitalized = source.mAutoCapitalized;
+ mCapitalizedMode = source.mCapitalizedMode;
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
mIsResumed = source.mIsResumed;
+ mIsBatchMode = source.mIsBatchMode;
refreshSize();
}
@@ -86,13 +85,15 @@ public class WordComposer {
mTypedWord.setLength(0);
mAutoCorrection = null;
mCapsCount = 0;
+ mDigitsCount = 0;
mIsFirstCharCapitalized = false;
mTrailingSingleQuotesCount = 0;
mIsResumed = false;
+ mIsBatchMode = false;
refreshSize();
}
- public final void refreshSize() {
+ private final void refreshSize() {
mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
}
@@ -116,12 +117,8 @@ public class WordComposer {
return mPrimaryKeyCodes[index];
}
- public int[] getXCoordinates() {
- return mXCoordinates;
- }
-
- public int[] getYCoordinates() {
- return mYCoordinates;
+ public InputPointers getInputPointers() {
+ return mInputPointers;
}
private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
@@ -129,40 +126,28 @@ public class WordComposer {
return previous && !Character.isUpperCase(codePoint);
}
- // TODO: remove input keyDetector
- public void add(int primaryCode, int x, int y, KeyDetector keyDetector) {
- final int keyX;
- final int keyY;
- if (null == keyDetector
- || x == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE
- || y == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE
- || x == KeyboardActionListener.NOT_A_TOUCH_COORDINATE
- || y == KeyboardActionListener.NOT_A_TOUCH_COORDINATE) {
- keyX = x;
- keyY = y;
- } else {
- keyX = keyDetector.getTouchX(x);
- keyY = keyDetector.getTouchY(y);
- }
- add(primaryCode, keyX, keyY);
- }
-
/**
* Add a new keystroke, with the pressed key's code point with the touch point coordinates.
*/
- private void add(int primaryCode, int keyX, int keyY) {
+ public void add(int primaryCode, int keyX, int keyY) {
final int newIndex = size();
mTypedWord.appendCodePoint(primaryCode);
refreshSize();
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
? Character.toLowerCase(primaryCode) : primaryCode;
- mXCoordinates[newIndex] = keyX;
- mYCoordinates[newIndex] = keyY;
+ // In the batch input mode, the {@code mInputPointers} holds batch input points and
+ // shouldn't be overridden by the "typed key" coordinates
+ // (See {@link #setBatchInputWord}).
+ if (!mIsBatchMode) {
+ // TODO: Set correct pointer id and time
+ mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
+ }
}
mIsFirstCharCapitalized = isFirstCharCapitalized(
newIndex, primaryCode, mIsFirstCharCapitalized);
if (Character.isUpperCase(primaryCode)) mCapsCount++;
+ if (Character.isDigit(primaryCode)) mDigitsCount++;
if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) {
++mTrailingSingleQuotesCount;
} else {
@@ -171,19 +156,35 @@ public class WordComposer {
mAutoCorrection = null;
}
+ public void setBatchInputPointers(InputPointers batchPointers) {
+ mInputPointers.set(batchPointers);
+ mIsBatchMode = true;
+ }
+
+ public void setBatchInputWord(CharSequence word) {
+ reset();
+ mIsBatchMode = true;
+ final int length = word.length();
+ for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
+ 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)}).
+ add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ }
+ }
+
/**
* Internal method to retrieve reasonable proximity info for a character.
*/
private void addKeyInfo(final int codePoint, final Keyboard keyboard) {
- for (final Key key : keyboard.mKeys) {
- if (key.mCode == codePoint) {
- final int x = key.mX + key.mWidth / 2;
- final int y = key.mY + key.mHeight / 2;
- add(codePoint, x, y);
- return;
- }
+ final Key key = keyboard.getKey(codePoint);
+ if (key != null) {
+ final int x = key.mX + key.mWidth / 2;
+ final int y = key.mY + key.mHeight / 2;
+ add(codePoint, x, y);
+ return;
}
- add(codePoint, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+ add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
/**
@@ -219,6 +220,7 @@ public class WordComposer {
mTypedWord.deleteCharAt(stringBuilderLength - 1);
}
if (Character.isUpperCase(lastChar)) mCapsCount--;
+ if (Character.isDigit(lastChar)) mDigitsCount--;
refreshSize();
}
// We may have deleted the last one.
@@ -263,7 +265,14 @@ public class WordComposer {
* @return true if all user typed chars are upper case, false otherwise
*/
public boolean isAllUpperCase() {
- return (mCapsCount > 0) && (mCapsCount == size());
+ return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
+ || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED
+ || (mCapsCount > 0) && (mCapsCount == size());
+ }
+
+ public boolean wasShiftedNoLock() {
+ return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
+ || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
}
/**
@@ -274,20 +283,34 @@ public class WordComposer {
}
/**
- * Saves the reason why the word is capitalized - whether it was automatic or
- * due to the user hitting shift in the middle of a sentence.
- * @param auto whether it was an automatic capitalization due to start of sentence
+ * Returns true if we have digits in the composing word.
+ */
+ public boolean hasDigits() {
+ return mDigitsCount > 0;
+ }
+
+ /**
+ * Saves the caps mode at the start of composing.
+ *
+ * WordComposer needs to know about this for several reasons. The first is, we need to know
+ * after the fact what the reason was, to register the correct form into the user history
+ * dictionary: if the word was automatically capitalized, we should insert it in all-lower
+ * case but if it's a manual pressing of shift, then it should be inserted as is.
+ * Also, batch input needs to know about the current caps mode to display correctly
+ * capitalized suggestions.
+ * @param mode the mode at the time of start
*/
- public void setAutoCapitalized(boolean auto) {
- mAutoCapitalized = auto;
+ public void setCapitalizedModeAtStartComposingTime(final int mode) {
+ mCapitalizedMode = mode;
}
/**
* Returns whether the word was automatically capitalized.
* @return whether the word was automatically capitalized
*/
- public boolean isAutoCapitalized() {
- return mAutoCapitalized;
+ public boolean wasAutoCapitalized() {
+ return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
+ || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
}
/**
@@ -318,19 +341,21 @@ public class WordComposer {
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
final int[] primaryKeyCodes = mPrimaryKeyCodes;
- final int[] xCoordinates = mXCoordinates;
- final int[] yCoordinates = mYCoordinates;
mPrimaryKeyCodes = new int[N];
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes,
- xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode,
+ mInputPointers, mTypedWord.toString(), committedWord, separatorCode,
prevWord);
+ mInputPointers.reset();
if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
&& type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
lastComposedWord.deactivate();
}
+ mCapsCount = 0;
+ mDigitsCount = 0;
+ mIsBatchMode = false;
mTypedWord.setLength(0);
+ mTrailingSingleQuotesCount = 0;
+ mIsFirstCharCapitalized = false;
refreshSize();
mAutoCorrection = null;
mIsResumed = false;
@@ -339,12 +364,15 @@ public class WordComposer {
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
- mXCoordinates = lastComposedWord.mXCoordinates;
- mYCoordinates = lastComposedWord.mYCoordinates;
+ mInputPointers.set(lastComposedWord.mInputPointers);
mTypedWord.setLength(0);
mTypedWord.append(lastComposedWord.mTypedWord);
refreshSize();
mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
mIsResumed = true;
}
+
+ public boolean isBatchMode() {
+ return mIsBatchMode;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 89c59f809..161b94ca0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -22,10 +22,13 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -124,7 +127,7 @@ public class BinaryDictInputOutput {
*/
private static final int VERSION_1_MAGIC_NUMBER = 0x78B1;
- private static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
+ public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
private static final int MINIMUM_SUPPORTED_VERSION = 1;
private static final int MAXIMUM_SUPPORTED_VERSION = 2;
private static final int NOT_A_VERSION_NUMBER = -1;
@@ -307,33 +310,32 @@ public class BinaryDictInputOutput {
}
/**
- * Reads a string from a RandomAccessFile. This is the converse of the above method.
+ * Reads a string from a ByteBuffer. This is the converse of the above method.
*/
- private static String readString(final RandomAccessFile source) throws IOException {
+ private static String readString(final ByteBuffer buffer) {
final StringBuilder s = new StringBuilder();
- int character = readChar(source);
+ int character = readChar(buffer);
while (character != INVALID_CHARACTER) {
s.appendCodePoint(character);
- character = readChar(source);
+ character = readChar(buffer);
}
return s.toString();
}
/**
- * Reads a character from the file.
+ * Reads a character from the ByteBuffer.
*
* This follows the character format documented earlier in this source file.
*
- * @param source the file, positioned over an encoded character.
+ * @param buffer the buffer, positioned over an encoded character.
* @return the character code.
*/
- private static int readChar(RandomAccessFile source) throws IOException {
- int character = source.readUnsignedByte();
+ private static int readChar(final ByteBuffer buffer) {
+ int character = readUnsignedByte(buffer);
if (!fitsOnOneByte(character)) {
- if (GROUP_CHARACTERS_TERMINATOR == character)
- return INVALID_CHARACTER;
+ if (GROUP_CHARACTERS_TERMINATOR == character) return INVALID_CHARACTER;
character <<= 16;
- character += source.readUnsignedShort();
+ character += readUnsignedShort(buffer);
}
return character;
}
@@ -783,13 +785,13 @@ public class BinaryDictInputOutput {
// their lower bound and exclude their higher bound so we need to have the first step
// start at exactly 1 unit higher than floor(unigramFreq + half a step).
// Note : to reconstruct the score, the dictionary reader will need to divide
- // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise, and add
- // (discretizedFrequency + 0.5) times this value to get the median value of the step,
- // which is the best approximation. This is how we get the most precise result with
- // only four bits.
- final double stepSize =
- (double)(MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5 + MAX_BIGRAM_FREQUENCY);
- final double firstStepStart = 1 + unigramFrequency + (stepSize / 2.0);
+ // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step,
+ // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best
+ // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the
+ // step pointed by the discretized frequency.
+ final float stepSize =
+ (MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5f + MAX_BIGRAM_FREQUENCY);
+ final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f);
final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize);
// If the bigram freq is less than half-a-step higher than the unigram freq, we get -1
// here. The best approximation would be the unigram freq itself, so we should not
@@ -1091,46 +1093,46 @@ public class BinaryDictInputOutput {
// readDictionaryBinary is the public entry point for them.
static final int[] characterBuffer = new int[MAX_WORD_LENGTH];
- private static CharGroupInfo readCharGroup(RandomAccessFile source,
- final int originalGroupAddress) throws IOException {
+ private static CharGroupInfo readCharGroup(final ByteBuffer buffer,
+ final int originalGroupAddress) {
int addressPointer = originalGroupAddress;
- final int flags = source.readUnsignedByte();
+ final int flags = readUnsignedByte(buffer);
++addressPointer;
final int characters[];
if (0 != (flags & FLAG_HAS_MULTIPLE_CHARS)) {
int index = 0;
- int character = CharEncoding.readChar(source);
+ int character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
while (-1 != character) {
characterBuffer[index++] = character;
- character = CharEncoding.readChar(source);
+ character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
}
characters = Arrays.copyOfRange(characterBuffer, 0, index);
} else {
- final int character = CharEncoding.readChar(source);
+ final int character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
characters = new int[] { character };
}
final int frequency;
if (0 != (FLAG_IS_TERMINAL & flags)) {
++addressPointer;
- frequency = source.readUnsignedByte();
+ frequency = readUnsignedByte(buffer);
} else {
frequency = CharGroup.NOT_A_TERMINAL;
}
int childrenAddress = addressPointer;
switch (flags & MASK_GROUP_ADDRESS_TYPE) {
case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
- childrenAddress += source.readUnsignedByte();
+ childrenAddress += readUnsignedByte(buffer);
addressPointer += 1;
break;
case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
- childrenAddress += source.readUnsignedShort();
+ childrenAddress += readUnsignedShort(buffer);
addressPointer += 2;
break;
case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
- childrenAddress += (source.readUnsignedByte() << 16) + source.readUnsignedShort();
+ childrenAddress += readUnsignedInt24(buffer);
addressPointer += 3;
break;
case FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
@@ -1140,38 +1142,38 @@ public class BinaryDictInputOutput {
}
ArrayList<WeightedString> shortcutTargets = null;
if (0 != (flags & FLAG_HAS_SHORTCUT_TARGETS)) {
- final long pointerBefore = source.getFilePointer();
+ final int pointerBefore = buffer.position();
shortcutTargets = new ArrayList<WeightedString>();
- source.readUnsignedShort(); // Skip the size
+ buffer.getShort(); // Skip the size
while (true) {
- final int targetFlags = source.readUnsignedByte();
- final String word = CharEncoding.readString(source);
+ final int targetFlags = readUnsignedByte(buffer);
+ final String word = CharEncoding.readString(buffer);
shortcutTargets.add(new WeightedString(word,
targetFlags & FLAG_ATTRIBUTE_FREQUENCY));
if (0 == (targetFlags & FLAG_ATTRIBUTE_HAS_NEXT)) break;
}
- addressPointer += (source.getFilePointer() - pointerBefore);
+ addressPointer += buffer.position() - pointerBefore;
}
ArrayList<PendingAttribute> bigrams = null;
if (0 != (flags & FLAG_HAS_BIGRAMS)) {
bigrams = new ArrayList<PendingAttribute>();
while (true) {
- final int bigramFlags = source.readUnsignedByte();
+ final int bigramFlags = readUnsignedByte(buffer);
++addressPointer;
final int sign = 0 == (bigramFlags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) ? 1 : -1;
int bigramAddress = addressPointer;
switch (bigramFlags & MASK_ATTRIBUTE_ADDRESS_TYPE) {
case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
- bigramAddress += sign * source.readUnsignedByte();
+ bigramAddress += sign * readUnsignedByte(buffer);
addressPointer += 1;
break;
case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
- bigramAddress += sign * source.readUnsignedShort();
+ bigramAddress += sign * readUnsignedShort(buffer);
addressPointer += 2;
break;
case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
- final int offset = ((source.readUnsignedByte() << 16)
- + source.readUnsignedShort());
+ final int offset = (readUnsignedByte(buffer) << 16)
+ + readUnsignedShort(buffer);
bigramAddress += sign * offset;
addressPointer += 3;
break;
@@ -1188,15 +1190,15 @@ public class BinaryDictInputOutput {
}
/**
- * Reads and returns the char group count out of a file and forwards the pointer.
+ * Reads and returns the char group count out of a buffer and forwards the pointer.
*/
- private static int readCharGroupCount(RandomAccessFile source) throws IOException {
- final int msb = source.readUnsignedByte();
+ private static int readCharGroupCount(final ByteBuffer buffer) {
+ final int msb = readUnsignedByte(buffer);
if (MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
return msb;
} else {
return ((MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8)
- + source.readUnsignedByte();
+ + readUnsignedByte(buffer);
}
}
@@ -1204,31 +1206,29 @@ public class BinaryDictInputOutput {
// of this method. Since it performs direct, unbuffered random access to the file and
// may be called hundreds of thousands of times, the resulting performance is not
// reasonable without some kind of cache. Thus:
- // TODO: perform buffered I/O here and in other places in the code.
private static TreeMap<Integer, String> wordCache = new TreeMap<Integer, String>();
/**
* Finds, as a string, the word at the address passed as an argument.
*
- * @param source the file to read from.
+ * @param buffer the buffer to read from.
* @param headerSize the size of the header.
* @param address the address to seek.
* @return the word, as a string.
- * @throws IOException if the file can't be read.
*/
- private static String getWordAtAddress(final RandomAccessFile source, final long headerSize,
- int address) throws IOException {
+ private static String getWordAtAddress(final ByteBuffer buffer, final int headerSize,
+ final int address) {
final String cachedString = wordCache.get(address);
if (null != cachedString) return cachedString;
- final long originalPointer = source.getFilePointer();
- source.seek(headerSize);
- final int count = readCharGroupCount(source);
+ final int originalPointer = buffer.position();
+ buffer.position(headerSize);
+ final int count = readCharGroupCount(buffer);
int groupOffset = getGroupCountSize(count);
final StringBuilder builder = new StringBuilder();
String result = null;
CharGroupInfo last = null;
for (int i = count - 1; i >= 0; --i) {
- CharGroupInfo info = readCharGroup(source, groupOffset);
+ CharGroupInfo info = readCharGroup(buffer, groupOffset);
groupOffset = info.mEndAddress;
if (info.mOriginalAddress == address) {
builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
@@ -1239,9 +1239,9 @@ public class BinaryDictInputOutput {
if (info.mChildrenAddress > address) {
if (null == last) continue;
builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
- source.seek(last.mChildrenAddress + headerSize);
+ buffer.position(last.mChildrenAddress + headerSize);
groupOffset = last.mChildrenAddress + 1;
- i = source.readUnsignedByte();
+ i = readUnsignedByte(buffer);
last = null;
continue;
}
@@ -1249,14 +1249,14 @@ public class BinaryDictInputOutput {
}
if (0 == i && hasChildrenAddress(last.mChildrenAddress)) {
builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
- source.seek(last.mChildrenAddress + headerSize);
+ buffer.position(last.mChildrenAddress + headerSize);
groupOffset = last.mChildrenAddress + 1;
- i = source.readUnsignedByte();
+ i = readUnsignedByte(buffer);
last = null;
continue;
}
}
- source.seek(originalPointer);
+ buffer.position(originalPointer);
wordCache.put(address, result);
return result;
}
@@ -1269,44 +1269,47 @@ public class BinaryDictInputOutput {
* This will recursively read other nodes into the structure, populating the reverse
* maps on the fly and using them to keep track of already read nodes.
*
- * @param source the data file, correctly positioned at the start of a node.
+ * @param buffer the buffer, correctly positioned at the start of a node.
* @param headerSize the size, in bytes, of the file header.
* @param reverseNodeMap a mapping from addresses to already read nodes.
* @param reverseGroupMap a mapping from addresses to already read character groups.
* @return the read node with all his children already read.
*/
- private static Node readNode(RandomAccessFile source, long headerSize,
- Map<Integer, Node> reverseNodeMap, Map<Integer, CharGroup> reverseGroupMap)
+ private static Node readNode(final ByteBuffer buffer, final int headerSize,
+ final Map<Integer, Node> reverseNodeMap, final Map<Integer, CharGroup> reverseGroupMap)
throws IOException {
- final int nodeOrigin = (int)(source.getFilePointer() - headerSize);
- final int count = readCharGroupCount(source);
+ final int nodeOrigin = buffer.position() - headerSize;
+ final int count = readCharGroupCount(buffer);
final ArrayList<CharGroup> nodeContents = new ArrayList<CharGroup>();
int groupOffset = nodeOrigin + getGroupCountSize(count);
for (int i = count; i > 0; --i) {
- CharGroupInfo info = readCharGroup(source, groupOffset);
+ CharGroupInfo info =readCharGroup(buffer, groupOffset);
ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
ArrayList<WeightedString> bigrams = null;
if (null != info.mBigrams) {
bigrams = new ArrayList<WeightedString>();
for (PendingAttribute bigram : info.mBigrams) {
- final String word = getWordAtAddress(source, headerSize, bigram.mAddress);
+ final String word = getWordAtAddress(
+ buffer, headerSize, bigram.mAddress);
bigrams.add(new WeightedString(word, bigram.mFrequency));
}
}
if (hasChildrenAddress(info.mChildrenAddress)) {
Node children = reverseNodeMap.get(info.mChildrenAddress);
if (null == children) {
- final long currentPosition = source.getFilePointer();
- source.seek(info.mChildrenAddress + headerSize);
- children = readNode(source, headerSize, reverseNodeMap, reverseGroupMap);
- source.seek(currentPosition);
+ final int currentPosition = buffer.position();
+ buffer.position(info.mChildrenAddress + headerSize);
+ children = readNode(
+ buffer, headerSize, reverseNodeMap, reverseGroupMap);
+ buffer.position(currentPosition);
}
nodeContents.add(
- new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency,
- children));
+ new CharGroup(info.mCharacters, shortcutTargets,
+ bigrams, info.mFrequency, children));
} else {
nodeContents.add(
- new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency));
+ new CharGroup(info.mCharacters, shortcutTargets,
+ bigrams, info.mFrequency));
}
groupOffset = info.mEndAddress;
}
@@ -1318,57 +1321,76 @@ public class BinaryDictInputOutput {
/**
* Helper function to get the binary format version from the header.
+ * @throws IOException
*/
- private static int getFormatVersion(final RandomAccessFile source) throws IOException {
- final int magic_v1 = source.readUnsignedShort();
- if (VERSION_1_MAGIC_NUMBER == magic_v1) return source.readUnsignedByte();
- final int magic_v2 = (magic_v1 << 16) + source.readUnsignedShort();
- if (VERSION_2_MAGIC_NUMBER == magic_v2) return source.readUnsignedShort();
+ private static int getFormatVersion(final ByteBuffer buffer) throws IOException {
+ final int magic_v1 = readUnsignedShort(buffer);
+ if (VERSION_1_MAGIC_NUMBER == magic_v1) return readUnsignedByte(buffer);
+ final int magic_v2 = (magic_v1 << 16) + readUnsignedShort(buffer);
+ if (VERSION_2_MAGIC_NUMBER == magic_v2) return readUnsignedShort(buffer);
return NOT_A_VERSION_NUMBER;
}
/**
- * Reads a random access file and returns the memory representation of the dictionary.
+ * Reads options from a file and populate a map with their contents.
+ *
+ * The file is read at the current file pointer, so the caller must take care the pointer
+ * is in the right place before calling this.
+ */
+ public static void populateOptions(final ByteBuffer buffer, final int headerSize,
+ final HashMap<String, String> options) {
+ while (buffer.position() < headerSize) {
+ final String key = CharEncoding.readString(buffer);
+ final String value = CharEncoding.readString(buffer);
+ options.put(key, value);
+ }
+ }
+
+ /**
+ * Reads a byte buffer and returns the memory representation of the dictionary.
*
* This high-level method takes a binary file and reads its contents, populating a
* FusionDictionary structure. The optional dict argument is an existing dictionary to
* which words from the file should be added. If it is null, a new dictionary is created.
*
- * @param source the file to read.
+ * @param buffer the buffer to read.
* @param dict an optional dictionary to add words to, or null.
* @return the created (or merged) dictionary.
*/
- public static FusionDictionary readDictionaryBinary(final RandomAccessFile source,
+ public static FusionDictionary readDictionaryBinary(final ByteBuffer buffer,
final FusionDictionary dict) throws IOException, UnsupportedFormatException {
// Check file version
- final int version = getFormatVersion(source);
- if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION ) {
+ final int version = getFormatVersion(buffer);
+ if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION) {
throw new UnsupportedFormatException("This file has version " + version
+ ", but this implementation does not support versions above "
+ MAXIMUM_SUPPORTED_VERSION);
}
+ // clear cache
+ wordCache.clear();
+
// Read options
- final int optionsFlags = source.readUnsignedShort();
+ final int optionsFlags = readUnsignedShort(buffer);
- final long headerSize;
+ final int headerSize;
final HashMap<String, String> options = new HashMap<String, String>();
if (version < FIRST_VERSION_WITH_HEADER_SIZE) {
- headerSize = source.getFilePointer();
+ headerSize = buffer.position();
} else {
- headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16)
- + (source.readUnsignedByte() << 8) + source.readUnsignedByte();
- while (source.getFilePointer() < headerSize) {
- final String key = CharEncoding.readString(source);
- final String value = CharEncoding.readString(source);
- options.put(key, value);
- }
- source.seek(headerSize);
+ headerSize = buffer.getInt();
+ populateOptions(buffer, headerSize, options);
+ buffer.position(headerSize);
+ }
+
+ if (headerSize < 0) {
+ throw new UnsupportedFormatException("header size can't be negative.");
}
Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>();
Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>();
- final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping);
+ final Node root = readNode(
+ buffer, headerSize, reverseNodeMapping, reverseGroupMapping);
FusionDictionary newDict = new FusionDictionary(root,
new FusionDictionary.DictionaryOptions(options,
@@ -1392,6 +1414,28 @@ public class BinaryDictInputOutput {
}
/**
+ * Helper function to read one byte from ByteBuffer.
+ */
+ private static int readUnsignedByte(final ByteBuffer buffer) {
+ return ((int)buffer.get()) & 0xFF;
+ }
+
+ /**
+ * Helper function to read two byte from ByteBuffer.
+ */
+ private static int readUnsignedShort(final ByteBuffer buffer) {
+ return ((int)buffer.getShort()) & 0xFFFF;
+ }
+
+ /**
+ * Helper function to read three byte from ByteBuffer.
+ */
+ private static int readUnsignedInt24(final ByteBuffer buffer) {
+ final int value = readUnsignedByte(buffer) << 16;
+ return value + readUnsignedShort(buffer);
+ }
+
+ /**
* Basic test to find out whether the file is a binary dictionary or not.
*
* Concretely this only tests the magic number.
@@ -1400,14 +1444,44 @@ public class BinaryDictInputOutput {
* @return true if it's a binary dictionary, false otherwise
*/
public static boolean isBinaryDictionary(final String filename) {
+ FileInputStream inStream = null;
try {
- RandomAccessFile f = new RandomAccessFile(filename, "r");
- final int version = getFormatVersion(f);
+ final File file = new File(filename);
+ inStream = new FileInputStream(file);
+ final ByteBuffer buffer = inStream.getChannel().map(
+ FileChannel.MapMode.READ_ONLY, 0, file.length());
+ final int version = getFormatVersion(buffer);
return (version >= MINIMUM_SUPPORTED_VERSION && version <= MAXIMUM_SUPPORTED_VERSION);
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
return false;
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
}
}
+
+ /**
+ * Calculate bigram frequency from compressed value
+ *
+ * @see #makeBigramFlags
+ *
+ * @param unigramFrequency
+ * @param bigramFrequency compressed frequency
+ * @return approximate bigram frequency
+ */
+ public static int reconstructBigramFrequency(final int unigramFrequency,
+ final int bigramFrequency) {
+ final float stepSize = (MAX_TERMINAL_FREQUENCY - unigramFrequency)
+ / (1.5f + MAX_BIGRAM_FREQUENCY);
+ final float resultFreqFloat = (float)unigramFrequency
+ + stepSize * (bigramFrequency + 1.0f);
+ return (int)resultFreqFloat;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 8b53c9427..7c15ba54d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -61,8 +61,8 @@ public class FusionDictionary implements Iterable<Word> {
* This represents an "attribute", that is either a bigram or a shortcut.
*/
public static class WeightedString {
- final String mWord;
- int mFrequency;
+ public final String mWord;
+ public int mFrequency;
public WeightedString(String word, int frequency) {
mWord = word;
mFrequency = frequency;
@@ -516,13 +516,23 @@ public class FusionDictionary implements Iterable<Word> {
int indexOfGroup = findIndexOfChar(node, s.codePointAt(index));
if (CHARACTER_NOT_FOUND == indexOfGroup) return null;
currentGroup = node.mData.get(indexOfGroup);
+
+ if (s.length() - index < currentGroup.mChars.length) return null;
+ int newIndex = index;
+ while (newIndex < s.length() && newIndex - index < currentGroup.mChars.length) {
+ if (currentGroup.mChars[newIndex - index] != s.codePointAt(newIndex)) return null;
+ newIndex++;
+ }
+ index = newIndex;
+
if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
- index += currentGroup.mChars.length;
if (index < s.length()) {
node = currentGroup.mChildren;
}
} while (null != node && index < s.length());
+ if (index < s.length()) return null;
+ if (!currentGroup.isTerminal()) return null;
if (DBG && !s.equals(checker.toString())) return null;
return currentGroup;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java
index d07826757..65fc72c40 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Word.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Word.java
@@ -27,10 +27,10 @@ import java.util.Arrays;
* This is chiefly used to iterate a dictionary.
*/
public class Word implements Comparable<Word> {
- final String mWord;
- final int mFrequency;
- final ArrayList<WeightedString> mShortcutTargets;
- final ArrayList<WeightedString> mBigrams;
+ public final String mWord;
+ public final int mFrequency;
+ public final ArrayList<WeightedString> mShortcutTargets;
+ public final ArrayList<WeightedString> mBigrams;
private int mHashCode = 0;
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 88efc5a85..eef7a51f2 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -20,30 +20,22 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.service.textservice.SpellCheckerService;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.LruCache;
-import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
-import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.ContactsBinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.Dictionary.WordCallback;
import com.android.inputmethod.latin.DictionaryCollection;
import com.android.inputmethod.latin.DictionaryFactory;
-import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LocaleUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary;
-import com.android.inputmethod.latin.SynchronouslyLoadedContactsDictionary;
import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary;
-import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary;
-import com.android.inputmethod.latin.WhitelistDictionary;
-import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.UserBinaryDictionary;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -66,18 +58,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService
public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
- private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
- private static final int CAPITALIZE_FIRST = 1; // First only
- private static final int CAPITALIZE_ALL = 2; // All caps
+ public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
+ public static final int CAPITALIZE_FIRST = 1; // First only
+ public static final int CAPITALIZE_ALL = 2; // All caps
private final static String[] EMPTY_STRING_ARRAY = new String[0];
- private Map<String, DictionaryPool> mDictionaryPools =
- Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
- private Map<String, Dictionary> mUserDictionaries =
- Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- private Map<String, Dictionary> mWhitelistDictionaries =
- Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- private Dictionary mContactsDictionary;
+ private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
+ private Map<String, UserBinaryDictionary> mUserDictionaries =
+ CollectionUtils.newSynchronizedTreeMap();
+ private ContactsBinaryDictionary mContactsDictionary;
// The threshold for a candidate to be offered as a suggestion.
private float mSuggestionThreshold;
@@ -88,12 +77,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private final Object mUseContactsLock = new Object();
private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList =
- new HashSet<WeakReference<DictionaryCollection>>();
+ CollectionUtils.newHashSet();
public static final int SCRIPT_LATIN = 0;
public static final int SCRIPT_CYRILLIC = 1;
- private static final String SINGLE_QUOTE = "\u0027";
- private static final String APOSTROPHE = "\u2019";
+ public static final String SINGLE_QUOTE = "\u0027";
+ public static final String APOSTROPHE = "\u2019";
private static final TreeMap<String, Integer> mLanguageToScript;
static {
// List of the supported languages and their associated script. We won't check
@@ -104,7 +93,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
// proximity to pass to the dictionary descent algorithm.
// IMPORTANT: this only contains languages - do not write countries in there.
// Only the language is searched from the map.
- mLanguageToScript = new TreeMap<String, Integer>();
+ mLanguageToScript = CollectionUtils.newTreeMap();
mLanguageToScript.put("en", SCRIPT_LATIN);
mLanguageToScript.put("fr", SCRIPT_LATIN);
mLanguageToScript.put("de", SCRIPT_LATIN);
@@ -130,7 +119,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
}
- private static int getScriptFromLocale(final Locale locale) {
+ public static int getScriptFromLocale(final Locale locale) {
final Integer script = mLanguageToScript.get(locale.getLanguage());
if (null == script) {
throw new RuntimeException("We have been called with an unsupported language: \""
@@ -154,13 +143,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private void startUsingContactsDictionaryLocked() {
if (null == mContactsDictionary) {
- if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) {
- // TODO: use the right locale for each session
- mContactsDictionary =
- new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault());
- } else {
- mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
- }
+ // TODO: use the right locale for each session
+ mContactsDictionary =
+ new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault());
}
final Iterator<WeakReference<DictionaryCollection>> iterator =
mDictionaryCollectionsList.iterator();
@@ -196,19 +181,27 @@ public class AndroidSpellCheckerService extends SpellCheckerService
@Override
public Session createSession() {
- return new AndroidSpellCheckerSession(this);
+ // Should not refer to AndroidSpellCheckerSession directly considering
+ // that AndroidSpellCheckerSession may be overlaid.
+ return AndroidSpellCheckerSessionFactory.newInstance(this);
}
- private static SuggestionsInfo getNotInDictEmptySuggestions() {
+ public static SuggestionsInfo getNotInDictEmptySuggestions() {
return new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
}
- private static SuggestionsInfo getInDictEmptySuggestions() {
+ public static SuggestionsInfo getInDictEmptySuggestions() {
return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
EMPTY_STRING_ARRAY);
}
- private static class SuggestionsGatherer implements WordCallback {
+ public SuggestionsGatherer newSuggestionsGatherer(final String text, int maxLength) {
+ return new SuggestionsGatherer(
+ text, mSuggestionThreshold, mRecommendedThreshold, maxLength);
+ }
+
+ // TODO: remove this class and replace it by storage local to the session.
+ public static class SuggestionsGatherer {
public static class Result {
public final String[] mSuggestions;
public final boolean mHasRecommendedSuggestions;
@@ -238,13 +231,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
mSuggestionThreshold = suggestionThreshold;
mRecommendedThreshold = recommendedThreshold;
mMaxLength = maxLength;
- mSuggestions = new ArrayList<CharSequence>(maxLength + 1);
+ mSuggestions = CollectionUtils.newArrayList(maxLength + 1);
mScores = new int[mMaxLength];
}
- @Override
- synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
- int dicTypeId, int dataType) {
+ synchronized public boolean addWord(char[] word, int[] spaceIndices, int wordOffset,
+ int wordLength, int score) {
final int positionIndex = Arrays.binarySearch(mScores, 0, mLength, score);
// binarySearch returns the index if the element exists, and -<insertion index> - 1
// if it doesn't. See documentation for binarySearch.
@@ -367,11 +359,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private void closeAllDictionaries() {
final Map<String, DictionaryPool> oldPools = mDictionaryPools;
- mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
- final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries;
- mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries;
- mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+ mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
+ final Map<String, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries;
+ mUserDictionaries = CollectionUtils.newSynchronizedTreeMap();
new Thread("spellchecker_close_dicts") {
@Override
public void run() {
@@ -381,15 +371,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
for (Dictionary dict : oldUserDictionaries.values()) {
dict.close();
}
- for (Dictionary dict : oldWhitelistDictionaries.values()) {
- dict.close();
- }
synchronized (mUseContactsLock) {
if (null != mContactsDictionary) {
// The synchronously loaded contacts dictionary should have been in one
// or several pools, but it is shielded against multiple closing and it's
// safe to call it several times.
- final Dictionary dictToClose = mContactsDictionary;
+ final ContactsBinaryDictionary dictToClose = mContactsDictionary;
// TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY
// is no longer needed
mContactsDictionary = null;
@@ -400,7 +387,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
}.start();
}
- private DictionaryPool getDictionaryPool(final String locale) {
+ public DictionaryPool getDictionaryPool(final String locale) {
DictionaryPool pool = mDictionaryPools.get(locale);
if (null == pool) {
final Locale localeObject = LocaleUtils.constructLocaleFromString(locale);
@@ -421,36 +408,20 @@ public class AndroidSpellCheckerService extends SpellCheckerService
DictionaryFactory.createMainDictionaryFromManager(this, locale,
true /* useFullEditDistance */);
final String localeStr = locale.toString();
- Dictionary userDictionary = mUserDictionaries.get(localeStr);
+ UserBinaryDictionary userDictionary = mUserDictionaries.get(localeStr);
if (null == userDictionary) {
- if (LatinIME.USE_BINARY_USER_DICTIONARY) {
- userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true);
- } else {
- userDictionary = new SynchronouslyLoadedUserDictionary(this, localeStr, true);
- }
+ userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true);
mUserDictionaries.put(localeStr, userDictionary);
}
dictionaryCollection.addDictionary(userDictionary);
- Dictionary whitelistDictionary = mWhitelistDictionaries.get(localeStr);
- if (null == whitelistDictionary) {
- whitelistDictionary = new WhitelistDictionary(this, locale);
- mWhitelistDictionaries.put(localeStr, whitelistDictionary);
- }
- dictionaryCollection.addDictionary(whitelistDictionary);
synchronized (mUseContactsLock) {
if (mUseContactsDictionary) {
if (null == mContactsDictionary) {
- // TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY is no
- // longer needed
- if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) {
- // TODO: use the right locale. We can't do it right now because the
- // spell checker is reusing the contacts dictionary across sessions
- // without regard for their locale, so we need to fix that first.
- mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this,
- Locale.getDefault());
- } else {
- mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
- }
+ // TODO: use the right locale. We can't do it right now because the
+ // spell checker is reusing the contacts dictionary across sessions
+ // without regard for their locale, so we need to fix that first.
+ mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this,
+ Locale.getDefault());
}
}
dictionaryCollection.addDictionary(mContactsDictionary);
@@ -461,7 +432,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
}
// This method assumes the text is not empty or null.
- private static int getCapitalizationType(String text) {
+ public static int getCapitalizationType(String text) {
// If the first char is not uppercase, then the word is either all lower case,
// and in either case we return CAPITALIZE_NONE.
if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
@@ -478,358 +449,4 @@ public class AndroidSpellCheckerService extends SpellCheckerService
if (1 == capsCount) return CAPITALIZE_FIRST;
return (len == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
}
-
- private static class AndroidSpellCheckerSession extends Session {
- // Immutable, but need the locale which is not available in the constructor yet
- private DictionaryPool mDictionaryPool;
- // Likewise
- private Locale mLocale;
- // Cache this for performance
- private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
-
- private final AndroidSpellCheckerService mService;
-
- private final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
-
- private static class SuggestionsParams {
- public final String[] mSuggestions;
- public final int mFlags;
- public SuggestionsParams(String[] suggestions, int flags) {
- mSuggestions = suggestions;
- mFlags = flags;
- }
- }
-
- private static class SuggestionsCache {
- private static final int MAX_CACHE_SIZE = 50;
- // TODO: support bigram
- private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
- new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE);
-
- public SuggestionsParams getSuggestionsFromCache(String query) {
- return mUnigramSuggestionsInfoCache.get(query);
- }
-
- public void putSuggestionsToCache(String query, String[] suggestions, int flags) {
- if (suggestions == null || TextUtils.isEmpty(query)) {
- return;
- }
- mUnigramSuggestionsInfoCache.put(query, new SuggestionsParams(suggestions, flags));
- }
- }
-
- AndroidSpellCheckerSession(final AndroidSpellCheckerService service) {
- mService = service;
- }
-
- @Override
- public void onCreate() {
- final String localeString = getLocale();
- mDictionaryPool = mService.getDictionaryPool(localeString);
- mLocale = LocaleUtils.constructLocaleFromString(localeString);
- mScript = getScriptFromLocale(mLocale);
- }
-
- /*
- * Returns whether the code point is a letter that makes sense for the specified
- * locale for this spell checker.
- * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
- * and is limited to EFIGS languages and Russian.
- * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
- * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
- */
- private static boolean isLetterCheckableByLanguage(final int codePoint,
- final int script) {
- switch (script) {
- 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);
- default:
- // Should never come here
- throw new RuntimeException("Impossible value of script: " + script);
- }
- }
-
- /**
- * Finds out whether a particular string should be filtered out of spell checking.
- *
- * This will loosely match URLs, numbers, symbols. To avoid always underlining words that
- * we know we will never recognize, this accepts a script identifier that should be one
- * of the SCRIPT_* constants defined above, to rule out quickly characters from very
- * different languages.
- *
- * @param text the string to evaluate.
- * @param script the identifier for the script this spell checker recognizes
- * @return true if we should filter this text out, false otherwise
- */
- private static boolean shouldFilterOut(final String text, final int script) {
- if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
-
- // TODO: check if an equivalent processing can't be done more quickly with a
- // compiled regexp.
- // Filter by first letter
- final int firstCodePoint = text.codePointAt(0);
- // Filter out words that don't start with a letter or an apostrophe
- if (!isLetterCheckableByLanguage(firstCodePoint, script)
- && '\'' != firstCodePoint) return true;
-
- // Filter contents
- final int length = text.length();
- int letterCount = 0;
- for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
- final int codePoint = text.codePointAt(i);
- // Any word containing a '@' is probably an e-mail address
- // Any word containing a '/' is probably either an ad-hoc combination of two
- // words or a URI - in either case we don't want to spell check that
- if ('@' == codePoint || '/' == codePoint) return true;
- if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
- }
- // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
- // in this word are letters
- return (letterCount * 4 < length * 3);
- }
-
- private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(
- TextInfo ti, SentenceSuggestionsInfo ssi) {
- final String typedText = ti.getText();
- if (!typedText.contains(SINGLE_QUOTE)) {
- return null;
- }
- final int N = ssi.getSuggestionsCount();
- final ArrayList<Integer> additionalOffsets = new ArrayList<Integer>();
- final ArrayList<Integer> additionalLengths = new ArrayList<Integer>();
- final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
- new ArrayList<SuggestionsInfo>();
- for (int i = 0; i < N; ++i) {
- final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
- final int flags = si.getSuggestionsAttributes();
- if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
- continue;
- }
- final int offset = ssi.getOffsetAt(i);
- final int length = ssi.getLengthAt(i);
- final String subText = typedText.substring(offset, offset + length);
- if (!subText.contains(SINGLE_QUOTE)) {
- continue;
- }
- final String[] splitTexts = subText.split(SINGLE_QUOTE, -1);
- if (splitTexts == null || splitTexts.length <= 1) {
- continue;
- }
- final int splitNum = splitTexts.length;
- for (int j = 0; j < splitNum; ++j) {
- final String splitText = splitTexts[j];
- if (TextUtils.isEmpty(splitText)) {
- continue;
- }
- if (mSuggestionsCache.getSuggestionsFromCache(splitText) == null) {
- continue;
- }
- final int newLength = splitText.length();
- // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
- final int newFlags = 0;
- final SuggestionsInfo newSi = new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
- newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
- if (DBG) {
- Log.d(TAG, "Override and remove old span over: "
- + splitText + ", " + offset + "," + newLength);
- }
- additionalOffsets.add(offset);
- additionalLengths.add(newLength);
- additionalSuggestionsInfos.add(newSi);
- }
- }
- final int additionalSize = additionalOffsets.size();
- if (additionalSize <= 0) {
- return null;
- }
- final int suggestionsSize = N + additionalSize;
- final int[] newOffsets = new int[suggestionsSize];
- final int[] newLengths = new int[suggestionsSize];
- final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
- int i;
- for (i = 0; i < N; ++i) {
- newOffsets[i] = ssi.getOffsetAt(i);
- newLengths[i] = ssi.getLengthAt(i);
- newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
- }
- for (; i < suggestionsSize; ++i) {
- newOffsets[i] = additionalOffsets.get(i - N);
- newLengths[i] = additionalLengths.get(i - N);
- newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
- }
- return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
- }
-
- @Override
- public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(
- TextInfo[] textInfos, int suggestionsLimit) {
- final SentenceSuggestionsInfo[] retval = super.onGetSentenceSuggestionsMultiple(
- textInfos, suggestionsLimit);
- if (retval == null || retval.length != textInfos.length) {
- return retval;
- }
- for (int i = 0; i < retval.length; ++i) {
- final SentenceSuggestionsInfo tempSsi =
- fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
- if (tempSsi != null) {
- retval[i] = tempSsi;
- }
- }
- return retval;
- }
-
- @Override
- public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
- int suggestionsLimit, boolean sequentialWords) {
- final int length = textInfos.length;
- final SuggestionsInfo[] retval = new SuggestionsInfo[length];
- for (int i = 0; i < length; ++i) {
- final String prevWord;
- if (sequentialWords && i > 0) {
- final String prevWordCandidate = textInfos[i - 1].getText();
- // Note that an empty string would be used to indicate the initial word
- // in the future.
- prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
- } else {
- prevWord = null;
- }
- retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit);
- retval[i].setCookieAndSequence(
- textInfos[i].getCookie(), textInfos[i].getSequence());
- }
- return retval;
- }
-
- // Note : this must be reentrant
- /**
- * Gets a list of suggestions for a specific string. This returns a list of possible
- * corrections for the text passed as an argument. It may split or group words, and
- * even perform grammatical analysis.
- */
- @Override
- public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
- final int suggestionsLimit) {
- return onGetSuggestions(textInfo, null, suggestionsLimit);
- }
-
- private SuggestionsInfo onGetSuggestions(
- final TextInfo textInfo, final String prevWord, final int suggestionsLimit) {
- try {
- final String inText = textInfo.getText();
- final SuggestionsParams cachedSuggestionsParams =
- mSuggestionsCache.getSuggestionsFromCache(inText);
- if (cachedSuggestionsParams != null) {
- if (DBG) {
- Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
- }
- return new SuggestionsInfo(
- cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
- }
-
- if (shouldFilterOut(inText, mScript)) {
- DictAndProximity dictInfo = null;
- try {
- dictInfo = mDictionaryPool.takeOrGetNull();
- if (null == dictInfo) return getNotInDictEmptySuggestions();
- return dictInfo.mDictionary.isValidWord(inText) ?
- getInDictEmptySuggestions() : getNotInDictEmptySuggestions();
- } finally {
- if (null != dictInfo) {
- if (!mDictionaryPool.offer(dictInfo)) {
- Log.e(TAG, "Can't re-insert a dictionary into its pool");
- }
- }
- }
- }
- final String text = inText.replaceAll(APOSTROPHE, SINGLE_QUOTE);
-
- // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
- final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text,
- mService.mSuggestionThreshold, mService.mRecommendedThreshold,
- suggestionsLimit);
- final WordComposer composer = new WordComposer();
- final int length = text.length();
- for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
- final int codePoint = text.codePointAt(i);
- // The getXYForCodePointAndScript method returns (Y << 16) + X
- final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript(
- codePoint, mScript);
- if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) {
- composer.add(codePoint, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE, null);
- } else {
- composer.add(codePoint, xy & 0xFFFF, xy >> 16, null);
- }
- }
-
- final int capitalizeType = getCapitalizationType(text);
- boolean isInDict = true;
- DictAndProximity dictInfo = null;
- try {
- dictInfo = mDictionaryPool.takeOrGetNull();
- if (null == dictInfo) return getNotInDictEmptySuggestions();
- dictInfo.mDictionary.getWords(composer, prevWord, suggestionsGatherer,
- dictInfo.mProximityInfo);
- isInDict = dictInfo.mDictionary.isValidWord(text);
- if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
- // We want to test the word again if it's all caps or first caps only.
- // If it's fully down, we already tested it, if it's mixed case, we don't
- // want to test a lowercase version of it.
- isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
- }
- } finally {
- if (null != dictInfo) {
- if (!mDictionaryPool.offer(dictInfo)) {
- Log.e(TAG, "Can't re-insert a dictionary into its pool");
- }
- }
- }
-
- final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(
- capitalizeType, mLocale);
-
- if (DBG) {
- Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
- + suggestionsLimit);
- Log.i(TAG, "IsInDict = " + isInDict);
- Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
- Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions);
- if (null != result.mSuggestions) {
- for (String suggestion : result.mSuggestions) {
- Log.i(TAG, suggestion);
- }
- }
- }
-
- final int flags =
- (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
- : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO)
- | (result.mHasRecommendedSuggestions
- ? SuggestionsInfoCompatUtils
- .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
- : 0);
- final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
- mSuggestionsCache.putSuggestionsToCache(text, result.mSuggestions, flags);
- return retval;
- } catch (RuntimeException e) {
- // Don't kill the keyboard if there is a bug in the spell checker
- if (DBG) {
- throw e;
- } else {
- Log.e(TAG, "Exception while spellcheking: " + e);
- return getNotInDictEmptySuggestions();
- }
- }
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
new file mode 100644
index 000000000..5a1bd37f5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.spellcheck;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.textservice.SentenceSuggestionsInfo;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.ArrayList;
+
+public class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
+ private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
+ private static final boolean DBG = false;
+ private final static String[] EMPTY_STRING_ARRAY = new String[0];
+
+ public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
+ super(service);
+ }
+
+ private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
+ SentenceSuggestionsInfo ssi) {
+ final String typedText = ti.getText();
+ if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
+ return null;
+ }
+ final int N = ssi.getSuggestionsCount();
+ final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList();
+ final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList();
+ final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
+ CollectionUtils.newArrayList();
+ String currentWord = null;
+ for (int i = 0; i < N; ++i) {
+ final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
+ final int flags = si.getSuggestionsAttributes();
+ if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
+ continue;
+ }
+ final int offset = ssi.getOffsetAt(i);
+ final int length = ssi.getLengthAt(i);
+ final String subText = typedText.substring(offset, offset + length);
+ final String prevWord = currentWord;
+ currentWord = subText;
+ if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
+ continue;
+ }
+ final String[] splitTexts =
+ subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1);
+ if (splitTexts == null || splitTexts.length <= 1) {
+ continue;
+ }
+ final int splitNum = splitTexts.length;
+ for (int j = 0; j < splitNum; ++j) {
+ final String splitText = splitTexts[j];
+ if (TextUtils.isEmpty(splitText)) {
+ continue;
+ }
+ if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) {
+ continue;
+ }
+ final int newLength = splitText.length();
+ // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
+ final int newFlags = 0;
+ final SuggestionsInfo newSi =
+ new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
+ newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
+ if (DBG) {
+ Log.d(TAG, "Override and remove old span over: " + splitText + ", "
+ + offset + "," + newLength);
+ }
+ additionalOffsets.add(offset);
+ additionalLengths.add(newLength);
+ additionalSuggestionsInfos.add(newSi);
+ }
+ }
+ final int additionalSize = additionalOffsets.size();
+ if (additionalSize <= 0) {
+ return null;
+ }
+ final int suggestionsSize = N + additionalSize;
+ final int[] newOffsets = new int[suggestionsSize];
+ final int[] newLengths = new int[suggestionsSize];
+ final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
+ int i;
+ for (i = 0; i < N; ++i) {
+ newOffsets[i] = ssi.getOffsetAt(i);
+ newLengths[i] = ssi.getLengthAt(i);
+ newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
+ }
+ for (; i < suggestionsSize; ++i) {
+ newOffsets[i] = additionalOffsets.get(i - N);
+ newLengths[i] = additionalLengths.get(i - N);
+ newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
+ }
+ return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
+ }
+
+ @Override
+ public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
+ int suggestionsLimit) {
+ final SentenceSuggestionsInfo[] retval =
+ super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
+ if (retval == null || retval.length != textInfos.length) {
+ return retval;
+ }
+ for (int i = 0; i < retval.length; ++i) {
+ final SentenceSuggestionsInfo tempSsi =
+ fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
+ if (tempSsi != null) {
+ retval[i] = tempSsi;
+ }
+ }
+ return retval;
+ }
+
+ @Override
+ public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
+ int suggestionsLimit, boolean sequentialWords) {
+ final int length = textInfos.length;
+ final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+ for (int i = 0; i < length; ++i) {
+ final String prevWord;
+ if (sequentialWords && i > 0) {
+ final String prevWordCandidate = textInfos[i - 1].getText();
+ // Note that an empty string would be used to indicate the initial word
+ // in the future.
+ prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
+ } else {
+ prevWord = null;
+ }
+ retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit);
+ retval[i].setCookieAndSequence(textInfos[i].getCookie(),
+ textInfos[i].getSequence());
+ }
+ return retval;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
new file mode 100644
index 000000000..8eb1eb68e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.spellcheck;
+
+import android.service.textservice.SpellCheckerService.Session;
+
+public abstract class AndroidSpellCheckerSessionFactory {
+ public static Session newInstance(AndroidSpellCheckerService service) {
+ return new AndroidSpellCheckerSession(service);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
new file mode 100644
index 000000000..f4784ff1a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.spellcheck;
+
+import android.service.textservice.SpellCheckerService.Session;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LruCache;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.LocaleUtils;
+import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public abstract class AndroidWordLevelSpellCheckerSession extends Session {
+ private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ // Immutable, but need the locale which is not available in the constructor yet
+ private DictionaryPool mDictionaryPool;
+ // Likewise
+ private Locale mLocale;
+ // Cache this for performance
+ private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
+ private final AndroidSpellCheckerService mService;
+ protected final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
+
+ private static class SuggestionsParams {
+ public final String[] mSuggestions;
+ public final int mFlags;
+ public SuggestionsParams(String[] suggestions, int flags) {
+ mSuggestions = suggestions;
+ mFlags = flags;
+ }
+ }
+
+ protected static class SuggestionsCache {
+ private static final char CHAR_DELIMITER = '\uFFFC';
+ private static final int MAX_CACHE_SIZE = 50;
+ private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
+ new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE);
+
+ // TODO: Support n-gram input
+ private static String generateKey(String query, String prevWord) {
+ if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWord)) {
+ return query;
+ }
+ return query + CHAR_DELIMITER + prevWord;
+ }
+
+ // TODO: Support n-gram input
+ public SuggestionsParams getSuggestionsFromCache(String query, String prevWord) {
+ return mUnigramSuggestionsInfoCache.get(generateKey(query, prevWord));
+ }
+
+ // TODO: Support n-gram input
+ public void putSuggestionsToCache(
+ String query, String prevWord, String[] suggestions, int flags) {
+ if (suggestions == null || TextUtils.isEmpty(query)) {
+ return;
+ }
+ mUnigramSuggestionsInfoCache.put(
+ generateKey(query, prevWord), new SuggestionsParams(suggestions, flags));
+ }
+ }
+
+ AndroidWordLevelSpellCheckerSession(final AndroidSpellCheckerService service) {
+ mService = service;
+ }
+
+ @Override
+ public void onCreate() {
+ final String localeString = getLocale();
+ mDictionaryPool = mService.getDictionaryPool(localeString);
+ mLocale = LocaleUtils.constructLocaleFromString(localeString);
+ mScript = AndroidSpellCheckerService.getScriptFromLocale(mLocale);
+ }
+
+ /*
+ * Returns whether the code point is a letter that makes sense for the specified
+ * locale for this spell checker.
+ * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
+ * and is limited to EFIGS languages and Russian.
+ * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
+ * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
+ */
+ private static boolean isLetterCheckableByLanguage(final int codePoint,
+ final int script) {
+ switch (script) {
+ case AndroidSpellCheckerService.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 AndroidSpellCheckerService.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);
+ default:
+ // Should never come here
+ throw new RuntimeException("Impossible value of script: " + script);
+ }
+ }
+
+ /**
+ * Finds out whether a particular string should be filtered out of spell checking.
+ *
+ * This will loosely match URLs, numbers, symbols. To avoid always underlining words that
+ * we know we will never recognize, this accepts a script identifier that should be one
+ * of the SCRIPT_* constants defined above, to rule out quickly characters from very
+ * different languages.
+ *
+ * @param text the string to evaluate.
+ * @param script the identifier for the script this spell checker recognizes
+ * @return true if we should filter this text out, false otherwise
+ */
+ private static boolean shouldFilterOut(final String text, final int script) {
+ if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
+
+ // TODO: check if an equivalent processing can't be done more quickly with a
+ // compiled regexp.
+ // Filter by first letter
+ final int firstCodePoint = text.codePointAt(0);
+ // Filter out words that don't start with a letter or an apostrophe
+ if (!isLetterCheckableByLanguage(firstCodePoint, script)
+ && '\'' != firstCodePoint) return true;
+
+ // Filter contents
+ final int length = text.length();
+ int letterCount = 0;
+ for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
+ final int codePoint = text.codePointAt(i);
+ // Any word containing a '@' is probably an e-mail address
+ // Any word containing a '/' is probably either an ad-hoc combination of two
+ // words or a URI - in either case we don't want to spell check that
+ if ('@' == codePoint || '/' == codePoint) return true;
+ if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
+ }
+ // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
+ // in this word are letters
+ return (letterCount * 4 < length * 3);
+ }
+
+ // Note : this must be reentrant
+ /**
+ * Gets a list of suggestions for a specific string. This returns a list of possible
+ * corrections for the text passed as an argument. It may split or group words, and
+ * even perform grammatical analysis.
+ */
+ @Override
+ public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
+ final int suggestionsLimit) {
+ return onGetSuggestions(textInfo, null, suggestionsLimit);
+ }
+
+ protected SuggestionsInfo onGetSuggestions(
+ final TextInfo textInfo, final String prevWord, final int suggestionsLimit) {
+ try {
+ final String inText = textInfo.getText();
+ final SuggestionsParams cachedSuggestionsParams =
+ mSuggestionsCache.getSuggestionsFromCache(inText, prevWord);
+ if (cachedSuggestionsParams != null) {
+ if (DBG) {
+ Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
+ }
+ return new SuggestionsInfo(
+ cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
+ }
+
+ if (shouldFilterOut(inText, mScript)) {
+ DictAndProximity dictInfo = null;
+ try {
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
+ if (!DictionaryPool.isAValidDictionary(dictInfo)) {
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ return dictInfo.mDictionary.isValidWord(inText)
+ ? AndroidSpellCheckerService.getInDictEmptySuggestions()
+ : AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ } finally {
+ if (null != dictInfo) {
+ if (!mDictionaryPool.offer(dictInfo)) {
+ Log.e(TAG, "Can't re-insert a dictionary into its pool");
+ }
+ }
+ }
+ }
+ final String text = inText.replaceAll(
+ AndroidSpellCheckerService.APOSTROPHE, AndroidSpellCheckerService.SINGLE_QUOTE);
+
+ // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
+ //final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text,
+ //mService.mSuggestionThreshold, mService.mRecommendedThreshold,
+ //suggestionsLimit);
+ final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer(
+ text, suggestionsLimit);
+ final WordComposer composer = new WordComposer();
+ final int length = text.length();
+ for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
+ final int codePoint = text.codePointAt(i);
+ // The getXYForCodePointAndScript method returns (Y << 16) + X
+ final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript(
+ codePoint, mScript);
+ if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) {
+ composer.add(codePoint,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ } else {
+ composer.add(codePoint, xy & 0xFFFF, xy >> 16);
+ }
+ }
+
+ final int capitalizeType = AndroidSpellCheckerService.getCapitalizationType(text);
+ boolean isInDict = true;
+ DictAndProximity dictInfo = null;
+ try {
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
+ if (!DictionaryPool.isAValidDictionary(dictInfo)) {
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ final ArrayList<SuggestedWordInfo> suggestions =
+ dictInfo.mDictionary.getSuggestions(composer, prevWord,
+ dictInfo.mProximityInfo);
+ for (final SuggestedWordInfo suggestion : suggestions) {
+ final String suggestionStr = suggestion.mWord.toString();
+ suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
+ suggestionStr.length(), suggestion.mScore);
+ }
+ isInDict = dictInfo.mDictionary.isValidWord(text);
+ if (!isInDict && AndroidSpellCheckerService.CAPITALIZE_NONE != capitalizeType) {
+ // We want to test the word again if it's all caps or first caps only.
+ // If it's fully down, we already tested it, if it's mixed case, we don't
+ // want to test a lowercase version of it.
+ isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
+ }
+ } finally {
+ if (null != dictInfo) {
+ if (!mDictionaryPool.offer(dictInfo)) {
+ Log.e(TAG, "Can't re-insert a dictionary into its pool");
+ }
+ }
+ }
+
+ final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(
+ capitalizeType, mLocale);
+
+ if (DBG) {
+ Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
+ + suggestionsLimit);
+ Log.i(TAG, "IsInDict = " + isInDict);
+ Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
+ Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions);
+ if (null != result.mSuggestions) {
+ for (String suggestion : result.mSuggestions) {
+ Log.i(TAG, suggestion);
+ }
+ }
+ }
+
+ final int flags =
+ (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+ : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO)
+ | (result.mHasRecommendedSuggestions
+ ? SuggestionsInfoCompatUtils
+ .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
+ : 0);
+ final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
+ mSuggestionsCache.putSuggestionsToCache(text, prevWord, result.mSuggestions, flags);
+ return retval;
+ } catch (RuntimeException e) {
+ // Don't kill the keyboard if there is a bug in the spell checker
+ if (DBG) {
+ throw e;
+ } else {
+ Log.e(TAG, "Exception while spellcheking: " + e);
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 8fc632ee7..53aa6c719 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -16,19 +16,56 @@
package com.android.inputmethod.latin.spellcheck;
+import android.util.Log;
+
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* A blocking queue that creates dictionaries up to a certain limit as necessary.
+ * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we
+ * will clear the queue and generate its contents again. This is transparent for
+ * the client code, but may help with sloppy clients.
*/
@SuppressWarnings("serial")
public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
+ private final static String TAG = DictionaryPool.class.getSimpleName();
+ // How many seconds we wait for a dictionary to become available. Past this delay, we give up in
+ // fear some bug caused a deadlock, and reset the whole pool.
+ private final static int TIMEOUT = 3;
private final AndroidSpellCheckerService mService;
private final int mMaxSize;
private final Locale mLocale;
private int mSize;
private volatile boolean mClosed;
+ final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList();
+ private final static DictAndProximity dummyDict = new DictAndProximity(
+ new Dictionary(Dictionary.TYPE_MAIN) {
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ return noSuggestions;
+ }
+ @Override
+ public boolean isValidWord(CharSequence word) {
+ // This is never called. However if for some strange reason it ever gets
+ // called, returning true is less destructive (it will not underline the
+ // word in red).
+ return true;
+ }
+ }, null);
+
+ static public boolean isAValidDictionary(final DictAndProximity dictInfo) {
+ return null != dictInfo && dummyDict != dictInfo;
+ }
public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service,
final Locale locale) {
@@ -41,13 +78,23 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
@Override
- public DictAndProximity take() throws InterruptedException {
+ public DictAndProximity poll(final long timeout, final TimeUnit unit)
+ throws InterruptedException {
final DictAndProximity dict = poll();
if (null != dict) return dict;
synchronized(this) {
if (mSize >= mMaxSize) {
- // Our pool is already full. Wait until some dictionary is ready.
- return super.take();
+ // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT
+ // expires to avoid a deadlock.
+ final DictAndProximity result = super.poll(timeout, unit);
+ if (null == result) {
+ Log.e(TAG, "Deadlock detected ! Resetting dictionary pool");
+ clear();
+ mSize = 1;
+ return mService.createDictAndProximity(mLocale);
+ } else {
+ return result;
+ }
} else {
++mSize;
return mService.createDictAndProximity(mLocale);
@@ -56,9 +103,9 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
// Convenience method
- public DictAndProximity takeOrGetNull() {
+ public DictAndProximity pollWithDefaultTimeout() {
try {
- return take();
+ return poll(TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return null;
}
@@ -78,7 +125,7 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
public boolean offer(final DictAndProximity dict) {
if (mClosed) {
dict.mDictionary.close();
- return false;
+ return super.offer(dummyDict);
} else {
return super.offer(dict);
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 0103e8423..fe5225ebd 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -16,14 +16,15 @@
package com.android.inputmethod.latin.spellcheck;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import java.util.TreeMap;
public class SpellCheckerProximityInfo {
/* public for test */
- final public static int NUL = KeyDetector.NOT_A_CODE;
+ final public static int NUL = Constants.NOT_A_CODE;
// This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
// native code - this value is passed at creation of the binary object and reused
@@ -59,7 +60,7 @@ public class SpellCheckerProximityInfo {
// character.
// Since we need to build such an array, we want to be able to search in our big proximity
// data quickly by character, and a map is probably the best way to do this.
- final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+ final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap();
// The proximity here is the union of
// - the proximity for a QWERTY keyboard.
@@ -111,6 +112,7 @@ public class SpellCheckerProximityInfo {
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
};
static {
buildProximityIndices(PROXIMITY, INDICES);
@@ -121,7 +123,7 @@ public class SpellCheckerProximityInfo {
}
private static class Cyrillic {
- final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+ final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap();
// TODO: The following table is solely based on the keyboard layout. Consult with Russian
// speakers on commonly misspelled words/letters.
final static int[] PROXIMITY = {
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index c6fe43b69..58b01aa55 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -42,10 +42,10 @@ public class MoreSuggestions extends Keyboard {
private int mToPos;
public static class MoreSuggestionsParam extends Keyboard.Params {
- private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mNumColumnsInRow = new int[SuggestionsView.MAX_SUGGESTIONS];
+ private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
private static final int MAX_COLUMNS_IN_ROW = 3;
private int mNumRows;
public Drawable mDivider;
@@ -63,7 +63,7 @@ public class MoreSuggestions extends Keyboard {
int row = 0;
int pos = fromPos, rowStartPos = fromPos;
- final int size = Math.min(suggestions.size(), SuggestionsView.MAX_SUGGESTIONS);
+ final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
while (pos < size) {
final String word = suggestions.getWord(pos).toString();
// TODO: Should take care of text x-scaling.
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 19287e3f3..5b23d7f3c 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -68,7 +68,7 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
@Override
public void onCodeInput(int primaryCode, int x, int y) {
final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
- if (index >= 0 && index < SuggestionsView.MAX_SUGGESTIONS) {
+ if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e86390b11..afc4293c0 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -57,22 +57,23 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.ViewLayoutUtils;
+import com.android.inputmethod.latin.AutoCorrection;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
-public class SuggestionsView extends RelativeLayout implements OnClickListener,
+public class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener {
public interface Listener {
- public boolean addWordToDictionary(String word);
- public void pickSuggestionManually(int index, CharSequence word, int x, int y);
+ public boolean addWordToUserDictionary(String word);
+ public void pickSuggestionManually(int index, CharSequence word);
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
@@ -88,9 +89,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
private final PopupWindow mMoreSuggestionsWindow;
- private final ArrayList<TextView> mWords = new ArrayList<TextView>();
- private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
- private final ArrayList<View> mDividers = new ArrayList<View>();
+ private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
+ private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
+ private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
@@ -98,24 +99,24 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private Listener mListener;
private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
- private final SuggestionsViewParams mParams;
+ private final SuggestionStripViewParams mParams;
private static final float MIN_TEXT_XSCALE = 0.70f;
private final UiHandler mHandler = new UiHandler(this);
- private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> {
+ private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
private static final int MSG_HIDE_PREVIEW = 0;
- public UiHandler(SuggestionsView outerInstance) {
+ public UiHandler(SuggestionStripView outerInstance) {
super(outerInstance);
}
@Override
public void dispatchMessage(Message msg) {
- final SuggestionsView suggestionsView = getOuterInstance();
+ final SuggestionStripView suggestionStripView = getOuterInstance();
switch (msg.what) {
case MSG_HIDE_PREVIEW:
- suggestionsView.hidePreview();
+ suggestionStripView.hidePreview();
break;
}
}
@@ -129,7 +130,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
}
- private static class SuggestionsViewParams {
+ private static class SuggestionStripViewParams {
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40;
private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
@@ -167,7 +168,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final int mSuggestionStripOption;
- private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+ private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList();
public boolean mMoreSuggestionsAvailable;
@@ -175,7 +176,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final TextView mLeftwardsArrowView;
private final TextView mHintToSaveView;
- public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle,
+ public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
mWords = words;
mDividers = dividers;
@@ -191,38 +192,39 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
final Resources res = word.getResources();
mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle);
- mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0);
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
+ mSuggestionStripOption = a.getInt(
+ R.styleable.SuggestionStripView_suggestionStripOption, 0);
final float alphaValidTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaValidTypedWord, 100);
+ R.styleable.SuggestionStripView_alphaValidTypedWord, 100);
final float alphaTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaTypedWord, 100);
+ R.styleable.SuggestionStripView_alphaTypedWord, 100);
final float alphaAutoCorrect = getPercent(a,
- R.styleable.SuggestionsView_alphaAutoCorrect, 100);
+ R.styleable.SuggestionStripView_alphaAutoCorrect, 100);
final float alphaSuggested = getPercent(a,
- R.styleable.SuggestionsView_alphaSuggested, 100);
- mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100);
- mColorValidTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0),
- alphaValidTypedWord);
- mColorTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord);
- mColorAutoCorrect = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorAutoCorrect, 0), alphaAutoCorrect);
- mColorSuggested = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorSuggested, 0), alphaSuggested);
+ R.styleable.SuggestionStripView_alphaSuggested, 100);
+ mAlphaObsoleted = getPercent(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 100);
+ mColorValidTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
+ mColorTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
+ mColorAutoCorrect = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
+ mColorSuggested = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
mSuggestionsCountInStrip = a.getInt(
- R.styleable.SuggestionsView_suggestionsCountInStrip,
+ R.styleable.SuggestionStripView_suggestionsCountInStrip,
DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
mCenterSuggestionWeight = getPercent(a,
- R.styleable.SuggestionsView_centerSuggestionPercentile,
+ R.styleable.SuggestionStripView_centerSuggestionPercentile,
DEFAULT_CENTER_SUGGESTION_PERCENTILE);
mMaxMoreSuggestionsRow = a.getInt(
- R.styleable.SuggestionsView_maxMoreSuggestionsRow,
+ R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
mMinMoreSuggestionsWidth = getRatio(a,
- R.styleable.SuggestionsView_minMoreSuggestionsWidth);
+ R.styleable.SuggestionStripView_minMoreSuggestionsWidth);
a.recycle();
mMoreSuggestionsHint = getMoreSuggestionsHint(res,
@@ -336,8 +338,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
// If we auto-correct, then the autocorrection is in slot 0 and the typed word
// is in slot 1.
- if (index == mCenterSuggestionIndex && suggestedWords.mHasAutoCorrectionCandidate
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(
+ if (index == mCenterSuggestionIndex
+ && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}
@@ -596,15 +598,15 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
/**
- * Construct a {@link SuggestionsView} for showing suggestions to be picked by the user.
+ * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
* @param context
* @param attrs
*/
- public SuggestionsView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.suggestionsViewStyle);
+ public SuggestionStripView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.suggestionStripViewStyle);
}
- public SuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -631,7 +633,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
}
- mParams = new SuggestionsViewParams(context, attrs, defStyle, mWords, mDividers, mInfos);
+ mParams = new SuggestionStripViewParams(
+ context, attrs, defStyle, mWords, mDividers, mInfos);
mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
@@ -677,7 +680,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mSuggestedWords = suggestedWords;
mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.suggestionsView_setSuggestions(mSuggestedWords);
+ ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
}
}
@@ -718,19 +721,13 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mPreviewPopup.dismiss();
}
- private void addToDictionary(CharSequence word) {
- mListener.addWordToDictionary(word.toString());
- }
-
private final KeyboardActionListener mMoreSuggestionsListener =
new KeyboardActionListener.Adapter() {
@Override
public boolean onCustomRequest(int requestCode) {
final int index = requestCode;
final CharSequence word = mSuggestedWords.getWord(index);
- // TODO: change caller path so coordinates are passed through here
- mListener.pickSuggestionManually(index, word, NOT_A_TOUCH_COORDINATE,
- NOT_A_TOUCH_COORDINATE);
+ mListener.pickSuggestionManually(index, word);
dismissMoreSuggestions();
return true;
}
@@ -763,7 +760,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
private boolean showMoreSuggestions() {
- final SuggestionsViewParams params = mParams;
+ final SuggestionStripViewParams params = mParams;
if (params.mMoreSuggestionsAvailable) {
final int stripWidth = getWidth();
final View container = mMoreSuggestionsContainer;
@@ -863,7 +860,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
@Override
public void onClick(View view) {
if (mParams.isAddToDictionaryShowing(view)) {
- addToDictionary(mParams.getAddToDictionaryWord());
+ mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
clear();
return;
}
@@ -876,7 +873,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
return;
final CharSequence word = mSuggestedWords.getWord(index);
- mListener.pickSuggestionManually(index, word, mLastX, mLastY);
+ mListener.pickSuggestionManually(index, word);
}
@Override
diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java
new file mode 100644
index 000000000..5124a35a6
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Arrange for the uploading service to be run on regular intervals.
+ */
+public final class BootBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ ResearchLogger.scheduleUploadingService(context);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java
new file mode 100644
index 000000000..11eae8813
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.CheckBox;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackActivity extends Activity {
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.research_feedback_activity);
+ final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout);
+ final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history);
+ final CharSequence cs = checkbox.getText();
+ final String actualString = String.format(cs.toString(),
+ ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE);
+ checkbox.setText(actualString);
+ layout.setActivity(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onBackPressed() {
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ super.onBackPressed();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
new file mode 100644
index 000000000..a2e08e2b7
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackFragment extends Fragment {
+ private EditText mEditText;
+ private CheckBox mCheckBox;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
+ false);
+ mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
+ mCheckBox = (CheckBox) view.findViewById(R.id.research_feedback_include_history);
+
+ final Button sendButton = (Button) view.findViewById(
+ R.id.research_feedback_send_button);
+ sendButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Editable editable = mEditText.getText();
+ final String feedbackContents = editable.toString();
+ final boolean includeHistory = mCheckBox.isChecked();
+ ResearchLogger.getInstance().sendFeedback(feedbackContents, includeHistory);
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ final Button cancelButton = (Button) view.findViewById(
+ R.id.research_feedback_cancel_button);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackLayout.java b/java/src/com/android/inputmethod/research/FeedbackLayout.java
new file mode 100644
index 000000000..f2cbfe308
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackLayout.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+public class FeedbackLayout extends LinearLayout {
+ private Activity mActivity;
+
+ public FeedbackLayout(Context context) {
+ super(context);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs, int defstyle) {
+ super(context, attrs, defstyle);
+ }
+
+ public void setActivity(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ state.startTracking(event, this);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP
+ && !event.isCanceled() && state.isTracking(event)) {
+ mActivity.onBackPressed();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java
new file mode 100644
index 000000000..ae7b1579a
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/LogBuffer.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.LinkedList;
+
+/**
+ * A buffer that holds a fixed number of LogUnits.
+ *
+ * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are
+ * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches
+ * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to
+ * stay under the capacity limit.
+ */
+public class LogBuffer {
+ protected final LinkedList<LogUnit> mLogUnits;
+ /* package for test */ int mWordCapacity;
+ // The number of members of mLogUnits that are actual words.
+ protected int mNumActualWords;
+
+ /**
+ * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and
+ * unlimited number of non-word LogUnits), and that outputs its result to a researchLog.
+ *
+ * @param wordCapacity maximum number of words
+ */
+ LogBuffer(final int wordCapacity) {
+ if (wordCapacity <= 0) {
+ throw new IllegalArgumentException("wordCapacity must be 1 or greater.");
+ }
+ mLogUnits = CollectionUtils.newLinkedList();
+ mWordCapacity = wordCapacity;
+ mNumActualWords = 0;
+ }
+
+ /**
+ * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's
+ * (oldest first) if word capacity is reached.
+ */
+ public void shiftIn(LogUnit newLogUnit) {
+ if (newLogUnit.getWord() == null) {
+ // This LogUnit isn't a word, so it doesn't count toward the word-limit.
+ mLogUnits.add(newLogUnit);
+ return;
+ }
+ if (mNumActualWords == mWordCapacity) {
+ shiftOutThroughFirstWord();
+ }
+ mLogUnits.add(newLogUnit);
+ mNumActualWords++; // Must be a word, or we wouldn't be here.
+ }
+
+ private void shiftOutThroughFirstWord() {
+ while (!mLogUnits.isEmpty()) {
+ final LogUnit logUnit = mLogUnits.removeFirst();
+ onShiftOut(logUnit);
+ if (logUnit.hasWord()) {
+ // Successfully shifted out a word-containing LogUnit and made space for the new
+ // LogUnit.
+ mNumActualWords--;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Removes all LogUnits from the buffer without calling onShiftOut().
+ */
+ public void clear() {
+ mLogUnits.clear();
+ mNumActualWords = 0;
+ }
+
+ /**
+ * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are
+ * removed in the order entered. This method is not called when shiftOut is called directly.
+ *
+ * Base class does nothing; subclasses may override.
+ */
+ protected void onShiftOut(LogUnit logUnit) {
+ }
+
+ /**
+ * Called to deliberately remove the oldest LogUnit. Usually called when draining the
+ * LogBuffer.
+ */
+ public LogUnit shiftOut() {
+ if (mLogUnits.isEmpty()) {
+ return null;
+ }
+ final LogUnit logUnit = mLogUnits.removeFirst();
+ if (logUnit.hasWord()) {
+ mNumActualWords--;
+ }
+ return logUnit;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
new file mode 100644
index 000000000..d8b3a29ff
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A group of log statements related to each other.
+ *
+ * A LogUnit is collection of LogStatements, each of which is generated by at a particular point
+ * in the code. (There is no LogStatement class; the data is stored across the instance variables
+ * here.) A single LogUnit's statements can correspond to all the calls made while in the same
+ * composing region, or all the calls between committing the last composing region, and the first
+ * character of the next composing region.
+ *
+ * Individual statements in a log may be marked as potentially private. If so, then they are only
+ * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit
+ * will not violate the user's privacy. Checks for this may include whether other LogUnits have
+ * been published recently, or whether the LogUnit contains numbers, etc.
+ */
+/* package */ class LogUnit {
+ private final ArrayList<String[]> mKeysList = CollectionUtils.newArrayList();
+ private final ArrayList<Object[]> mValuesList = CollectionUtils.newArrayList();
+ private final ArrayList<Boolean> mIsPotentiallyPrivate = CollectionUtils.newArrayList();
+ private String mWord;
+ private boolean mContainsDigit;
+
+ public void addLogStatement(final String[] keys, final Object[] values,
+ final Boolean isPotentiallyPrivate) {
+ mKeysList.add(keys);
+ mValuesList.add(values);
+ mIsPotentiallyPrivate.add(isPotentiallyPrivate);
+ }
+
+ public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ final int size = mKeysList.size();
+ for (int i = 0; i < size; i++) {
+ if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) {
+ researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
+ }
+ }
+ }
+
+ public void setWord(String word) {
+ mWord = word;
+ }
+
+ public String getWord() {
+ return mWord;
+ }
+
+ public boolean hasWord() {
+ return mWord != null;
+ }
+
+ public void setContainsDigit() {
+ mContainsDigit = true;
+ }
+
+ public boolean hasDigit() {
+ return mContainsDigit;
+ }
+
+ public boolean isEmpty() {
+ return mKeysList.isEmpty();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java
new file mode 100644
index 000000000..745768d35
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.Suggest;
+
+import java.util.Random;
+
+public class MainLogBuffer extends LogBuffer {
+ // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams.
+ private static final int N_GRAM_SIZE = 2;
+ // The number of words between n-grams to omit from the log.
+ private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18;
+
+ private final ResearchLog mResearchLog;
+ private Suggest mSuggest;
+
+ // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if
+ // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc.
+ // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a
+ // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe
+ // n-gram does appear.
+ /* package for test */ int mMinWordPeriod;
+
+ // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod
+ // after a sample is taken.
+ /* package for test */ int mWordsUntilSafeToSample;
+
+ public MainLogBuffer(final ResearchLog researchLog) {
+ super(N_GRAM_SIZE);
+ mResearchLog = researchLog;
+ mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE;
+ final Random random = new Random();
+ mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod);
+ }
+
+ public void setSuggest(Suggest suggest) {
+ mSuggest = suggest;
+ }
+
+ @Override
+ public void shiftIn(final LogUnit newLogUnit) {
+ super.shiftIn(newLogUnit);
+ if (newLogUnit.hasWord()) {
+ if (mWordsUntilSafeToSample > 0) {
+ mWordsUntilSafeToSample--;
+ }
+ }
+ }
+
+ public void resetWordCounter() {
+ mWordsUntilSafeToSample = mMinWordPeriod;
+ }
+
+ /**
+ * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete
+ * form and still protect the user's privacy.
+ *
+ * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any
+ * non-character data that is typed between words. The decision about privacy is made based on
+ * the buffer's entire content. If it is decided that the privacy risks are too great to upload
+ * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g.,
+ * the screen orientation and other characteristics about the device can be uploaded without
+ * revealing much about the user.
+ */
+ public boolean isSafeToLog() {
+ // Check that we are not sampling too frequently. Having sampled recently might disclose
+ // too much of the user's intended meaning.
+ if (mWordsUntilSafeToSample > 0) {
+ return false;
+ }
+ if (mSuggest == null || !mSuggest.hasMainDictionary()) {
+ // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word
+ // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to
+ // potentially pose a privacy risk.
+ return false;
+ }
+ // Reload the dictionary in case it has changed (e.g., because the user has changed
+ // languages).
+ final Dictionary dictionary = mSuggest.getMainDictionary();
+ if (dictionary == null) {
+ return false;
+ }
+ // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the
+ // complete buffer contents in detail.
+ final int length = mLogUnits.size();
+ for (int i = 0; i < length; i++) {
+ final LogUnit logUnit = mLogUnits.get(i);
+ final String word = logUnit.getWord();
+ if (word == null) {
+ // Digits outside words are a privacy threat.
+ if (logUnit.hasDigit()) {
+ return false;
+ }
+ } else {
+ // Words not in the dictionary are a privacy threat.
+ if (!(dictionary.isValidWord(word))) {
+ return false;
+ }
+ }
+ }
+ // All checks have passed; this buffer's content can be safely uploaded.
+ return true;
+ }
+
+ @Override
+ protected void onShiftOut(LogUnit logUnit) {
+ if (mResearchLog != null) {
+ mResearchLog.publish(logUnit, false /* isIncludingPrivateData */);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
new file mode 100644
index 000000000..71a6d6a78
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.content.SharedPreferences;
+import android.os.SystemClock;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.view.inputmethod.CompletionInfo;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.define.ProductionFlag;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Logs the use of the LatinIME keyboard.
+ *
+ * This class logs operations on the IME keyboard, including what the user has typed.
+ * Data is stored locally in a file in app-specific storage.
+ *
+ * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
+ */
+public class ResearchLog {
+ private static final String TAG = ResearchLog.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
+ private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4;
+
+ /* package */ final ScheduledExecutorService mExecutor;
+ /* package */ final File mFile;
+ private JsonWriter mJsonWriter = NULL_JSON_WRITER;
+ // true if at least one byte of data has been written out to the log file. This must be
+ // remembered because JsonWriter requires that calls matching calls to beginObject and
+ // endObject, as well as beginArray and endArray, and the file is opened lazily, only when
+ // it is certain that data will be written. Alternatively, the matching call exceptions
+ // could be caught, but this might suppress other errors.
+ private boolean mHasWrittenData = false;
+
+ private static final JsonWriter NULL_JSON_WRITER = new JsonWriter(
+ new OutputStreamWriter(new NullOutputStream()));
+ private static class NullOutputStream extends OutputStream {
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer, int offset, int count) {
+ // nop
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer) {
+ // nop
+ }
+
+ @Override
+ public void write(int oneByte) {
+ }
+ }
+
+ public ResearchLog(final File outputFile) {
+ if (outputFile == null) {
+ throw new IllegalArgumentException();
+ }
+ mExecutor = Executors.newSingleThreadScheduledExecutor();
+ mFile = outputFile;
+ }
+
+ public synchronized void close() {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ if (mHasWrittenData) {
+ mJsonWriter.endArray();
+ mJsonWriter.flush();
+ mJsonWriter.close();
+ mHasWrittenData = false;
+ }
+ } catch (Exception e) {
+ Log.d(TAG, "error when closing ResearchLog:");
+ e.printStackTrace();
+ } finally {
+ if (mFile.exists()) {
+ mFile.setWritable(false, false);
+ }
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ }
+
+ private boolean mIsAbortSuccessful;
+
+ public synchronized void abort() {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ if (mHasWrittenData) {
+ mJsonWriter.endArray();
+ mJsonWriter.close();
+ mHasWrittenData = false;
+ }
+ } finally {
+ mIsAbortSuccessful = mFile.delete();
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ }
+
+ public boolean blockingAbort() throws InterruptedException {
+ abort();
+ mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
+ return mIsAbortSuccessful;
+ }
+
+ public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException {
+ mExecutor.awaitTermination(delay, timeUnit);
+ }
+
+ /* package */ synchronized void flush() {
+ removeAnyScheduledFlush();
+ mExecutor.submit(mFlushCallable);
+ }
+
+ private final Callable<Object> mFlushCallable = new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ mJsonWriter.flush();
+ return null;
+ }
+ };
+
+ private ScheduledFuture<Object> mFlushFuture;
+
+ private void removeAnyScheduledFlush() {
+ if (mFlushFuture != null) {
+ mFlushFuture.cancel(false);
+ mFlushFuture = null;
+ }
+ }
+
+ private void scheduleFlush() {
+ removeAnyScheduledFlush();
+ mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) {
+ try {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ logUnit.publishTo(ResearchLog.this, isIncludingPrivateData);
+ scheduleFlush();
+ return null;
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ // TODO: Add code to record loss of data, and report.
+ }
+ }
+
+ private static final String CURRENT_TIME_KEY = "_ct";
+ private static final String UPTIME_KEY = "_ut";
+ private static final String EVENT_TYPE_KEY = "_ty";
+
+ void outputEvent(final String[] keys, final Object[] values) {
+ // Not thread safe.
+ if (keys.length == 0) {
+ return;
+ }
+ if (DEBUG) {
+ if (keys.length != values.length + 1) {
+ Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]);
+ }
+ }
+ try {
+ if (mJsonWriter == NULL_JSON_WRITER) {
+ mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
+ mJsonWriter.beginArray();
+ mHasWrittenData = true;
+ }
+ mJsonWriter.beginObject();
+ mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
+ mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis());
+ mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]);
+ final int length = values.length;
+ for (int i = 0; i < length; i++) {
+ mJsonWriter.name(keys[i + 1]);
+ Object value = values[i];
+ if (value instanceof CharSequence) {
+ mJsonWriter.value(value.toString());
+ } else if (value instanceof Number) {
+ mJsonWriter.value((Number) value);
+ } else if (value instanceof Boolean) {
+ mJsonWriter.value((Boolean) value);
+ } else if (value instanceof CompletionInfo[]) {
+ CompletionInfo[] ci = (CompletionInfo[]) value;
+ mJsonWriter.beginArray();
+ for (int j = 0; j < ci.length; j++) {
+ mJsonWriter.value(ci[j].toString());
+ }
+ mJsonWriter.endArray();
+ } else if (value instanceof SharedPreferences) {
+ SharedPreferences prefs = (SharedPreferences) value;
+ mJsonWriter.beginObject();
+ for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
+ mJsonWriter.name(entry.getKey());
+ final Object innerValue = entry.getValue();
+ if (innerValue == null) {
+ mJsonWriter.nullValue();
+ } else if (innerValue instanceof Boolean) {
+ mJsonWriter.value((Boolean) innerValue);
+ } else if (innerValue instanceof Number) {
+ mJsonWriter.value((Number) innerValue);
+ } else {
+ mJsonWriter.value(innerValue.toString());
+ }
+ }
+ mJsonWriter.endObject();
+ } else if (value instanceof Key[]) {
+ Key[] keyboardKeys = (Key[]) value;
+ mJsonWriter.beginArray();
+ for (Key keyboardKey : keyboardKeys) {
+ mJsonWriter.beginObject();
+ mJsonWriter.name("code").value(keyboardKey.mCode);
+ mJsonWriter.name("altCode").value(keyboardKey.mAltCode);
+ mJsonWriter.name("x").value(keyboardKey.mX);
+ mJsonWriter.name("y").value(keyboardKey.mY);
+ mJsonWriter.name("w").value(keyboardKey.mWidth);
+ mJsonWriter.name("h").value(keyboardKey.mHeight);
+ mJsonWriter.endObject();
+ }
+ mJsonWriter.endArray();
+ } else if (value instanceof SuggestedWords) {
+ SuggestedWords words = (SuggestedWords) value;
+ mJsonWriter.beginObject();
+ mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
+ mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
+ mJsonWriter.name("isPunctuationSuggestions")
+ .value(words.mIsPunctuationSuggestions);
+ mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
+ mJsonWriter.name("isPrediction").value(words.mIsPrediction);
+ mJsonWriter.name("words");
+ mJsonWriter.beginArray();
+ final int size = words.size();
+ for (int j = 0; j < size; j++) {
+ SuggestedWordInfo wordInfo = words.getWordInfo(j);
+ mJsonWriter.value(wordInfo.toString());
+ }
+ mJsonWriter.endArray();
+ mJsonWriter.endObject();
+ } else if (value == null) {
+ mJsonWriter.nullValue();
+ } else {
+ Log.w(TAG, "Unrecognized type to be logged: " +
+ (value == null ? "<null>" : value.getClass().getName()));
+ mJsonWriter.nullValue();
+ }
+ }
+ mJsonWriter.endObject();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.w(TAG, "Error in JsonWriter; disabling logging");
+ try {
+ mJsonWriter.close();
+ } catch (IllegalStateException e1) {
+ // Assume that this is just the json not being terminated properly.
+ // Ignore
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } finally {
+ mJsonWriter = NULL_JSON_WRITER;
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
new file mode 100644
index 000000000..918fcf5a1
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -0,0 +1,1270 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
+
+import android.app.AlarmManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputConnection;
+import com.android.inputmethod.latin.RichInputConnection.Range;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.define.ProductionFlag;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.UUID;
+
+/**
+ * Logs the use of the LatinIME keyboard.
+ *
+ * This class logs operations on the IME keyboard, including what the user has typed.
+ * Data is stored locally in a file in app-specific storage.
+ *
+ * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
+ */
+public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = ResearchLogger.class.getSimpleName();
+ private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
+ public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
+ /* package */ static boolean sIsLogging = false;
+ private static final int OUTPUT_FORMAT_VERSION = 1;
+ private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
+ private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash";
+ /* package */ static final String FILENAME_PREFIX = "researchLog";
+ private static final String FILENAME_SUFFIX = ".txt";
+ private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
+ new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
+ private static final boolean IS_SHOWING_INDICATOR = true;
+ private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false;
+ public static final int FEEDBACK_WORD_BUFFER_SIZE = 5;
+
+ // constants related to specific log points
+ private static final String WHITESPACE_SEPARATORS = " \t\n\r";
+ private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1
+ private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid";
+
+ private static final ResearchLogger sInstance = new ResearchLogger();
+ // to write to a different filename, e.g., for testing, set mFile before calling start()
+ /* package */ File mFilesDir;
+ /* package */ String mUUIDString;
+ /* package */ ResearchLog mMainResearchLog;
+ // mFeedbackLog records all events for the session, private or not (excepting
+ // passwords). It is written to permanent storage only if the user explicitly commands
+ // the system to do so.
+ // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are
+ // complete.
+ /* package */ ResearchLog mFeedbackLog;
+ /* package */ MainLogBuffer mMainLogBuffer;
+ /* package */ LogBuffer mFeedbackLogBuffer;
+
+ private boolean mIsPasswordView = false;
+ private boolean mIsLoggingSuspended = false;
+ private SharedPreferences mPrefs;
+
+ // digits entered by the user are replaced with this codepoint.
+ /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
+ Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area"
+ // U+E001 is in the "private-use area"
+ /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
+ private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
+ private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
+ private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
+ protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
+ // set when LatinIME should ignore an onUpdateSelection() callback that
+ // arises from operations in this class
+ private static boolean sLatinIMEExpectingUpdateSelection = false;
+
+ // used to check whether words are not unique
+ private Suggest mSuggest;
+ private Dictionary mDictionary;
+ private MainKeyboardView mMainKeyboardView;
+ private InputMethodService mInputMethodService;
+ private final Statistics mStatistics;
+
+ private Intent mUploadIntent;
+ private PendingIntent mUploadPendingIntent;
+
+ private LogUnit mCurrentLogUnit = new LogUnit();
+
+ private ResearchLogger() {
+ mStatistics = Statistics.getInstance();
+ }
+
+ public static ResearchLogger getInstance() {
+ return sInstance;
+ }
+
+ public void init(final InputMethodService ims, final SharedPreferences prefs) {
+ assert ims != null;
+ if (ims == null) {
+ Log.w(TAG, "IMS is null; logging is off");
+ } else {
+ mFilesDir = ims.getFilesDir();
+ if (mFilesDir == null || !mFilesDir.exists()) {
+ Log.w(TAG, "IME storage directory does not exist.");
+ }
+ }
+ if (prefs != null) {
+ mUUIDString = getUUID(prefs);
+ if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
+ Editor e = prefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE);
+ e.apply();
+ }
+ sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
+ prefs.registerOnSharedPreferenceChangeListener(this);
+
+ final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L);
+ final long now = System.currentTimeMillis();
+ if (lastCleanupTime + DURATION_BETWEEN_DIR_CLEANUP_IN_MS < now) {
+ final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS;
+ cleanupLoggingDir(mFilesDir, timeHorizon);
+ Editor e = prefs.edit();
+ e.putLong(PREF_LAST_CLEANUP_TIME, now);
+ e.apply();
+ }
+ }
+ mInputMethodService = ims;
+ mPrefs = prefs;
+ mUploadIntent = new Intent(mInputMethodService, UploaderService.class);
+ mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0);
+
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ scheduleUploadingService(mInputMethodService);
+ }
+ }
+
+ /**
+ * Arrange for the UploaderService to be run on a regular basis.
+ *
+ * Any existing scheduled invocation of UploaderService is removed and rescheduled. This may
+ * cause problems if this method is called often and frequent updates are required, but since
+ * the user will likely be sleeping at some point, if the interval is less that the expected
+ * sleep duration and this method is not called during that time, the service should be invoked
+ * at some point.
+ */
+ public static void scheduleUploadingService(Context context) {
+ final Intent intent = new Intent(context, UploaderService.class);
+ final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
+ final AlarmManager manager =
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ manager.cancel(pendingIntent);
+ manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent);
+ }
+
+ private void cleanupLoggingDir(final File dir, final long time) {
+ for (File file : dir.listFiles()) {
+ if (file.getName().startsWith(ResearchLogger.FILENAME_PREFIX) &&
+ file.lastModified() < time) {
+ file.delete();
+ }
+ }
+ }
+
+ public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) {
+ mMainKeyboardView = mainKeyboardView;
+ maybeShowSplashScreen();
+ }
+
+ public void mainKeyboardView_onDetachedFromWindow() {
+ mMainKeyboardView = null;
+ }
+
+ private boolean hasSeenSplash() {
+ return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false);
+ }
+
+ private Dialog mSplashDialog = null;
+
+ private void maybeShowSplashScreen() {
+ if (hasSeenSplash()) {
+ return;
+ }
+ if (mSplashDialog != null && mSplashDialog.isShowing()) {
+ return;
+ }
+ final IBinder windowToken = mMainKeyboardView != null
+ ? mMainKeyboardView.getWindowToken() : null;
+ if (windowToken == null) {
+ return;
+ }
+ mSplashDialog = new Dialog(mInputMethodService, android.R.style.Theme_Holo_Dialog);
+ mSplashDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mSplashDialog.setContentView(R.layout.research_splash);
+ mSplashDialog.setCancelable(true);
+ final Window w = mSplashDialog.getWindow();
+ final WindowManager.LayoutParams lp = w.getAttributes();
+ lp.token = windowToken;
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ w.setAttributes(lp);
+ w.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ mSplashDialog.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mInputMethodService.requestHideSelf(0);
+ }
+ });
+ final Button doNotLogButton = (Button) mSplashDialog.findViewById(
+ R.id.research_do_not_log_button);
+ doNotLogButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onUserLoggingElection(false);
+ mSplashDialog.dismiss();
+ }
+ });
+ final Button doLogButton = (Button) mSplashDialog.findViewById(R.id.research_do_log_button);
+ doLogButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onUserLoggingElection(true);
+ mSplashDialog.dismiss();
+ }
+ });
+ mSplashDialog.show();
+ }
+
+ public void onUserLoggingElection(final boolean enableLogging) {
+ setLoggingAllowed(enableLogging);
+ if (mPrefs == null) {
+ return;
+ }
+ final Editor e = mPrefs.edit();
+ e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true);
+ e.apply();
+ restart();
+ }
+
+ private void setLoggingAllowed(boolean enableLogging) {
+ if (mPrefs == null) {
+ return;
+ }
+ Editor e = mPrefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
+ e.apply();
+ sIsLogging = enableLogging;
+ }
+
+ private File createLogFile(File filesDir) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(FILENAME_PREFIX).append('-');
+ sb.append(mUUIDString).append('-');
+ sb.append(TIMESTAMP_DATEFORMAT.format(new Date()));
+ sb.append(FILENAME_SUFFIX);
+ return new File(filesDir, sb.toString());
+ }
+
+ private void checkForEmptyEditor() {
+ if (mInputMethodService == null) {
+ return;
+ }
+ final InputConnection ic = mInputMethodService.getCurrentInputConnection();
+ if (ic == null) {
+ return;
+ }
+ final CharSequence textBefore = ic.getTextBeforeCursor(1, 0);
+ if (!TextUtils.isEmpty(textBefore)) {
+ mStatistics.setIsEmptyUponStarting(false);
+ return;
+ }
+ final CharSequence textAfter = ic.getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(textAfter)) {
+ mStatistics.setIsEmptyUponStarting(false);
+ return;
+ }
+ if (textBefore != null && textAfter != null) {
+ mStatistics.setIsEmptyUponStarting(true);
+ }
+ }
+
+ private void start() {
+ maybeShowSplashScreen();
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ mStatistics.reset();
+ checkForEmptyEditor();
+ if (!isAllowedToLog()) {
+ // Log.w(TAG, "not in usability mode; not logging");
+ return;
+ }
+ if (mFilesDir == null || !mFilesDir.exists()) {
+ Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
+ return;
+ }
+ if (mMainLogBuffer == null) {
+ mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ mMainLogBuffer = new MainLogBuffer(mMainResearchLog);
+ mMainLogBuffer.setSuggest(mSuggest);
+ }
+ if (mFeedbackLogBuffer == null) {
+ mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold
+ // the feedback LogUnit itself.
+ mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1);
+ }
+ }
+
+ /* package */ void stop() {
+ logStatistics();
+ commitCurrentLogUnit();
+
+ if (mMainLogBuffer != null) {
+ publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */);
+ mMainResearchLog.close();
+ mMainLogBuffer = null;
+ }
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLog.close();
+ mFeedbackLogBuffer = null;
+ }
+ }
+
+ public boolean abort() {
+ boolean didAbortMainLog = false;
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.clear();
+ try {
+ didAbortMainLog = mMainResearchLog.blockingAbort();
+ } catch (InterruptedException e) {
+ // Don't know whether this succeeded or not. We assume not; this is reported
+ // to the caller.
+ }
+ mMainLogBuffer = null;
+ }
+ boolean didAbortFeedbackLog = false;
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLogBuffer.clear();
+ try {
+ didAbortFeedbackLog = mFeedbackLog.blockingAbort();
+ } catch (InterruptedException e) {
+ // Don't know whether this succeeded or not. We assume not; this is reported
+ // to the caller.
+ }
+ mFeedbackLogBuffer = null;
+ }
+ return didAbortMainLog && didAbortFeedbackLog;
+ }
+
+ private void restart() {
+ stop();
+ start();
+ }
+
+ private long mResumeTime = 0L;
+ private void suspendLoggingUntil(long time) {
+ mIsLoggingSuspended = true;
+ mResumeTime = time;
+ requestIndicatorRedraw();
+ }
+
+ private void resumeLogging() {
+ mResumeTime = 0L;
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ if (isAllowedToLog()) {
+ restart();
+ }
+ }
+
+ private void updateSuspendedState() {
+ final long time = System.currentTimeMillis();
+ if (time > mResumeTime) {
+ mIsLoggingSuspended = false;
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (key == null || prefs == null) {
+ return;
+ }
+ sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
+ if (sIsLogging == false) {
+ abort();
+ }
+ requestIndicatorRedraw();
+ mPrefs = prefs;
+ prefsChanged(prefs);
+ }
+
+ public void presentResearchDialog(final LatinIME latinIME) {
+ if (mInFeedbackDialog) {
+ Toast.makeText(latinIME, R.string.research_please_exit_feedback_form,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
+ final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
+ final CharSequence[] items = new CharSequence[] {
+ latinIME.getString(R.string.research_feedback_menu_option),
+ showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
+ latinIME.getString(R.string.research_do_not_log_this_session)
+ };
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int position) {
+ di.dismiss();
+ switch (position) {
+ case 0:
+ presentFeedbackDialog(latinIME);
+ break;
+ case 1:
+ if (showEnable) {
+ if (!sIsLogging) {
+ setLoggingAllowed(true);
+ }
+ resumeLogging();
+ Toast.makeText(latinIME,
+ R.string.research_notify_session_logging_enabled,
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast toast = Toast.makeText(latinIME,
+ R.string.research_notify_session_log_deleting,
+ Toast.LENGTH_LONG);
+ toast.show();
+ boolean isLogDeleted = abort();
+ final long currentTime = System.currentTimeMillis();
+ final long resumeTime = currentTime + 1000 * 60 *
+ SUSPEND_DURATION_IN_MINUTES;
+ suspendLoggingUntil(resumeTime);
+ toast.cancel();
+ Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
+ Toast.LENGTH_LONG).show();
+ }
+ break;
+ }
+ }
+
+ };
+ final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME)
+ .setItems(items, listener)
+ .setTitle(title);
+ latinIME.showOptionDialog(builder.create());
+ }
+
+ private boolean mInFeedbackDialog = false;
+ public void presentFeedbackDialog(LatinIME latinIME) {
+ mInFeedbackDialog = true;
+ latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
+ }
+
+ private static final String[] EVENTKEYS_FEEDBACK = {
+ "UserTimestamp", "contents"
+ };
+ public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+ if (mFeedbackLogBuffer == null) {
+ return;
+ }
+ if (includeHistory) {
+ commitCurrentLogUnit();
+ } else {
+ mFeedbackLogBuffer.clear();
+ }
+ final LogUnit feedbackLogUnit = new LogUnit();
+ final Object[] values = {
+ feedbackContents
+ };
+ feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values,
+ false /* isPotentiallyPrivate */);
+ mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
+ publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */);
+ mFeedbackLog.close();
+ uploadNow();
+ mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ }
+
+ public void uploadNow() {
+ mInputMethodService.startService(mUploadIntent);
+ }
+
+ public void onLeavingSendFeedbackDialog() {
+ mInFeedbackDialog = false;
+ }
+
+ public void initSuggest(Suggest suggest) {
+ mSuggest = suggest;
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.setSuggest(mSuggest);
+ }
+ }
+
+ private void setIsPasswordView(boolean isPasswordView) {
+ mIsPasswordView = isPasswordView;
+ }
+
+ private boolean isAllowedToLog() {
+ return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog;
+ }
+
+ public void requestIndicatorRedraw() {
+ if (!IS_SHOWING_INDICATOR) {
+ return;
+ }
+ if (mMainKeyboardView == null) {
+ return;
+ }
+ mMainKeyboardView.invalidateAllKeys();
+ }
+
+
+ public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width,
+ int height) {
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger
+ // and remove this method.
+ // The check for MainKeyboardView ensures that a red border is only placed around
+ // the main keyboard, not every keyboard.
+ if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
+ final int savedColor = paint.getColor();
+ paint.setColor(Color.RED);
+ final Style savedStyle = paint.getStyle();
+ paint.setStyle(Style.STROKE);
+ final float savedStrokeWidth = paint.getStrokeWidth();
+ if (IS_SHOWING_INDICATOR_CLEARLY) {
+ paint.setStrokeWidth(5);
+ canvas.drawRect(0, 0, width, height, paint);
+ } else {
+ // Put a tiny red dot on the screen so a knowledgeable user can check whether
+ // it is enabled. The dot is actually a zero-width, zero-height rectangle,
+ // placed at the lower-right corner of the canvas, painted with a non-zero border
+ // width.
+ paint.setStrokeWidth(3);
+ canvas.drawRect(width, height, width, height, paint);
+ }
+ paint.setColor(savedColor);
+ paint.setStyle(savedStyle);
+ paint.setStrokeWidth(savedStrokeWidth);
+ }
+ }
+
+ private static final Object[] EVENTKEYS_NULLVALUES = {};
+
+ /**
+ * Buffer a research log event, flagging it as privacy-sensitive.
+ *
+ * This event contains potentially private information. If the word that this event is a part
+ * of is determined to be privacy-sensitive, then this event should not be included in the
+ * output log. The system waits to output until the containing word is known.
+ *
+ * @param keys an array containing a descriptive name for the event, followed by the keys
+ * @param values an array of values, either a String or Number. length should be one
+ * less than the keys array
+ */
+ private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys,
+ final Object[] values) {
+ assert values.length + 1 == keys.length;
+ if (isAllowedToLog()) {
+ mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */);
+ }
+ }
+
+ private void setCurrentLogUnitContainsDigitFlag() {
+ mCurrentLogUnit.setContainsDigit();
+ }
+
+ /**
+ * Buffer a research log event, flaggint it as not privacy-sensitive.
+ *
+ * This event contains no potentially private information. Even if the word that this event
+ * is privacy-sensitive, this event can still safely be sent to the output log. The system
+ * waits until the containing word is known so that this event can be written in the proper
+ * temporal order with other events that may be privacy sensitive.
+ *
+ * @param keys an array containing a descriptive name for the event, followed by the keys
+ * @param values an array of values, either a String or Number. length should be one
+ * less than the keys array
+ */
+ private synchronized void enqueueEvent(final String[] keys, final Object[] values) {
+ assert values.length + 1 == keys.length;
+ if (isAllowedToLog()) {
+ mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */);
+ }
+ }
+
+ /* package for test */ void commitCurrentLogUnit() {
+ if (!mCurrentLogUnit.isEmpty()) {
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.shiftIn(mCurrentLogUnit);
+ if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) {
+ publishLogBuffer(mMainLogBuffer, mMainResearchLog,
+ true /* isIncludingPrivateData */);
+ mMainLogBuffer.resetWordCounter();
+ }
+ }
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLogBuffer.shiftIn(mCurrentLogUnit);
+ }
+ mCurrentLogUnit = new LogUnit();
+ Log.d(TAG, "commitCurrentLogUnit");
+ }
+ }
+
+ /* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
+ final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ LogUnit logUnit;
+ while ((logUnit = logBuffer.shiftOut()) != null) {
+ researchLog.publish(logUnit, isIncludingPrivateData);
+ }
+ }
+
+ private boolean hasOnlyLetters(final String word) {
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (!Character.isLetter(codePoint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void onWordComplete(final String word) {
+ Log.d(TAG, "onWordComplete: " + word);
+ if (word != null && word.length() > 0 && hasOnlyLetters(word)) {
+ mCurrentLogUnit.setWord(word);
+ mStatistics.recordWordEntered();
+ }
+ commitCurrentLogUnit();
+ }
+
+ private static int scrubDigitFromCodePoint(int codePoint) {
+ return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint;
+ }
+
+ /* package for test */ static String scrubDigitsFromString(String s) {
+ StringBuilder sb = null;
+ final int length = s.length();
+ for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) {
+ final int codePoint = Character.codePointAt(s, i);
+ if (Character.isDigit(codePoint)) {
+ if (sb == null) {
+ sb = new StringBuilder(length);
+ sb.append(s.substring(0, i));
+ }
+ sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT);
+ } else {
+ if (sb != null) {
+ sb.appendCodePoint(codePoint);
+ }
+ }
+ }
+ if (sb == null) {
+ return s;
+ } else {
+ return sb.toString();
+ }
+ }
+
+ private static String getUUID(final SharedPreferences prefs) {
+ String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
+ if (null == uuidString) {
+ UUID uuid = UUID.randomUUID();
+ uuidString = uuid.toString();
+ Editor editor = prefs.edit();
+ editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
+ editor.apply();
+ }
+ return uuidString;
+ }
+
+ private String scrubWord(String word) {
+ if (mDictionary == null) {
+ return WORD_REPLACEMENT_STRING;
+ }
+ if (mDictionary.isValidWord(word)) {
+ return word;
+ }
+ return WORD_REPLACEMENT_STRING;
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
+ "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
+ "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
+ };
+ public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
+ final SharedPreferences prefs) {
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.start();
+ if (editorInfo != null) {
+ final Context context = researchLogger.mInputMethodService;
+ try {
+ final PackageInfo packageInfo;
+ packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
+ 0);
+ final Integer versionCode = packageInfo.versionCode;
+ final String versionName = packageInfo.versionName;
+ final Object[] values = {
+ researchLogger.mUUIDString, editorInfo.packageName,
+ Integer.toHexString(editorInfo.inputType),
+ Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
+ Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
+ OUTPUT_FORMAT_VERSION
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void latinIME_onFinishInputInternal() {
+ stop();
+ }
+
+ private static final String[] EVENTKEYS_USER_FEEDBACK = {
+ "UserFeedback", "FeedbackContents"
+ };
+
+ private static final String[] EVENTKEYS_PREFS_CHANGED = {
+ "PrefsChanged", "prefs"
+ };
+ public static void prefsChanged(final SharedPreferences prefs) {
+ final ResearchLogger researchLogger = getInstance();
+ final Object[] values = {
+ prefs
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values);
+ }
+
+ // Regular logging methods
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
+ "MainKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
+ "pressure"
+ };
+ public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
+ final long eventTime, final int index, final int id, final int x, final int y) {
+ if (me != null) {
+ final String actionString;
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL: actionString = "CANCEL"; break;
+ case MotionEvent.ACTION_UP: actionString = "UP"; break;
+ case MotionEvent.ACTION_DOWN: actionString = "DOWN"; break;
+ case MotionEvent.ACTION_POINTER_UP: actionString = "POINTER_UP"; break;
+ case MotionEvent.ACTION_POINTER_DOWN: actionString = "POINTER_DOWN"; break;
+ case MotionEvent.ACTION_MOVE: actionString = "MOVE"; break;
+ case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break;
+ default: actionString = "ACTION_" + action; break;
+ }
+ final float size = me.getSize(index);
+ final float pressure = me.getPressure(index);
+ final Object[] values = {
+ actionString, eventTime, id, x, y, size, pressure
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = {
+ "LatinIMEOnCodeInput", "code", "x", "y"
+ };
+ public static void latinIME_onCodeInput(final int code, final int x, final int y) {
+ final long time = SystemClock.uptimeMillis();
+ final ResearchLogger researchLogger = getInstance();
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y
+ };
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
+ if (Character.isDigit(code)) {
+ researchLogger.setCurrentLogUnitContainsDigitFlag();
+ }
+ researchLogger.mStatistics.recordChar(code, time);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = {
+ "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions"
+ };
+ public static void latinIME_onDisplayCompletions(
+ final CompletionInfo[] applicationSpecifiedCompletions) {
+ final Object[] values = {
+ applicationSpecifiedCompletions
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS,
+ values);
+ }
+
+ public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
+ boolean returnValue = sLatinIMEExpectingUpdateSelection;
+ sLatinIMEExpectingUpdateSelection = false;
+ return returnValue;
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = {
+ "LatinIMEOnWindowHidden", "isTextTruncated", "text"
+ };
+ public static void latinIME_onWindowHidden(final int savedSelectionStart,
+ final int savedSelectionEnd, final InputConnection ic) {
+ if (ic != null) {
+ // Capture the TextView contents. This will trigger onUpdateSelection(), so we
+ // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
+ // it can tell that it was generated by the logging code, and not by the user, and
+ // therefore keep user-visible state as is.
+ ic.beginBatchEdit();
+ ic.performContextMenuAction(android.R.id.selectAll);
+ CharSequence charSequence = ic.getSelectedText(0);
+ ic.setSelection(savedSelectionStart, savedSelectionEnd);
+ ic.endBatchEdit();
+ sLatinIMEExpectingUpdateSelection = true;
+ final Object[] values = new Object[2];
+ if (OUTPUT_ENTIRE_BUFFER) {
+ if (TextUtils.isEmpty(charSequence)) {
+ values[0] = false;
+ values[1] = "";
+ } else {
+ if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) {
+ int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE;
+ // do not cut in the middle of a supplementary character
+ final char c = charSequence.charAt(length - 1);
+ if (Character.isHighSurrogate(c)) {
+ length--;
+ }
+ final CharSequence truncatedCharSequence = charSequence.subSequence(0,
+ length);
+ values[0] = true;
+ values[1] = truncatedCharSequence.toString();
+ } else {
+ values[0] = false;
+ values[1] = charSequence.toString();
+ }
+ }
+ } else {
+ values[0] = true;
+ values[1] = "";
+ }
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
+ researchLogger.commitCurrentLogUnit();
+ getInstance().stop();
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
+ "LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
+ "oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
+ "expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context"
+ };
+ public static void latinIME_onUpdateSelection(final int lastSelectionStart,
+ final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
+ final int newSelStart, final int newSelEnd, final int composingSpanStart,
+ final int composingSpanEnd, final boolean expectingUpdateSelection,
+ final boolean expectingUpdateSelectionFromLogger,
+ final RichInputConnection connection) {
+ String word = "";
+ if (connection != null) {
+ Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1);
+ if (range != null) {
+ word = range.mWord;
+ }
+ }
+ final ResearchLogger researchLogger = getInstance();
+ final String scrubbedWord = researchLogger.scrubWord(word);
+ final Object[] values = {
+ lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart,
+ newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, scrubbedWord
+ };
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = {
+ "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y"
+ };
+ public static void latinIME_pickSuggestionManually(final String replacedWord,
+ final int index, CharSequence suggestion) {
+ final Object[] values = {
+ scrubDigitsFromString(replacedWord), index,
+ (suggestion == null ? null : scrubDigitsFromString(suggestion.toString())),
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = {
+ "LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y"
+ };
+ public static void latinIME_punctuationSuggestion(final int index,
+ final CharSequence suggestion) {
+ final Object[] values = {
+ index, suggestion,
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
+ };
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = {
+ "LatinIMESendKeyCodePoint", "code"
+ };
+ public static void latinIME_sendKeyCodePoint(final int code) {
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code))
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values);
+ if (Character.isDigit(code)) {
+ researchLogger.setCurrentLogUnitContainsDigitFlag();
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = {
+ "LatinIMESwapSwapperAndSpace"
+ };
+ public static void latinIME_swapSwapperAndSpace() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = {
+ "MainKeyboardViewOnLongPress"
+ };
+ public static void mainKeyboardView_onLongPress() {
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD = {
+ "MainKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width",
+ "modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey",
+ "passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled",
+ "isMultiLine", "tw", "th", "keys"
+ };
+ public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) {
+ if (keyboard != null) {
+ final KeyboardId kid = keyboard.mId;
+ final boolean isPasswordView = kid.passwordInput();
+ getInstance().setIsPasswordView(isPasswordView);
+ final Object[] values = {
+ KeyboardId.elementIdToName(kid.mElementId),
+ kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
+ kid.mOrientation,
+ kid.mWidth,
+ KeyboardId.modeName(kid.mMode),
+ kid.imeAction(),
+ kid.navigateNext(),
+ kid.navigatePrevious(),
+ kid.mClobberSettingsKey,
+ isPasswordView,
+ kid.mShortcutKeyEnabled,
+ kid.mHasShortcutKey,
+ kid.mLanguageSwitchKeyEnabled,
+ kid.isMultiLine(),
+ keyboard.mOccupiedWidth,
+ keyboard.mOccupiedHeight,
+ keyboard.mKeys
+ };
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD, values);
+ getInstance().setIsPasswordView(isPasswordView);
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = {
+ "LatinIMERevertCommit", "originallyTypedWord"
+ };
+ public static void latinIME_revertCommit(final String originallyTypedWord) {
+ final Object[] values = {
+ originallyTypedWord
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = {
+ "PointerTrackerCallListenerOnCancelInput"
+ };
+ public static void pointerTracker_callListenerOnCancelInput() {
+ getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT,
+ EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = {
+ "PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y",
+ "ignoreModifierKey", "altersCode", "isEnabled"
+ };
+ public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
+ final int y, final boolean ignoreModifierKey, final boolean altersCode,
+ final int code) {
+ if (key != null) {
+ CharSequence outputText = key.mOutputText;
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null
+ : scrubDigitsFromString(outputText.toString()),
+ x, y, ignoreModifierKey, altersCode, key.isEnabled()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = {
+ "PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey",
+ "isEnabled"
+ };
+ public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
+ final boolean withSliding, final boolean ignoreModifierKey) {
+ if (key != null) {
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
+ ignoreModifierKey, key.isEnabled()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = {
+ "PointerTrackerOnDownEvent", "deltaT", "distanceSquared"
+ };
+ public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
+ final Object[] values = {
+ deltaT, distanceSquared
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = {
+ "PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY"
+ };
+ public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
+ final int lastY) {
+ final Object[] values = {
+ x, y, lastX, lastY
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = {
+ "RichInputConnectionCommitCompletion", "completionInfo"
+ };
+ public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) {
+ final Object[] values = {
+ completionInfo
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values);
+ }
+
+ // Disabled for privacy-protection reasons. Because this event comes after
+ // richInputConnection_commitText, which is the event used to separate LogUnits, the
+ // data in this event can be associated with the next LogUnit, revealing information
+ // about the current word even if it was supposed to be suppressed. The occurrance of
+ // autocorrection can be determined by examining the difference between the text strings in
+ // the last call to richInputConnection_setComposingText before
+ // richInputConnection_commitText, so it's not a data loss.
+ // TODO: Figure out how to log this event without loss of privacy.
+ /*
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = {
+ "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection"
+ };
+ */
+ public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) {
+ /*
+ final String typedWord = correctionInfo.getOldText().toString();
+ final String autoCorrection = correctionInfo.getNewText().toString();
+ final Object[] values = {
+ scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection)
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values);
+ */
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = {
+ "RichInputConnectionCommitText", "typedWord", "newCursorPosition"
+ };
+ public static void richInputConnection_commitText(final CharSequence typedWord,
+ final int newCursorPosition) {
+ final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
+ final Object[] values = {
+ scrubbedWord, newCursorPosition
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT,
+ values);
+ researchLogger.onWordComplete(scrubbedWord);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = {
+ "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength"
+ };
+ public static void richInputConnection_deleteSurroundingText(final int beforeLength,
+ final int afterLength) {
+ final Object[] values = {
+ beforeLength, afterLength
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = {
+ "RichInputConnectionFinishComposingText"
+ };
+ public static void richInputConnection_finishComposingText() {
+ getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT,
+ EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = {
+ "RichInputConnectionPerformEditorAction", "imeActionNext"
+ };
+ public static void richInputConnection_performEditorAction(final int imeActionNext) {
+ final Object[] values = {
+ imeActionNext
+ };
+ getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = {
+ "RichInputConnectionSendKeyEvent", "eventTime", "action", "code"
+ };
+ public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) {
+ final Object[] values = {
+ keyEvent.getEventTime(),
+ keyEvent.getAction(),
+ keyEvent.getKeyCode()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = {
+ "RichInputConnectionSetComposingText", "text", "newCursorPosition"
+ };
+ public static void richInputConnection_setComposingText(final CharSequence text,
+ final int newCursorPosition) {
+ if (text == null) {
+ throw new RuntimeException("setComposingText is null");
+ }
+ final Object[] values = {
+ text, newCursorPosition
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = {
+ "RichInputConnectionSetSelection", "from", "to"
+ };
+ public static void richInputConnection_setSelection(final int from, final int to) {
+ final Object[] values = {
+ from, to
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = {
+ "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent"
+ };
+ public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
+ if (me != null) {
+ final Object[] values = {
+ me.toString()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS = {
+ "SuggestionStripViewSetSuggestions", "suggestedWords"
+ };
+ public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
+ if (suggestedWords != null) {
+ final Object[] values = {
+ suggestedWords
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_USER_TIMESTAMP = {
+ "UserTimestamp"
+ };
+ public void userTimestamp() {
+ getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_STATISTICS = {
+ "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount",
+ "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys",
+ "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete"
+ };
+ private static void logStatistics() {
+ final ResearchLogger researchLogger = getInstance();
+ final Statistics statistics = researchLogger.mStatistics;
+ final Object[] values = {
+ statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount,
+ statistics.mSpaceCount, statistics.mDeleteKeyCount,
+ statistics.mWordCount, statistics.mIsEmptyUponStarting,
+ statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(),
+ statistics.mBeforeDeleteKeyCounter.getAverageTime(),
+ statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
+ statistics.mAfterDeleteKeyCounter.getAverageTime()
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values);
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java
new file mode 100644
index 000000000..eab465aa2
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/Statistics.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import com.android.inputmethod.keyboard.Keyboard;
+
+public class Statistics {
+ // Number of characters entered during a typing session
+ int mCharCount;
+ // Number of letter characters entered during a typing session
+ int mLetterCount;
+ // Number of number characters entered
+ int mNumberCount;
+ // Number of space characters entered
+ int mSpaceCount;
+ // Number of delete operations entered (taps on the backspace key)
+ int mDeleteKeyCount;
+ // Number of words entered during a session.
+ int mWordCount;
+ // Whether the text field was empty upon editing
+ boolean mIsEmptyUponStarting;
+ boolean mIsEmptinessStateKnown;
+
+ // Timers to count average time to enter a key, first press a delete key,
+ // between delete keys, and then to return typing after a delete key.
+ final AverageTimeCounter mKeyCounter = new AverageTimeCounter();
+ final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter();
+ final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter();
+ final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter();
+
+ static class AverageTimeCounter {
+ int mCount;
+ int mTotalTime;
+
+ public void reset() {
+ mCount = 0;
+ mTotalTime = 0;
+ }
+
+ public void add(long deltaTime) {
+ mCount++;
+ mTotalTime += deltaTime;
+ }
+
+ public int getAverageTime() {
+ if (mCount == 0) {
+ return 0;
+ }
+ return mTotalTime / mCount;
+ }
+ }
+
+ // To account for the interruptions when the user's attention is directed elsewhere, times
+ // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic.
+ public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds
+ public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds
+
+ // The last time that a tap was performed
+ private long mLastTapTime;
+ // The type of the last keypress (delete key or not)
+ boolean mIsLastKeyDeleteKey;
+
+ private static final Statistics sInstance = new Statistics();
+
+ public static Statistics getInstance() {
+ return sInstance;
+ }
+
+ private Statistics() {
+ reset();
+ }
+
+ public void reset() {
+ mCharCount = 0;
+ mLetterCount = 0;
+ mNumberCount = 0;
+ mSpaceCount = 0;
+ mDeleteKeyCount = 0;
+ mWordCount = 0;
+ mIsEmptyUponStarting = true;
+ mIsEmptinessStateKnown = false;
+ mKeyCounter.reset();
+ mBeforeDeleteKeyCounter.reset();
+ mDuringRepeatedDeleteKeysCounter.reset();
+ mAfterDeleteKeyCounter.reset();
+
+ mLastTapTime = 0;
+ mIsLastKeyDeleteKey = false;
+ }
+
+ public void recordChar(int codePoint, long time) {
+ final long delta = time - mLastTapTime;
+ if (codePoint == Keyboard.CODE_DELETE) {
+ mDeleteKeyCount++;
+ if (delta < MIN_DELETION_INTERMISSION) {
+ if (mIsLastKeyDeleteKey) {
+ mDuringRepeatedDeleteKeysCounter.add(delta);
+ } else {
+ mBeforeDeleteKeyCounter.add(delta);
+ }
+ }
+ mIsLastKeyDeleteKey = true;
+ } else {
+ mCharCount++;
+ if (Character.isDigit(codePoint)) {
+ mNumberCount++;
+ }
+ if (Character.isLetter(codePoint)) {
+ mLetterCount++;
+ }
+ if (Character.isSpaceChar(codePoint)) {
+ mSpaceCount++;
+ }
+ if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) {
+ mAfterDeleteKeyCounter.add(delta);
+ } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) {
+ mKeyCounter.add(delta);
+ }
+ mIsLastKeyDeleteKey = false;
+ }
+ mLastTapTime = time;
+ }
+
+ public void recordWordEntered() {
+ mWordCount++;
+ }
+
+ public void setIsEmptyUponStarting(final boolean isEmpty) {
+ mIsEmptyUponStarting = isEmpty;
+ mIsEmptinessStateKnown = true;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
new file mode 100644
index 000000000..7a5749096
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.research;
+
+import android.Manifest;
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.inputmethod.latin.R;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public final class UploaderService extends IntentService {
+ private static final String TAG = UploaderService.class.getSimpleName();
+ public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR;
+ private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName()
+ + ".extra.UPLOAD_UNCONDITIONALLY";
+ private static final int BUF_SIZE = 1024 * 8;
+ protected static final int TIMEOUT_IN_MS = 1000 * 4;
+
+ private boolean mCanUpload;
+ private File mFilesDir;
+ private URL mUrl;
+
+ public UploaderService() {
+ super("Research Uploader Service");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mCanUpload = false;
+ mFilesDir = null;
+ mUrl = null;
+
+ final PackageManager packageManager = getPackageManager();
+ final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
+ getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ if (!hasPermission) {
+ return;
+ }
+
+ try {
+ final String urlString = getString(R.string.research_logger_upload_url);
+ if (urlString == null || urlString.equals("")) {
+ return;
+ }
+ mFilesDir = getFilesDir();
+ mUrl = new URL(urlString);
+ mCanUpload = true;
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (!mCanUpload) {
+ return;
+ }
+ boolean isUploadingUnconditionally = false;
+ Bundle bundle = intent.getExtras();
+ if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) {
+ isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY);
+ }
+ doUpload(isUploadingUnconditionally);
+ }
+
+ private boolean isExternallyPowered() {
+ final Intent intent = registerReceiver(null, new IntentFilter(
+ Intent.ACTION_BATTERY_CHANGED));
+ final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ return pluggedState == BatteryManager.BATTERY_PLUGGED_AC
+ || pluggedState == BatteryManager.BATTERY_PLUGGED_USB;
+ }
+
+ private boolean hasWifiConnection() {
+ final ConnectivityManager manager =
+ (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return wifiInfo.isConnected();
+ }
+
+ private void doUpload(final boolean isUploadingUnconditionally) {
+ if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) {
+ return;
+ }
+ if (mFilesDir == null) {
+ return;
+ }
+ final File[] files = mFilesDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX)
+ && !pathname.canWrite();
+ }
+ });
+ boolean success = true;
+ if (files.length == 0) {
+ success = false;
+ }
+ for (final File file : files) {
+ if (!uploadFile(file)) {
+ success = false;
+ }
+ }
+ }
+
+ private boolean uploadFile(File file) {
+ Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
+ boolean success = false;
+ final int contentLength = (int) file.length();
+ HttpURLConnection connection = null;
+ InputStream fileInputStream = null;
+ try {
+ fileInputStream = new FileInputStream(file);
+ connection = (HttpURLConnection) mUrl.openConnection();
+ connection.setRequestMethod("PUT");
+ connection.setDoOutput(true);
+ connection.setFixedLengthStreamingMode(contentLength);
+ final OutputStream os = connection.getOutputStream();
+ final byte[] buf = new byte[BUF_SIZE];
+ int numBytesRead;
+ while ((numBytesRead = fileInputStream.read(buf)) != -1) {
+ os.write(buf, 0, numBytesRead);
+ }
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ Log.d(TAG, "upload failed: " + connection.getResponseCode());
+ InputStream netInputStream = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Log.d(TAG, "| " + reader.readLine());
+ }
+ reader.close();
+ return success;
+ }
+ file.delete();
+ success = true;
+ Log.d(TAG, "upload successful");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (fileInputStream != null) {
+ try {
+ fileInputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return success;
+ }
+}