aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml16
-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.xml15
-rw-r--r--java/res/values-am/strings-appname.xml27
-rw-r--r--java/res/values-am/strings.xml15
-rw-r--r--java/res/values-ar/strings-appname.xml27
-rw-r--r--java/res/values-ar/strings.xml15
-rw-r--r--java/res/values-be/strings-appname.xml27
-rw-r--r--java/res/values-be/strings.xml15
-rw-r--r--java/res/values-bg/strings-appname.xml27
-rw-r--r--java/res/values-bg/strings.xml15
-rw-r--r--java/res/values-ca/strings-appname.xml27
-rw-r--r--java/res/values-ca/strings.xml15
-rw-r--r--java/res/values-cs/strings-appname.xml27
-rw-r--r--java/res/values-cs/strings.xml15
-rw-r--r--java/res/values-da/strings-appname.xml27
-rw-r--r--java/res/values-da/strings.xml15
-rw-r--r--java/res/values-de/strings-appname.xml27
-rw-r--r--java/res/values-de/strings.xml15
-rw-r--r--java/res/values-el/strings-appname.xml27
-rw-r--r--java/res/values-el/strings.xml15
-rw-r--r--java/res/values-en-rGB/strings-appname.xml27
-rw-r--r--java/res/values-en-rGB/strings.xml15
-rw-r--r--java/res/values-es-rUS/strings-appname.xml27
-rw-r--r--java/res/values-es-rUS/strings.xml15
-rw-r--r--java/res/values-es/strings-appname.xml27
-rw-r--r--java/res/values-es/strings.xml15
-rw-r--r--java/res/values-et/strings-appname.xml27
-rw-r--r--java/res/values-et/strings.xml15
-rw-r--r--java/res/values-fa/strings-appname.xml27
-rw-r--r--java/res/values-fa/strings.xml59
-rw-r--r--java/res/values-fi/strings-appname.xml27
-rw-r--r--java/res/values-fi/strings.xml15
-rw-r--r--java/res/values-fr/strings-appname.xml27
-rw-r--r--java/res/values-fr/strings.xml15
-rw-r--r--java/res/values-hi/strings-appname.xml27
-rw-r--r--java/res/values-hi/strings.xml15
-rw-r--r--java/res/values-hr/strings-appname.xml27
-rw-r--r--java/res/values-hr/strings.xml15
-rw-r--r--java/res/values-hu/strings-appname.xml27
-rw-r--r--java/res/values-hu/strings.xml15
-rw-r--r--java/res/values-in/strings-appname.xml27
-rw-r--r--java/res/values-in/strings.xml15
-rw-r--r--java/res/values-it/strings-appname.xml27
-rw-r--r--java/res/values-it/strings.xml15
-rw-r--r--java/res/values-iw/strings-appname.xml27
-rw-r--r--java/res/values-iw/strings.xml15
-rw-r--r--java/res/values-ja/strings-appname.xml27
-rw-r--r--java/res/values-ja/strings.xml15
-rw-r--r--java/res/values-ko/strings-appname.xml27
-rw-r--r--java/res/values-ko/strings.xml15
-rw-r--r--java/res/values-lt/strings-appname.xml27
-rw-r--r--java/res/values-lt/strings.xml15
-rw-r--r--java/res/values-lv/strings-appname.xml27
-rw-r--r--java/res/values-lv/strings.xml15
-rw-r--r--java/res/values-ms/strings-appname.xml27
-rw-r--r--java/res/values-ms/strings.xml15
-rw-r--r--java/res/values-nb/strings-appname.xml27
-rw-r--r--java/res/values-nb/strings.xml15
-rw-r--r--java/res/values-nl/strings-appname.xml27
-rw-r--r--java/res/values-nl/strings.xml15
-rw-r--r--java/res/values-pl/strings-appname.xml30
-rw-r--r--java/res/values-pl/strings.xml15
-rw-r--r--java/res/values-pt-rPT/strings-appname.xml27
-rw-r--r--java/res/values-pt-rPT/strings.xml15
-rw-r--r--java/res/values-pt/strings-appname.xml27
-rw-r--r--java/res/values-pt/strings.xml15
-rw-r--r--java/res/values-rm/strings.xml18
-rw-r--r--java/res/values-ro/strings-appname.xml27
-rw-r--r--java/res/values-ro/strings.xml15
-rw-r--r--java/res/values-ru/strings-appname.xml27
-rw-r--r--java/res/values-ru/strings.xml15
-rw-r--r--java/res/values-sk/strings-appname.xml27
-rw-r--r--java/res/values-sk/strings.xml15
-rw-r--r--java/res/values-sl/strings-appname.xml27
-rw-r--r--java/res/values-sl/strings.xml15
-rw-r--r--java/res/values-sr/strings-appname.xml27
-rw-r--r--java/res/values-sr/strings.xml15
-rw-r--r--java/res/values-sv/strings-appname.xml27
-rw-r--r--java/res/values-sv/strings.xml15
-rw-r--r--java/res/values-sw/strings-appname.xml27
-rw-r--r--java/res/values-sw/strings.xml15
-rw-r--r--java/res/values-sw600dp/config.xml3
-rw-r--r--java/res/values-sw768dp/config.xml2
-rw-r--r--java/res/values-th/strings-appname.xml27
-rw-r--r--java/res/values-th/strings.xml15
-rw-r--r--java/res/values-tl/strings-appname.xml27
-rw-r--r--java/res/values-tl/strings.xml15
-rw-r--r--java/res/values-tr/strings-appname.xml27
-rw-r--r--java/res/values-tr/strings.xml15
-rw-r--r--java/res/values-uk/strings-appname.xml27
-rw-r--r--java/res/values-uk/strings.xml15
-rw-r--r--java/res/values-vi/strings-appname.xml27
-rw-r--r--java/res/values-vi/strings.xml15
-rw-r--r--java/res/values-zh-rCN/strings-appname.xml27
-rw-r--r--java/res/values-zh-rCN/strings.xml15
-rw-r--r--java/res/values-zh-rTW/strings-appname.xml27
-rw-r--r--java/res/values-zh-rTW/strings.xml15
-rw-r--r--java/res/values-zu/strings-appname.xml27
-rw-r--r--java/res/values-zu/strings.xml15
-rw-r--r--java/res/values/attrs.xml15
-rw-r--r--java/res/values/config.xml9
-rw-r--r--java/res/values/dimens.xml8
-rw-r--r--java/res/values/gesture-input.xml22
-rw-r--r--java/res/values/strings-appname.xml33
-rw-r--r--java/res/values/strings.xml104
-rw-r--r--java/res/values/styles.xml18
-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.xml22
-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_styles_common.xml11
-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.xml30
-rw-r--r--java/res/xml/rowkeys_symbols3.xml2
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java6
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java16
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java134
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java35
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java7
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java31
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java85
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java (renamed from java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java)161
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java16
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java318
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java28
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java198
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java14
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java27
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java8
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java17
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java427
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java19
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java172
-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/AutoCorrection.java91
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java141
-rw-r--r--java/src/com/android/inputmethod/latin/BoundedTreeSet.java49
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java7
-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/Dictionary.java80
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java43
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java11
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java182
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java47
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java127
-rw-r--r--java/src/com/android/inputmethod/latin/ImfUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java5
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java127
-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.java1327
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java2
-rw-r--r--java/src/com/android/inputmethod/latin/NativeUtils.java32
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java757
-rw-r--r--java/src/com/android/inputmethod/latin/ResizableIntArray.java134
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java421
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java76
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java97
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java3
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java6
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java560
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java63
-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.java22
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java10
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java16
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java13
-rw-r--r--java/src/com/android/inputmethod/latin/WhitelistDictionary.java12
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java121
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java450
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java152
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java25
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java302
-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)90
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackActivity.java52
-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/ResearchLog.java375
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogUploader.java223
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java1280
209 files changed, 8311 insertions, 4622 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 06d852bb0..6b823829f 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -1,3 +1,19 @@
+<?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">
diff --git a/java/proguard.flags b/java/proguard.flags
index 34e23aa9a..24b4c1987 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,9 @@
<init>(...);
}
--keep class com.android.inputmethod.latin.ResearchLogger {
- void setLogFileManager(...);
- void clearAll();
- com.android.inputmethod.latin.ResearchLogger$LogFileManager getLogFileManager();
-}
-
--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..0a5d048ef 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,10 @@
<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="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>
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..ab3b71c1c 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,10 @@
<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="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>
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..8407f923e 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,10 @@
<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="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>
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..9301750e3 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,10 @@
<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="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>
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..6061e3c52 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,10 @@
<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="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>
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..865a391a0 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,10 @@
<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="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>
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..1d9c8e38c 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,10 @@
<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="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>
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..ca0938740 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,10 @@
<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="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>
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..437fc80f4 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,10 @@
<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="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>
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..4ecde7b7c 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,10 @@
<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="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>
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..e505f46d2 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,10 @@
<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="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>
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..4187b87b0 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,10 @@
<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="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>
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..9de4fcf57 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,10 @@
<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="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>
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..cedbde055 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,10 @@
<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="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>
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..f1bd870e2 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -20,51 +20,46 @@
<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="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>
@@ -107,26 +102,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..898d522ba 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,10 @@
<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="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>
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..63c36af4c 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,10 @@
<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="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>
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..22c9efb1f 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,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,10 @@
<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="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>
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..e7c508d90 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,10 @@
<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="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>
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..637b5e482 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,10 @@
<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="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>
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..d5c6d48a4 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,10 @@
<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="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>
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..efa02b23a 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,10 @@
<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="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>
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..b182ffd32 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,10 @@
<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="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>
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..9d31d5cec 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,10 @@
<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="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>
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..8b4a498d0 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,10 @@
<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="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>
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..e52452e30 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,10 @@
<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="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>
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..274dd2f27 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,10 @@
<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="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>
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..5100bb901 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,10 @@
<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="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>
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..be609226a 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,10 @@
<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="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>
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..dd2608668 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,10 @@
<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="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>
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..bc2e3ca1a 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,10 @@
<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="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>
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..48a010a6a 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,10 @@
<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="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>
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..fc5ccaa00 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,10 @@
<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="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>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 18741f816..dceeaf4a2 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,13 @@
<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_suggestion_summary (6635527607242625713) -->
+ <!-- no translation found for bigram_prediction_summary (3253961591626441019) -->
<skip />
- <!-- no translation found for bigram_prediction (3216364899483135294) -->
+ <!-- no translation found for gesture_input (3310827802759290774) -->
<skip />
- <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+ <!-- no translation found for gesture_input_summary (7019742443455085809) -->
<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>
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..d0473464c 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,10 @@
<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="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>
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..7e12ddb22 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,10 @@
<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="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>
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..619f41fc5 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,10 @@
<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="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>
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..1df021d1f 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,10 @@
<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 izsleditvijo črk"</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>
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..7febdf0dc 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,10 @@
<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="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>
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..301b35813 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,10 @@
<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="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>
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..eed8e994f 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,10 @@
<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="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>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 2f35d9ae5..619169c52 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -32,8 +32,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..27cb9ac21 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -30,7 +30,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..cad003f55 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,10 @@
<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="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>
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..8ebd9c742 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,10 @@
<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="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>
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..32d8d9882 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,10 @@
<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="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>
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..d0b3b46c5 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,10 @@
<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="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>
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..e4ffa913c 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,10 @@
<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="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>
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..3b70b6030 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,10 @@
<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="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>
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..848411fc8 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,10 @@
<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="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>
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..58fc37c56 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,10 @@
<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="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>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a18371fc9..1d52e46bb 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" />
@@ -123,7 +120,7 @@
</attr>
</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 +155,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..50f46c3f5 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -22,13 +22,10 @@
<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>
@@ -51,7 +48,7 @@
-->
<integer name="config_key_preview_linger_timeout">70</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..b47897f3c 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_preview_text_size">35dp</dimen>
+ <dimen name="gesture_preview_text_offset">75dp</dimen>
+ <dimen name="gesture_preview_text_shadow_border">17.5dp</dimen>
+ <dimen name="gesture_preview_text_shading_border">7.5dp</dimen>
+ <dimen name="gesture_preview_text_connector_width">1.0dp</dimen>
</resources>
diff --git a/java/res/values/gesture-input.xml b/java/res/values/gesture-input.xml
new file mode 100644
index 000000000..235616fbe
--- /dev/null
+++ b/java/res/values/gesture-input.xml
@@ -0,0 +1,22 @@
+<?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>
+ <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..25d982793 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,15 @@
<!-- 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=20]-->
+ <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>
<!-- 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 +181,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 +228,60 @@
<!-- 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 -->
+ <string name="research_feedback_include_history_label" translatable="false">Include last 5 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>
+
<!-- Preference for input language selection -->
<string name="select_language">Input languages</string>
@@ -260,6 +309,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 +347,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..8797fd7aa 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -67,7 +67,7 @@
<item name="shadowColor">#BB000000</item>
<item name="shadowRadius">2.75</item>
<item name="backgroundDimAlpha">128</item>
- <!-- Common attributes of LatinKeyboardView -->
+ <!-- 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 +84,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 +114,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 +155,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 +190,7 @@
<item name="shadowColor">#FFFFFFFF</item>
</style>
<style
- name="LatinKeyboardView.Stone"
+ name="MainKeyboardView.Stone"
parent="KeyboardView.Stone"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -230,7 +230,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 +259,7 @@
<item name="keyTextStyle">bold</item>
</style>
<style
- name="LatinKeyboardView.Gingerbread"
+ name="MainKeyboardView.Gingerbread"
parent="KeyboardView.Gingerbread"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -316,7 +316,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 +348,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/urls.xml b/java/res/values/urls.xml
new file mode 100644
index 000000000..a8e9ad7d3
--- /dev/null
+++ b/java/res/values/urls.xml
@@ -0,0 +1,22 @@
+<?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>
+ <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_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
index 40082ac35..7afe5848a 100644
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ b/java/res/xml-sw768dp/key_styles_common.xml
@@ -117,6 +117,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/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..4f11cb7e5 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"
@@ -114,24 +126,6 @@
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"/>
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/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index f6376d5f4..4ecbf827a 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;
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 23acb8b74..9b74070af 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -19,6 +19,7 @@ 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;
@@ -39,8 +40,8 @@ public class KeyCodeDescriptionMapper {
// Map of key labels to spoken description resource IDs
private final HashMap<CharSequence, Integer> mKeyLabelMap;
- // 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();
@@ -52,17 +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/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 13e909c7e..a3b7776b3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -25,7 +25,6 @@ public class KeyDetector {
private Keyboard mKeyboard;
private int mCorrectionX;
private int mCorrectionY;
- private boolean mProximityCorrectOn;
/**
* This class handles key detection.
@@ -53,6 +52,7 @@ public class KeyDetector {
return x + mCorrectionX;
}
+ // TODO: Remove vertical correction.
public int getTouchY(int y) {
return y + mCorrectionY;
}
@@ -63,14 +63,6 @@ public class KeyDetector {
return mKeyboard;
}
- public void setProximityCorrectionEnabled(boolean enabled) {
- mProximityCorrectOn = enabled;
- }
-
- public boolean isProximityCorrectionEnabled() {
- return mProximityCorrectOn;
- }
-
public boolean alwaysAllowsSlidingInput() {
return false;
}
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 21f175d7d..f1a35b212 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;
@@ -44,7 +46,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 +87,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 +102,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 +134,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 = new SparseArray<Key>();
private final ProximityInfo mProximityInfo;
private final boolean mProximityCharsCorrectionEnabled;
@@ -182,23 +184,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 +216,7 @@ public class Keyboard {
}
public static boolean isLetterCode(int code) {
- return code >= MINIMUM_LETTER_CODE;
+ return code >= CODE_SPACE;
}
public static class Params {
@@ -342,8 +346,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 +359,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 +426,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>
*/
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 275aacf36..b1621a55b 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.keyboard;
+import com.android.inputmethod.latin.InputPointers;
+
public interface KeyboardActionListener {
/**
@@ -52,6 +54,7 @@ public interface KeyboardActionListener {
*/
public void onCodeInput(int primaryCode, int x, int y);
+ // See {@link Adapter#isInvalidCoordinate(int)}.
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;
@@ -64,6 +67,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 +105,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 KeyboardActionListener#NOT_A_TOUCH_COORDINATE},
+ // {@link KeyboardActionListener#SUGGESTION_STRIP_COORDINATE}, and
+ // {@link KeyboardActionListener#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..b54c72687 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -137,11 +137,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() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 8c7246855..aab89a3e5 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,6 +29,7 @@ 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;
@@ -116,9 +117,9 @@ public class KeyboardLayoutSet {
InputMethodSubtype mSubtype;
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 =
+ new SparseArray<ElementParams>();
static class ElementParams {
int mKeyboardXmlId;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2e4ce199e..f27d79382 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -61,7 +61,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
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("Stone.Bold", 7, R.style.KeyboardTheme_Stone_Bold),
new KeyboardTheme("GingerBread", 8, R.style.KeyboardTheme_Gingerbread),
new KeyboardTheme("IceCreamSandwich", 5, R.style.KeyboardTheme_IceCreamSandwich),
};
@@ -71,7 +71,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;
@@ -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 = getKeyboardView();
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 = getKeyboardView();
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 = getKeyboardView();
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 = getKeyboardView();
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 = getKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelLongPressTimer();
@@ -343,7 +344,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
- public LatinKeyboardView getKeyboardView() {
+ public MainKeyboardView getKeyboardView() {
return mKeyboardView;
}
@@ -369,7 +370,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
- mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
mKeyboardView.setKeyboardActionListener(mLatinIME);
if (mForceNonDistinctMultitouch) {
mKeyboardView.setDistinctMultitouch(false);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 51a0f537f..9c6543b61 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -30,19 +30,22 @@ import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
+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.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;
/**
@@ -94,8 +97,6 @@ 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;
@@ -105,7 +106,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
protected final KeyPreviewDrawParams mKeyPreviewDrawParams;
private boolean mShowKeyPreviewPopup = true;
private int mDelayAfterPreview;
- private ViewGroup mPreviewPlacer;
+ private final PreviewPlacerView mPreviewPlacerView;
+
+ /** True if {@link KeyboardView} should handle gesture events. */
+ protected boolean mShouldHandleGesture;
// Drawing
/** True if the entire keyboard needs to be dimmed. */
@@ -124,12 +128,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private Canvas mCanvas;
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 = new SparseArray<Float>();
+ // This sparse array caches key label text width in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextWidthCache = new SparseArray<Float>();
private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
@@ -266,7 +268,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));
}
}
@@ -374,6 +376,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
mPaint.setAntiAlias(true);
+
+ mPreviewPlacerView = new PreviewPlacerView(context);
}
// Read fraction value in TypedArray as float.
@@ -428,6 +432,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return mShowKeyPreviewPopup;
}
+ public void setGestureHandlingMode(boolean shouldHandleGesture) {
+ mShouldHandleGesture = shouldHandleGesture;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mKeyboard != null) {
@@ -501,6 +509,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
}
+ // 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 +534,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 +781,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 +793,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;
@@ -863,17 +878,39 @@ 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;
}
- mPreviewPlacer.addView(
- keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
+ final int[] viewOrigin = new int[2];
+ getLocationInWindow(viewOrigin);
+ mPreviewPlacerView.setOrigin(viewOrigin[0], viewOrigin[1]);
+ final ViewGroup windowContentView =
+ (ViewGroup)getRootView().findViewById(android.R.id.content);
+ windowContentView.addView(mPreviewPlacerView);
+ }
+
+ public void showGesturePreviewText(String gesturePreviewText) {
+ // TDOD: Add user settings option to control drawing gesture trail.
+ locatePreviewPlacerView();
+ mPreviewPlacerView.setGesturePreviewText(gesturePreviewText);
+ mPreviewPlacerView.invalidate();
}
@Override
+ public void showGestureTrail(PointerTracker tracker) {
+ // TDOD: Add user settings option to control drawing gesture trail.
+ locatePreviewPlacerView();
+ mPreviewPlacerView.invalidatePointer(tracker);
+ }
+
+ @SuppressWarnings("deprecation") // setBackgroundDrawable is replaced by setBackground in API16
+ @Override
public void showKeyPreview(PointerTracker tracker) {
if (!mShowKeyPreviewPopup) return;
@@ -1012,9 +1049,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
closing();
- if (mPreviewPlacer != null) {
- mPreviewPlacer.removeAllViews();
- }
+ mPreviewPlacerView.removeAllViews();
if (mBuffer != null) {
mBuffer.recycle();
mBuffer = null;
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 383298de9..9c28419cf 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;
@@ -119,7 +120,7 @@ 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_REPEAT_KEY = 1;
private static final int MSG_LONGPRESS_KEY = 2;
@@ -127,21 +128,23 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private static final int MSG_TYPING_STATE_EXPIRED = 4;
private final KeyTimerParams mParams;
- private boolean mInKeyRepeat;
- public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
+ public KeyTimerHandler(MainKeyboardView outerInstance, KeyTimerParams params) {
super(outerInstance);
mParams = params;
}
@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_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, mParams.mKeyRepeatInterval);
+ }
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
@@ -158,22 +161,23 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
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);
}
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
@@ -246,7 +250,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
if (isTyping) {
return;
}
- final LatinKeyboardView keyboardView = getOuterInstance();
+ final MainKeyboardView keyboardView = getOuterInstance();
cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
}
@@ -296,13 +300,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
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);
+ public PointerTrackerParams(TypedArray mainKeyboardViewAttr) {
+ mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
+ R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
+ mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
+ mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimension(
+ R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
}
}
@@ -313,65 +317,67 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
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 KeyTimerParams(TypedArray mainKeyboardViewAttr) {
+ 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);
}
}
- public LatinKeyboardView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.latinKeyboardViewStyle);
+ public MainKeyboardView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.mainKeyboardViewStyle);
}
- 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);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
mPointerTrackerParams = new PointerTrackerParams(a);
final float keyHysteresisDistance = a.getDimension(
- R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
+ R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
mKeyDetector = new KeyDetector(keyHysteresisDistance);
mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
- R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
a.recycle();
PointerTracker.setParameters(mPointerTrackerParams);
@@ -451,22 +457,22 @@ 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);
- PointerTracker.setKeyDetector(mKeyDetector);
+ PointerTracker.setKeyDetector(mKeyDetector, mShouldHandleGesture);
mTouchScreenRegulator.setKeyboard(keyboard);
mMoreKeysPanelCache.clear();
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 +480,12 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
}
+ @Override
+ public void setGestureHandlingMode(final boolean shouldHandleGesture) {
+ super.setGestureHandlingMode(shouldHandleGesture);
+ PointerTracker.setKeyDetector(mKeyDetector, shouldHandleGesture);
+ }
+
/**
* Returns whether the device has distinct multi-touch panel.
* @return true if the device has distinct multi-touch panel.
@@ -486,21 +498,15 @@ 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);
- }
-
- /**
- * Returns true if proximity correction is enabled.
- */
- public boolean isProximityCorrectionEnabled() {
- return mKeyDetector.isProximityCorrectionEnabled();
+ @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();
+ }
}
@Override
@@ -552,7 +558,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()) {
@@ -703,7 +709,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 +761,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 +781,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);
}
}
@@ -861,7 +870,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..870eff29f 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -25,6 +25,7 @@ import android.widget.PopupWindow;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.R;
/**
@@ -58,6 +59,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..bd896517b 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -16,16 +16,21 @@
package com.android.inputmethod.keyboard;
+import android.graphics.Canvas;
+import android.graphics.Paint;
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.PointerTrackerQueue;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.ResearchLogger;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
@@ -36,6 +41,11 @@ public class PointerTracker {
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 final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
+
public interface KeyEventHandler {
/**
* Get KeyDetector object that is used for this PointerTracker.
@@ -68,6 +78,7 @@ public class PointerTracker {
public TextView inflateKeyPreviewText();
public void showKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public void showGestureTrail(PointerTracker tracker);
}
public interface TimerProxy {
@@ -107,12 +118,18 @@ public class PointerTracker {
}
// Parameters for pointer handling.
- private static LatinKeyboardView.PointerTrackerParams sParams;
+ private static MainKeyboardView.PointerTrackerParams sParams;
private static int sTouchNoiseThresholdDistanceSquared;
private static boolean sNeedsPhantomSuddenMoveEventHack;
private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
+ private static final InputPointers sAggregratedPointers = new InputPointers(
+ GestureStroke.DEFAULT_CAPACITY);
private static PointerTrackerQueue sPointerTrackerQueue;
+ // HACK: Change gesture detection criteria depending on this variable.
+ // TODO: Find more comprehensive ways to detect a gesture start.
+ // True when the previous user input was a gesture input, not a typing input.
+ private static boolean sWasInGesture;
public final int mPointerId;
@@ -125,6 +142,14 @@ public class PointerTracker {
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;
private long mUpTime;
@@ -148,9 +173,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 +186,8 @@ public class PointerTracker {
private static final KeyboardActionListener EMPTY_LISTENER =
new KeyboardActionListener.Adapter();
+ private final GestureStroke mGestureStroke;
+
public static void init(boolean hasDistinctMultitouch,
boolean needsPhantomSuddenMoveEventHack) {
if (hasDistinctMultitouch) {
@@ -173,15 +197,26 @@ public class PointerTracker {
}
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
- setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
+ setParameters(MainKeyboardView.PointerTrackerParams.DEFAULT);
+ updateGestureHandlingMode(null, false /* shouldHandleGesture */);
}
- public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
+ public static void setParameters(MainKeyboardView.PointerTrackerParams params) {
sParams = params;
sTouchNoiseThresholdDistanceSquared = (int)(
params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
}
+ private static void updateGestureHandlingMode(Keyboard keyboard, boolean shouldHandleGesture) {
+ if (!shouldHandleGesture
+ || AccessibilityUtils.getInstance().isTouchExplorationEnabled()
+ || (keyboard != null && keyboard.mId.passwordInput())) {
+ sShouldHandleGesture = false;
+ } else {
+ sShouldHandleGesture = true;
+ }
+ }
+
public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
final ArrayList<PointerTracker> trackers = sTrackers;
@@ -199,30 +234,72 @@ public class PointerTracker {
}
public static void setKeyboardActionListener(KeyboardActionListener listener) {
- for (final PointerTracker tracker : sTrackers) {
+ 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(KeyDetector keyDetector, boolean shouldHandleGesture) {
+ 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();
+ updateGestureHandlingMode(keyboard, shouldHandleGesture);
}
public static void dismissAllKeyPreviews() {
- for (final PointerTracker tracker : sTrackers) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
}
}
- public PointerTracker(int id, KeyEventHandler handler) {
+ // 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.mGestureStroke.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.mGestureStroke.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.mGestureStroke.reset();
+ }
+ sAggregratedPointers.reset();
+ }
+
+ private PointerTracker(int id, KeyEventHandler handler) {
if (handler == null)
throw new NullPointerException();
mPointerId = id;
+ mGestureStroke = new GestureStroke(id);
setKeyDetectorInner(handler.getKeyDetector());
mListener = handler.getKeyboardActionListener();
mDrawingProxy = handler.getDrawingProxy();
@@ -236,16 +313,15 @@ public class PointerTracker {
// Returns true if keyboard has been changed by this callback.
private boolean callListenerOnPressAndCheckKeyboardLayoutChange(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;
}
@@ -293,6 +369,9 @@ 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) {
+ if (mInGesture) {
+ return;
+ }
final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
@@ -323,6 +402,16 @@ public class PointerTracker {
private void setKeyDetectorInner(KeyDetector keyDetector) {
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
+ mIsAlphabetKeyboard = mKeyboard.mId.isAlphabetKeyboard();
+ mGestureStroke.setGestureSampleLength(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
+ final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
+ if (newKey != mCurrentKey) {
+ if (mDrawingProxy != null) {
+ setReleasedKeyGraphics(mCurrentKey);
+ }
+ mCurrentKey = newKey;
+ }
final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
}
@@ -386,7 +475,7 @@ public class PointerTracker {
return;
}
- if (!key.noKeyPreview()) {
+ if (!key.noKeyPreview() && !mInGesture) {
mDrawingProxy.showKeyPreview(this);
}
updatePressKeyGraphics(key);
@@ -423,6 +512,12 @@ public class PointerTracker {
mDrawingProxy.invalidateKey(key);
}
+ public void drawGestureTrail(Canvas canvas, Paint paint) {
+ if (mInGesture) {
+ mGestureStroke.drawGestureTrail(canvas, paint, mLastX, mLastY);
+ }
+ }
+
public int getLastX() {
return mLastX;
}
@@ -457,6 +552,53 @@ public class PointerTracker {
return newKey;
}
+ private void startBatchInput() {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onStartBatchInput");
+ }
+ mInGesture = true;
+ mListener.onStartBatchInput();
+ }
+
+ private void updateBatchInput(InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onUpdateBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onUpdateBatchInput(batchPoints);
+ }
+
+ private void endBatchInput(InputPointers batchPoints) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, "onEndBatchInput: batchPoints=" + batchPoints.getPointerSize());
+ }
+ mListener.onEndBatchInput(batchPoints);
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ sWasInGesture = true;
+ }
+
+ private void abortBatchInput() {
+ clearBatchInputRecognitionStateOfThisPointerTracker();
+ clearBatchInputPointsOfAllPointerTrackers();
+ }
+
+ private void clearBatchInputRecognitionStateOfThisPointerTracker() {
+ mIsPossibleGesture = false;
+ mInGesture = false;
+ mLastRecognitionPointSize = 0;
+ mLastRecognitionTime = 0;
+ }
+
+ private boolean updateBatchInputRecognitionState(long eventTime, int size) {
+ if (size > mLastRecognitionPointSize
+ && eventTime > mLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) {
+ mLastRecognitionPointSize = size;
+ mLastRecognitionTime = eventTime;
+ return true;
+ }
+ return false;
+ }
+
public void processMotionEvent(int action, int x, int y, long eventTime,
KeyEventHandler handler) {
switch (action) {
@@ -469,7 +611,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);
@@ -504,8 +646,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,6 +656,17 @@ 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.
+ mGestureStroke.addPoint(x, y, 0, false);
+ }
+ }
}
private void onDownEventInternal(int x, int y, long eventTime) {
@@ -525,7 +678,6 @@ public class PointerTracker {
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
- mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
mIgnoreModifierKey = false;
if (key != null) {
@@ -549,16 +701,60 @@ public class PointerTracker {
mIsInSlidingKeyInput = true;
}
- public void onMoveEvent(int x, int y, long eventTime) {
+ private void onGestureMoveEvent(PointerTracker tracker, int x, int y, long eventTime,
+ boolean isHistorical, Key key) {
+ final int gestureTime = (int)(eventTime - tracker.getDownTime());
+ if (sShouldHandleGesture && mIsPossibleGesture) {
+ final GestureStroke stroke = mGestureStroke;
+ stroke.addPoint(x, y, gestureTime, isHistorical);
+ if (!mInGesture && stroke.isStartOfAGesture(gestureTime, sWasInGesture)) {
+ startBatchInput();
+ }
+ }
+
+ if (key != null && mInGesture) {
+ final InputPointers batchPoints = getIncrementalBatchPoints();
+ mDrawingProxy.showGestureTrail(this);
+ if (updateBatchInputRecognitionState(eventTime, batchPoints.getPointerSize())) {
+ updateBatchInput(batchPoints);
+ }
+ }
+ }
+
+ public void onMoveEvent(int x, int y, long eventTime, MotionEvent me) {
if (DEBUG_MOVE_EVENT)
printTouchEvent("onMoveEvent:", x, y, eventTime);
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 +794,34 @@ 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();
+ onUpEventInternal(x, y, eventTime);
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.
+ if (me != null && me.getPointerCount() > 1
+ && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
+ onUpEventInternal(x, y, eventTime);
+ }
+ if (!mIsPossibleGesture) {
+ mKeyAlreadyProcessed = true;
+ }
setReleasedKeyGraphics(oldKey);
}
}
@@ -627,7 +837,9 @@ public class PointerTracker {
if (mIsAllowedSlidingKeyInput) {
onMoveToNewKey(key, x, y);
} else {
- mKeyAlreadyProcessed = true;
+ if (!mIsPossibleGesture) {
+ mKeyAlreadyProcessed = true;
+ }
}
}
}
@@ -639,16 +851,18 @@ public class PointerTracker {
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);
}
- onUpEventInternal();
+ onUpEventInternal(x, y, eventTime);
}
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
@@ -657,30 +871,48 @@ public class PointerTracker {
public void onPhantomUpEvent(int x, int y, long eventTime) {
if (DEBUG_EVENT)
printTouchEvent("onPhntEvent:", x, y, eventTime);
- onUpEventInternal();
+ onUpEventInternal(x, y, eventTime);
mKeyAlreadyProcessed = true;
}
- private void onUpEventInternal() {
+ private void onUpEventInternal(int x, int y, long eventTime) {
mTimerProxy.cancelKeyTimers();
mIsInSlidingKeyInput = false;
+ mIsPossibleGesture = false;
// Release the last pressed key.
setReleasedKeyGraphics(mCurrentKey);
if (mIsShowingMoreKeysPanel) {
mDrawingProxy.dismissMoreKeysPanel();
mIsShowingMoreKeysPanel = false;
}
+
+ 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;
+ }
+ // 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 (!mIsRepeatableKey) {
+ if (mCurrentKey != null && !mCurrentKey.isRepeatable()) {
detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
}
}
public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
+ abortBatchInput();
onLongPressed();
- onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
mIsShowingMoreKeysPanel = true;
+ onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
}
public void onLongPressed() {
@@ -715,12 +947,9 @@ public class PointerTracker {
}
private void startRepeatKey(Key key) {
- if (key != null && key.isRepeatable()) {
+ if (key != null && key.isRepeatable() && !mInGesture) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
- mIsRepeatableKey = true;
- } else {
- mIsRepeatableKey = false;
}
}
@@ -748,7 +977,7 @@ public class PointerTracker {
}
private void startLongPressTimer(Key key) {
- if (key != null && key.isLongPressEnabled()) {
+ if (key != null && key.isLongPressEnabled() && !mInGesture) {
mTimerProxy.startLongPressTimer(this);
}
}
@@ -762,16 +991,13 @@ public class PointerTracker {
int code = key.mCode;
callListenerOnCodeInput(key, code, x, y);
callListenerOnRelease(key, code, false);
+ sWasInGesture = false;
}
- private long mPreviousEventTime;
-
private void printTouchEvent(String title, int x, int y, 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..ae123e29a 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -24,9 +24,10 @@ import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
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);
}
@@ -209,10 +189,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
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..28d6c1d07
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -0,0 +1,198 @@
+/*
+ * 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.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.FloatMath;
+
+import com.android.inputmethod.latin.Constants;
+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 mMinGestureLengthWhileInGesture;
+ private int mMinGestureSampleLength;
+
+ // TODO: Move some of these to resource.
+ private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 1.0f;
+ private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE = 0.5f;
+ private static final int MIN_GESTURE_DURATION = 150; // msec
+ private static final int MIN_GESTURE_DURATION_WHILE_IN_GESTURE = 75; // msec
+ private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT = 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 * Math.PI);
+
+ // Fade based on number of gesture samples, see MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT
+ private static final int DRAWING_GESTURE_FADE_START = 10;
+ private static final int DRAWING_GESTURE_FADE_RATE = 6;
+
+ public GestureStroke(int pointerId) {
+ mPointerId = pointerId;
+ reset();
+ }
+
+ public void setGestureSampleLength(final int keyWidth, final int keyHeight) {
+ // 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);
+ mMinGestureLengthWhileInGesture = (int)(
+ keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH_WHILE_IN_GESTURE);
+ mMinGestureSampleLength = (int)(keyHeight * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_HEIGHT);
+ }
+
+ public boolean isStartOfAGesture(final int downDuration, final boolean wasInGesture) {
+ // The tolerance of the time duration and the stroke length to detect the start of a
+ // gesture stroke should be eased when the previous input was a gesture input.
+ if (wasInGesture) {
+ return downDuration > MIN_GESTURE_DURATION_WHILE_IN_GESTURE
+ && mLength > mMinGestureLengthWhileInGesture;
+ }
+ 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 p1x, final int p1y,
+ final int p2x, final int p2y) {
+ final float dx = p1x - p2x;
+ final float dy = p1y - p2y;
+ // TODO: Optimize out this {@link FloatMath#sqrt(float)} call.
+ return FloatMath.sqrt(dx * dx + dy * dy);
+ }
+
+ private static float getAngle(final int p1x, final int p1y, final int p2x, final int p2y) {
+ final int dx = p1x - p2x;
+ final int dy = p1y - p2y;
+ if (dx == 0 && dy == 0) return 0;
+ 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;
+ }
+
+ public void drawGestureTrail(Canvas canvas, Paint paint, int lastX, int lastY) {
+ // TODO: These paint parameter interpolation should be tunable, possibly introduce an object
+ // that implements an interface such as Paint getPaint(int step, int strokePoints)
+ final int size = mXCoordinates.getLength();
+ int[] xCoords = mXCoordinates.getPrimitiveArray();
+ int[] yCoords = mYCoordinates.getPrimitiveArray();
+ int alpha = Constants.Color.ALPHA_OPAQUE;
+ for (int i = size - 1; i > 0 && alpha > 0; i--) {
+ paint.setAlpha(alpha);
+ if (size - i > DRAWING_GESTURE_FADE_START) {
+ alpha -= DRAWING_GESTURE_FADE_RATE;
+ }
+ canvas.drawLine(xCoords[i - 1], yCoords[i - 1], xCoords[i], yCoords[i], paint);
+ if (i == size - 1) {
+ canvas.drawLine(lastX, lastY, xCoords[i], yCoords[i], paint);
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index c4452a5f5..53261205d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -68,12 +68,20 @@ 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);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 80f4f259b..291b3b943 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -18,6 +18,7 @@ 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.R;
@@ -89,7 +90,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 = new SparseArray<Object>();
public DeclaredKeyStyle(String parentStyleName) {
mParentStyleName = parentStyleName;
@@ -100,8 +101,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 +114,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 +127,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 +137,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..f7981a320 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -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..5155851fe 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -20,6 +20,7 @@ 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.R;
@@ -31,8 +32,7 @@ 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>();
@@ -76,7 +76,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..620ab7bd3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -195,38 +195,37 @@ public final class KeyboardTextsSet {
/* 87 */ "more_keys_for_symbols_0",
/* 88 */ "keylabel_for_comma",
/* 89 */ "more_keys_for_comma",
- /* 90 */ "keylabel_for_symbols_exclamation",
- /* 91 */ "keylabel_for_symbols_question",
- /* 92 */ "keylabel_for_symbols_semicolon",
- /* 93 */ "keylabel_for_symbols_percent",
- /* 94 */ "more_keys_for_symbols_exclamation",
- /* 95 */ "more_keys_for_symbols_question",
- /* 96 */ "more_keys_for_symbols_semicolon",
- /* 97 */ "more_keys_for_symbols_percent",
- /* 98 */ "keylabel_for_tablet_comma",
- /* 99 */ "keyhintlabel_for_tablet_comma",
- /* 100 */ "more_keys_for_tablet_comma",
- /* 101 */ "keyhintlabel_for_tablet_period",
- /* 102 */ "more_keys_for_tablet_period",
- /* 103 */ "keylabel_for_apostrophe",
- /* 104 */ "keyhintlabel_for_apostrophe",
- /* 105 */ "more_keys_for_apostrophe",
- /* 106 */ "more_keys_for_am_pm",
- /* 107 */ "settings_as_more_key",
- /* 108 */ "shortcut_as_more_key",
- /* 109 */ "action_next_as_more_key",
- /* 110 */ "action_previous_as_more_key",
- /* 111 */ "label_to_more_symbol_key",
- /* 112 */ "label_to_more_symbol_for_tablet_key",
- /* 113 */ "label_tab_key",
- /* 114 */ "label_to_phone_numeric_key",
- /* 115 */ "label_to_phone_symbols_key",
- /* 116 */ "label_time_am",
- /* 117 */ "label_time_pm",
- /* 118 */ "label_to_symbol_key_pcqwerty",
- /* 119 */ "keylabel_for_popular_domain",
- /* 120 */ "more_keys_for_popular_domain",
- /* 121 */ "more_keys_for_smiley",
+ /* 90 */ "keylabel_for_symbols_question",
+ /* 91 */ "keylabel_for_symbols_semicolon",
+ /* 92 */ "keylabel_for_symbols_percent",
+ /* 93 */ "more_keys_for_symbols_exclamation",
+ /* 94 */ "more_keys_for_symbols_question",
+ /* 95 */ "more_keys_for_symbols_semicolon",
+ /* 96 */ "more_keys_for_symbols_percent",
+ /* 97 */ "keylabel_for_tablet_comma",
+ /* 98 */ "keyhintlabel_for_tablet_comma",
+ /* 99 */ "more_keys_for_tablet_comma",
+ /* 100 */ "keyhintlabel_for_tablet_period",
+ /* 101 */ "more_keys_for_tablet_period",
+ /* 102 */ "keylabel_for_apostrophe",
+ /* 103 */ "keyhintlabel_for_apostrophe",
+ /* 104 */ "more_keys_for_apostrophe",
+ /* 105 */ "more_keys_for_am_pm",
+ /* 106 */ "settings_as_more_key",
+ /* 107 */ "shortcut_as_more_key",
+ /* 108 */ "action_next_as_more_key",
+ /* 109 */ "action_previous_as_more_key",
+ /* 110 */ "label_to_more_symbol_key",
+ /* 111 */ "label_to_more_symbol_for_tablet_key",
+ /* 112 */ "label_tab_key",
+ /* 113 */ "label_to_phone_numeric_key",
+ /* 114 */ "label_to_phone_symbols_key",
+ /* 115 */ "label_time_am",
+ /* 116 */ "label_time_pm",
+ /* 117 */ "label_to_symbol_key_pcqwerty",
+ /* 118 */ "keylabel_for_popular_domain",
+ /* 119 */ "more_keys_for_popular_domain",
+ /* 120 */ "more_keys_for_smiley",
};
private static final String EMPTY = "";
@@ -331,52 +330,104 @@ public final class KeyboardTextsSet {
/* 87 */ "\u207F,\u2205",
/* 88 */ ",",
/* 89 */ EMPTY,
- /* 90 */ "!",
- /* 91 */ "?",
- /* 92 */ ";",
- /* 93 */ "%",
+ /* 90 */ "?",
+ /* 91 */ ";",
+ /* 92 */ "%",
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 94 */ "\u00A1",
+ /* 93 */ "\u00A1",
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 95 */ "\u00BF",
- /* 96 */ EMPTY,
+ /* 94 */ "\u00BF",
+ /* 95 */ EMPTY,
// U+2030: "‰" PER MILLE SIGN
- /* 97 */ "\u2030",
- /* 98 */ ",",
+ /* 96 */ "\u2030",
+ /* 97 */ ",",
+ /* 98 */ "!",
/* 99 */ "!",
- /* 100 */ "!",
+ /* 100 */ "?",
/* 101 */ "?",
- /* 102 */ "?",
- /* 103 */ "\'",
+ /* 102 */ "\'",
+ /* 103 */ "\"",
/* 104 */ "\"",
- /* 105 */ "\"",
- /* 106 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
- /* 107 */ "!icon/settings_key|!code/key_settings",
- /* 108 */ "!icon/shortcut_key|!code/key_shortcut",
- /* 109 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
- /* 110 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
+ /* 105 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
+ /* 106 */ "!icon/settings_key|!code/key_settings",
+ /* 107 */ "!icon/shortcut_key|!code/key_shortcut",
+ /* 108 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
+ /* 109 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
- /* 111 */ "= \\ <",
+ /* 110 */ "= \\ <",
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
- /* 112 */ "~ \\ {",
+ /* 111 */ "~ \\ {",
// Label for "Tab" key. Must be short to fit on key!
- /* 113 */ "Tab",
+ /* 112 */ "Tab",
// Label for "switch to phone numeric" key. Must be short to fit on key!
- /* 114 */ "123",
+ /* 113 */ "123",
// Label for "switch to phone symbols" key. Must be short to fit on key!
// U+FF0A: "*" FULLWIDTH ASTERISK
// U+FF03: "#" FULLWIDTH NUMBER SIGN
- /* 115 */ "\uFF0A\uFF03",
+ /* 114 */ "\uFF0A\uFF03",
// Key label for "ante meridiem"
- /* 116 */ "AM",
+ /* 115 */ "AM",
// Key label for "post meridiem"
- /* 117 */ "PM",
+ /* 116 */ "PM",
// Label for "switch to symbols" key on PC QWERTY layout
- /* 118 */ "Sym",
- /* 119 */ ".com",
+ /* 117 */ "Sym",
+ /* 118 */ ".com",
// popular web domains for the locale - most popular, displayed on the keyboard
- /* 120 */ "!hasLabels!,.net,.org,.gov,.edu",
- /* 121 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+ /* 119 */ "!hasLabels!,.net,.org,.gov,.edu",
+ /* 120 */ "!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 */
@@ -483,25 +534,24 @@ public final class KeyboardTextsSet {
// U+060C: "،" ARABIC COMMA
/* 88 */ "\u060C",
/* 89 */ "\\,",
- /* 90 */ null,
- /* 91 */ "\u061F",
- /* 92 */ "\u061B",
+ /* 90 */ "\u061F",
+ /* 91 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
- /* 93 */ "\u066A",
- /* 94 */ null,
- /* 95 */ "?",
- /* 96 */ ";",
+ /* 92 */ "\u066A",
+ /* 93 */ null,
+ /* 94 */ "?",
+ /* 95 */ ";",
// U+2030: "‰" PER MILLE SIGN
- /* 97 */ "\\%,\u2030",
- /* 98~ */
+ /* 96 */ "\\%,\u2030",
+ /* 97~ */
null, null, null, null, null,
- /* ~102 */
+ /* ~101 */
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
// U+061F: "؟" ARABIC QUESTION MARK
- /* 103 */ "\u060C",
- /* 104 */ "\u061F",
- /* 105 */ "\u061F,\u061B,!,:,-,/,\',\"",
+ /* 102 */ "\u060C",
+ /* 103 */ "\u061F",
+ /* 104 */ "\u061F,\u061B,!,:,-,/,\',\"",
};
/* Language be: Belarusian */
@@ -861,27 +911,18 @@ public final class KeyboardTextsSet {
/* ~47 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 48 */ "!fixedColumnOrder!9,\",\',#,-,\u00A1,!,\u00BF,\\,,?,@,&,\\%,+,;,:,/,(,)",
+ /* 48 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
/* 49~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, 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,
+ /* ~98 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 90 */ "\u00A1",
+ /* 99 */ "!,\u00A1",
+ /* 100 */ 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,?",
+ /* 101 */ "?,\u00BF",
};
/* Language et: Estonian */
@@ -1088,29 +1129,28 @@ public final class KeyboardTextsSet {
// U+060C: "،" ARABIC COMMA
/* 88 */ "\u060C",
/* 89 */ "\\,",
- /* 90 */ null,
- /* 91 */ "\u061F",
- /* 92 */ "\u061B",
+ /* 90 */ "\u061F",
+ /* 91 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
- /* 93 */ "\u066A",
- /* 94 */ null,
- /* 95 */ "?",
- /* 96 */ ";",
+ /* 92 */ "\u066A",
+ /* 93 */ null,
+ /* 94 */ "?",
+ /* 95 */ ";",
// U+2030: "‰" PER MILLE SIGN
- /* 97 */ "\\%,\u2030",
+ /* 96 */ "\\%,\u2030",
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
// U+061F: "؟" ARABIC QUESTION MARK
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
// U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
- /* 98 */ "\u060C",
- /* 99 */ "!",
- /* 100 */ "!,\\,",
- /* 101 */ "\u061F",
- /* 102 */ "\u061F,?",
- /* 103 */ "\u060C",
- /* 104 */ "\u061F",
- /* 105 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 97 */ "\u060C",
+ /* 98 */ "!",
+ /* 99 */ "!,\\,",
+ /* 100 */ "\u061F",
+ /* 101 */ "\u061F,?",
+ /* 102 */ "\u060C",
+ /* 103 */ "\u061F",
+ /* 104 */ "!fixedColumnOrder!4,:,!,\u061F,\u061B,-,/,\u00AB|\u00BB,\u00BB|\u00AB",
};
/* Language fi: Finnish */
@@ -2111,6 +2151,24 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null,
/* ~34 */
+ // 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",
// U+045B: "ћ" CYRILLIC SMALL LETTER TSHE
@@ -2124,6 +2182,7 @@ public final class KeyboardTextsSet {
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
/* 40 */ "\u045D",
/* 41 */ 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
@@ -2182,6 +2241,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
@@ -2332,6 +2496,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 +2668,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 +2702,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..e4a71844a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -28,8 +28,13 @@ public class PointerTrackerQueue {
private static final String TAG = PointerTrackerQueue.class.getSimpleName();
private static final boolean DEBUG = false;
+ // TODO: Use ring buffer instead of {@link LinkedList}.
private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
+ public int size() {
+ return mQueue.size();
+ }
+
public synchronized void add(PointerTracker tracker) {
mQueue.add(tracker);
}
@@ -81,6 +86,20 @@ public class PointerTrackerQueue {
}
}
+ public synchronized boolean hasModifierKeyOlderThan(PointerTracker tracker) {
+ final Iterator<PointerTracker> it = mQueue.iterator();
+ while (it.hasNext()) {
+ final PointerTracker t = it.next();
+ if (t == tracker) {
+ break;
+ }
+ if (t.isModifier()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public synchronized boolean isAnyInSlidingKeyInput() {
for (final PointerTracker tracker : mQueue) {
if (tracker.isInSlidingKeyInput()) {
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..2a53c59bb
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -0,0 +1,172 @@
+/*
+ * 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.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.widget.RelativeLayout;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.R;
+
+public class PreviewPlacerView extends RelativeLayout {
+ // TODO: Move these parameters to attributes of {@link KeyboardView}.
+ private final static int GESTURE_DRAWING_COLOR = 0xff33b5e5;
+ private static final int GESTURE_PREVIEW_TEXT_COLOR = Color.WHITE;
+ private static final int GESTURE_PREVIEW_TEXT_SHADING_COLOR = 0xff33b5e5;
+ private static final int GESTURE_PREVIEW_TEXT_SHADOW_COLOR = 0xff252525;
+ private static final int GESTURE_PREVIEW_CONNECTOR_COLOR = Color.WHITE;
+
+ private final Paint mGesturePaint;
+ private final int mGesturePreviewTraileWidth;
+ private final Paint mTextPaint;
+ private final int mGesturePreviewTextOffset;
+ private final int mGesturePreviewTextShadowBorder;
+ private final int mGesturePreviewTextShadingBorder;
+ private final int mGesturePreviewTextConnectorWidth;
+
+ private int mXOrigin;
+ private int mYOrigin;
+
+ private final SparseArray<PointerTracker> mPointers = new SparseArray<PointerTracker>();
+
+ private String mGesturePreviewText;
+
+ public PreviewPlacerView(Context context) {
+ super(context);
+ setWillNotDraw(false);
+
+ final Resources res = getResources();
+ // TODO: Move these parameters to attributes of {@link KeyboardView}.
+ mGesturePreviewTraileWidth = res.getDimensionPixelSize(
+ R.dimen.gesture_preview_trail_width);
+ final int textSize = res.getDimensionPixelSize(R.dimen.gesture_preview_text_size);
+ mGesturePreviewTextOffset = res.getDimensionPixelSize(
+ R.dimen.gesture_preview_text_offset);
+ mGesturePreviewTextShadowBorder = res.getDimensionPixelOffset(
+ R.dimen.gesture_preview_text_shadow_border);
+ mGesturePreviewTextShadingBorder = res.getDimensionPixelOffset(
+ R.dimen.gesture_preview_text_shading_border);
+ mGesturePreviewTextConnectorWidth = res.getDimensionPixelOffset(
+ R.dimen.gesture_preview_text_connector_width);
+
+ mGesturePaint = new Paint();
+ mGesturePaint.setAntiAlias(true);
+ mGesturePaint.setStyle(Paint.Style.STROKE);
+ mGesturePaint.setStrokeJoin(Paint.Join.ROUND);
+ mGesturePaint.setColor(GESTURE_DRAWING_COLOR);
+ mGesturePaint.setStrokeWidth(mGesturePreviewTraileWidth);
+
+ mTextPaint = new Paint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setStrokeJoin(Paint.Join.ROUND);
+ mTextPaint.setTextAlign(Align.CENTER);
+ mTextPaint.setTextSize(textSize);
+ }
+
+ public void setOrigin(int x, int y) {
+ mXOrigin = x;
+ mYOrigin = y;
+ }
+
+ public void invalidatePointer(PointerTracker tracker) {
+ synchronized (mPointers) {
+ mPointers.put(tracker.mPointerId, tracker);
+ // TODO: Should narrow the invalidate region.
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ // TDOD: Add user settings option to control drawing gesture trail and gesture preview.
+ synchronized (mPointers) {
+ canvas.translate(mXOrigin, mYOrigin);
+ final int trackerCount = mPointers.size();
+ boolean floatingPreviewHasDrawn = false;
+ for (int index = 0; index < trackerCount; index++) {
+ final PointerTracker tracker = mPointers.valueAt(index);
+ tracker.drawGestureTrail(canvas, mGesturePaint);
+ // TODO: Figure out more cleaner way to draw gesture preview text.
+ if (!floatingPreviewHasDrawn) {
+ drawGesturePreviewText(canvas, tracker, mGesturePreviewText);
+ floatingPreviewHasDrawn = true;
+ }
+ }
+ canvas.translate(-mXOrigin, -mYOrigin);
+ }
+ }
+
+ public void setGesturePreviewText(String gesturePreviewText) {
+ mGesturePreviewText = gesturePreviewText;
+ invalidate();
+ }
+
+ private void drawGesturePreviewText(Canvas canvas, PointerTracker tracker,
+ String gesturePreviewText) {
+ if (TextUtils.isEmpty(gesturePreviewText)) {
+ return;
+ }
+
+ final Paint paint = mTextPaint;
+ final int lastX = tracker.getLastX();
+ final int lastY = tracker.getLastY();
+ final int textSize = (int)paint.getTextSize();
+ final int canvasWidth = canvas.getWidth();
+
+ final int halfTextWidth = (int)paint.measureText(gesturePreviewText) / 2 + textSize;
+ final int textX = Math.min(Math.max(lastX, halfTextWidth), canvasWidth - halfTextWidth);
+
+ int textY = Math.max(-textSize, lastY - mGesturePreviewTextOffset);
+ if (textY < 0) {
+ // Paint black text shadow if preview extends above keyboard region.
+ paint.setStyle(Paint.Style.FILL_AND_STROKE);
+ paint.setColor(GESTURE_PREVIEW_TEXT_SHADOW_COLOR);
+ paint.setStrokeWidth(mGesturePreviewTextShadowBorder);
+ canvas.drawText(gesturePreviewText, textX, textY, paint);
+ }
+
+ // Paint the vertical line connecting the touch point to the preview text.
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(GESTURE_PREVIEW_CONNECTOR_COLOR);
+ paint.setStrokeWidth(mGesturePreviewTextConnectorWidth);
+ 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(GESTURE_PREVIEW_TEXT_SHADING_COLOR);
+ paint.setStrokeWidth(mGesturePreviewTextShadingBorder);
+ canvas.drawText(gesturePreviewText, textX, textY, paint);
+
+ // Paint the text preview
+ paint.setColor(GESTURE_PREVIEW_TEXT_COLOR);
+ paint.setStyle(Paint.Style.FILL);
+ canvas.drawText(gesturePreviewText, 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/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index e0452483c..a66337404 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,7 @@ public class AutoCorrection {
}
final CharSequence lowerCasedWord = word.toString().toLowerCase();
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
+ if (key.equals(Dictionary.TYPE_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 +64,7 @@ public class AutoCorrection {
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
+ if (key.equals(Dictionary.TYPE_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
@@ -92,11 +75,12 @@ public class AutoCorrection {
return maxFreq;
}
- public static boolean allowsToBeAutoCorrected(
+ // Returns true if this is a whitelist entry, or it isn't in any dictionary.
+ public static boolean isWhitelistedOrNotAWord(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
+ (WhitelistDictionary)dictionaries.get(Dictionary.TYPE_WHITELIST);
// If "word" is in the whitelist dictionary, it should not be auto corrected.
if (whitelistDictionary != null
&& whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
@@ -105,33 +89,19 @@ public class AutoCorrection {
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 +109,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..534cffb2d 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -20,7 +20,9 @@ import android.content.Context;
import android.text.TextUtils;
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,19 +42,20 @@ 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 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;
@@ -65,14 +68,12 @@ 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);
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
}
@@ -82,113 +83,77 @@ 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 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);
+ int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodes, 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, int beforeLength, char[] after, int afterLength, int score);
private static native int editDistanceNative(
char[] before, int beforeLength, char[] after, int afterLength);
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) {
+ if (!isValidDictionary()) return null;
+ Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
+ Arrays.fill(mOutputChars, (char) 0);
+ Arrays.fill(mOutputScores, 0);
+ // 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++) {
+ mInputCodes[i] = composer.getCodeAt(i);
}
}
- }
-
- // 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);
+ 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(), ips.getXCoordinates(),
+ ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
+ mInputCodes, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
+ mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
+ final int count = Math.min(tmpCount, MAX_PREDICTIONS);
+
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<SuggestedWordInfo>();
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) {
+ while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
++len;
}
if (len > 0) {
- callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
- Dictionary.UNIGRAM);
+ suggestions.add(new SuggestedWordInfo(
+ new String(mOutputChars, start, len),
+ mOutputScores[j], SuggestedWordInfo.KIND_CORRECTION, 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);
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/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index e79db367c..1242967ad 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
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/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 7cd9bc2a8..fd40aa6da 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,7 +16,12 @@
package com.android.inputmethod.latin;
+import android.text.TextUtils;
+
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,55 +33,36 @@ 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";
+ public static final String TYPE_WHITELIST ="whitelist";
+ 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)
- */
- abstract public void getWords(final WordComposer composer,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- 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
+ * @return the list of suggestions (possibly null if none)
*/
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- // empty base implementation
- }
+ // 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);
/**
* Checks if the given word occurs in the dictionary
@@ -115,4 +101,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..88ac07d7a 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,11 +33,13 @@ public class DictionaryCollection extends Dictionary {
private final String TAG = DictionaryCollection.class.getSimpleName();
protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
- public DictionaryCollection() {
+ public DictionaryCollection(final String dictType) {
+ super(dictType);
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
}
- public DictionaryCollection(Dictionary... dictionaries) {
+ public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
+ super(dictType);
if (null == dictionaries) {
mDictionaries = new CopyOnWriteArrayList<Dictionary>();
} else {
@@ -44,23 +48,29 @@ public class DictionaryCollection extends Dictionary {
}
}
- public DictionaryCollection(Collection<Dictionary> dictionaries) {
+ public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
+ super(dictType);
mDictionaries = new CopyOnWriteArrayList<Dictionary>(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 = new ArrayList<SuggestedWordInfo>();
+ final int length = dictionaries.size();
+ for (int i = 0; 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..06a5f4b72 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -49,7 +49,8 @@ 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>();
@@ -59,7 +60,7 @@ public class DictionaryFactory {
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..016530abb 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;
@@ -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;
@@ -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..5d7995dc2 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -17,10 +17,12 @@
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 +39,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 +152,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() {
@@ -247,27 +248,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 = new ArrayList<SuggestedWordInfo>();
+ 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 = new ArrayList<SuggestedWordInfo>();
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.
@@ -281,10 +298,11 @@ public class ExpandableDictionary extends Dictionary {
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 +386,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 +429,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 +464,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 +479,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
@@ -483,7 +504,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 +512,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 +535,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(
@@ -580,32 +603,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 +638,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) {
@@ -651,8 +657,9 @@ public class ExpandableDictionary extends Dictionary {
} while (node != null);
if (freq >= 0) {
- callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index,
- freq, mDicTypeId, Dictionary.BIGRAM);
+ 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..e561f5956 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -29,7 +29,6 @@ public class InputAttributes {
final public boolean mInputTypeNoAutoCorrect;
final public boolean mIsSettingsSuggestionStripOn;
final public boolean mApplicationSpecifiedCompletionOn;
- final public int mEditorAction;
public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
final int inputType = null != editorInfo ? editorInfo.inputType : 0;
@@ -64,7 +63,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 +91,6 @@ public class InputAttributes {
mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
}
- mEditorAction = (editorInfo == null) ? EditorInfo.IME_ACTION_UNSPECIFIED
- : editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
}
@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..cbc916a7e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputPointers.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.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, startPos, 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();
+ }
+}
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..1aac734ba 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,15 +48,12 @@ 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;
@@ -64,15 +61,17 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.CompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
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 +82,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 +103,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 +122,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 +140,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;
@@ -203,14 +179,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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_SUGGESTION_STRIP = 7;
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
private long mDoubleSpacesTurnIntoPeriodTimeout;
+ private long mDoubleSpaceTimerStart;
public UIHandler(LatinIME outerInstance) {
super(outerInstance);
@@ -231,29 +206,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 +236,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.
@@ -393,7 +355,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, mKeyboardSwitcher);
}
InputMethodManagerCompatWrapper.init(this);
SubtypeSwitcher.init(this);
@@ -411,14 +373,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
loadSettings();
- ImfUtils.setAdditionalInputMethodSubtypes(this, mSettingsValues.getAdditionalSubtypes());
-
- // TODO: remove the following when it's not needed by updateCorrectionMode() any more
- mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
- updateCorrectionMode();
+ ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
Utils.GCUtils.getInstance().reset();
boolean tryGC = true;
+ // Shouldn't this be removed? I think that from Honeycomb on, the GC is now actually working
+ // as expected and this code is useless.
for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
try {
initSuggest();
@@ -454,42 +414,49 @@ 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);
+ 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());
}
+ @Override
+ public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable) {
+ mIsMainDictionaryAvailable = isMainDictionaryAvailable;
+ updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
+ }
+
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);
}
}
@@ -569,9 +535,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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
+ mConnection.beginBatchEdit();
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mConnection.finishComposingText();
+ mConnection.endBatchEdit();
if (isShowingOptionDialog())
mOptionsDialog.dismiss();
}
@@ -590,9 +557,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);
}
@@ -639,7 +606,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();
+ MainKeyboardView inputView = switcher.getKeyboardView();
if (editorInfo == null) {
Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -692,53 +659,77 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
accessUtils.onStartInputViewInternal(editorInfo, restarting);
}
- mSubtypeSwitcher.updateParametersOnStartInputView();
+ if (!restarting) {
+ mSubtypeSwitcher.updateParametersOnStartInputView();
+ }
// 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);
+ final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart
+ || mLastSelectionEnd != editorInfo.initialSelEnd;
+ if (!restarting || 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 (!restarting) {
+ inputView.closing();
+ loadSettings();
+
+ if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
+ }
- if (mSuggestionsView != null)
- mSuggestionsView.clear();
+ switcher.loadKeyboard(editorInfo, mCurrentSettings);
+ updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
+ }
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);
+ inputView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
+ mCurrentSettings.mKeyPreviewPopupDismissDelay);
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();
@@ -748,6 +739,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
super.onFinishInput();
LatinImeLogger.commit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().latinIME_onFinishInputInternal();
+ }
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) inputView.closing();
@@ -759,7 +753,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) inputView.cancelAllMessages();
// Remove pending messages related to update suggestions
- mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateSuggestionStrip();
}
@Override
@@ -768,7 +762,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 +773,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 +841,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
@Override
public void onExtractedTextClicked() {
- if (isSuggestionsRequested()) return;
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
super.onExtractedTextClicked();
}
@@ -857,7 +857,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);
}
@@ -888,39 +888,37 @@ 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;
- }
-
- 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);
+ if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
+ mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
+ if (applicationSpecifiedCompletions == null) {
+ clearSuggestionStrip();
+ return;
}
+
+ 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);
}
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 MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
final boolean shouldShowSuggestions = shown
&& (needsInputViewShown ? inputViewShown : true);
@@ -958,7 +956,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;
}
@@ -981,7 +979,7 @@ 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();
+ final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
if (keyboardView != null && keyboardView.isShown()) {
if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
touchY -= suggestionsHeight;
@@ -1001,7 +999,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 +1014,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 +1027,26 @@ 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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_commitText(typedWord);
}
final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
mLastComposedWord = mWordComposer.commitWord(
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
separatorCode, prevWord);
}
- updateSuggestions();
+ updateSuggestionStrip();
}
+ // 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,27 +1064,23 @@ 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);
+ 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);
+ mConnection.deleteSurroundingText(2, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(2);
}
- ic.commitText(lastTwo.charAt(1) + " ", 1);
+ mConnection.commitText(lastTwo.charAt(1) + " ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit();
}
@@ -1098,18 +1088,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- 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);
+ mConnection.deleteSurroundingText(2, 0);
+ mConnection.commitText(". ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_doubleSpaceAutoPeriod();
}
@@ -1131,29 +1120,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 +1164,14 @@ 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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_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 +1189,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));
}
@@ -1239,24 +1207,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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);
- }
+ // 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());
+ }
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_sendKeyCodePoint(code);
}
}
@@ -1268,11 +1233,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mDeleteCount = 0;
}
mLastKeyTime = when;
+ mConnection.beginBatchEdit();
if (ProductionFlag.IS_EXPERIMENTAL) {
- if (ResearchLogger.sIsLogging) {
- ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y);
- }
+ ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
}
final KeyboardSwitcher switcher = mKeyboardSwitcher;
@@ -1321,23 +1285,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 = NOT_A_TOUCH_COORDINATE;
+ keyY = NOT_A_TOUCH_COORDINATE;
}
+ handleCharacter(primaryCode, keyX, keyY, spaceState);
}
mExpectingUpdateSelection = true;
mShouldSwitchToLastSubtype = true;
@@ -1349,23 +1319,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
&& primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
mEnteredText = null;
+ mConnection.endBatchEdit();
}
+ // 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);
+ mConnection.beginBatchEdit();
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ text = specificTldProcessingOnTextInput(text);
if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
- ic.commitText(text, 1);
+ mConnection.commitText(text, 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(text);
}
- ic.endBatchEdit();
+ mConnection.endBatchEdit();
mKeyboardSwitcher.updateShiftState();
mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
mSpaceState = SPACE_STATE_NONE;
@@ -1373,9 +1343,57 @@ 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.setAutoCapitalized(
+ getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
+ }
+
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {
+ mWordComposer.setBatchInputPointers(batchPointers);
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
+ final String gesturePreviewText = (suggestedWords.size() > 0)
+ ? suggestedWords.getWord(0) : null;
+ mKeyboardSwitcher.getKeyboardView().showGesturePreviewText(gesturePreviewText);
+ }
+
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mWordComposer.setBatchInputPointers(batchPointers);
+ final SuggestedWords suggestedWords = getSuggestedWords();
+ showSuggestionStrip(suggestedWords, null);
+ mKeyboardSwitcher.getKeyboardView().showGesturePreviewText(null);
+ 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 +1402,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 +1411,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,24 +1419,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);
+ mConnection.deleteSurroundingText(length, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(length);
}
@@ -1430,19 +1440,16 @@ 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);
+ mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1);
}
@@ -1450,17 +1457,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} 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,8 +1479,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);
+ mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
+ mConnection.deleteSurroundingText(lengthToDelete, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete);
}
@@ -1490,40 +1498,39 @@ 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);
+ mConnection.deleteSurroundingText(1, 0);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1);
}
if (mDeleteCount > DELETE_ACCELERATE_AT) {
- ic.deleteSurroundingText(1, 0);
+ mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1);
}
}
}
- 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 +1541,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 +1556,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 +1569,73 @@ 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);
- }
- mHandler.postUpdateSuggestions();
+ final int keyX, keyY;
+ if (KeyboardActionListener.Adapter.isInvalidCoordinate(x)
+ || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) {
+ keyX = x;
+ keyY = y;
+ } else {
+ final KeyDetector keyDetector =
+ mKeyboardSwitcher.getKeyboardView().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.setAutoCapitalized(
+ getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
+ }
+ mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode,
spaceState, KeyboardActionListener.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 && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
+ commitCurrentAutoCorrection(primaryCode);
didAutoCorrect = true;
} else {
- commitTyped(ic, primaryCode);
+ commitTyped(primaryCode);
}
}
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
KeyboardActionListener.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,13 +1643,12 @@ 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) {
// If we are in phantom space state, and the user presses a separator, we want to
@@ -1682,9 +1667,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
Utils.Stats.onSeparator((char)primaryCode, x, y);
- if (ic != null) {
- ic.endBatchEdit();
- }
return didAutoCorrect;
}
@@ -1695,153 +1677,132 @@ 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();
+ MainKeyboardView 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();
+ // 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 isShowingSuggestionsStrip() {
- return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
- || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
- }
-
- 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,21 +1812,21 @@ 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.");
@@ -1878,27 +1839,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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();
+ final int x, final int y) {
+ 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.
@@ -1915,29 +1871,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
+ completionInfo.getText(), x, y);
}
return;
}
@@ -1953,6 +1912,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mExpectingUpdateSelection = true;
commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
+ mConnection.endBatchEdit();
// Don't allow cancellation of manual pick
mLastComposedWord.deactivate();
mSpaceState = SPACE_STATE_PHANTOM;
@@ -1960,37 +1920,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));
+ // 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, 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 (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,22 +1943,11 @@ 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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_commitText(chosenWord);
}
// Add the word to the user history dictionary
final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
@@ -2026,42 +1959,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());
@@ -2070,22 +1972,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
if (TextUtils.isEmpty(suggestion)) 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()) {
secondWord = suggestion.toString().toLowerCase(
@@ -2098,95 +1993,36 @@ 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);
+ mConnection.deleteSurroundingText(length, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(length);
}
- ic.setComposingText(word, 1);
- mHandler.postUpdateSuggestions();
+ 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,7 +2044,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
}
}
- ic.deleteSurroundingText(deleteLength, 0);
+ mConnection.deleteSurroundingText(deleteLength, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(deleteLength);
}
@@ -2216,122 +2052,68 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
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);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
- }
- 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);
+ mConnection.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_revertSwapPunctuation();
+ ResearchLogger.latinIME_revertCommit(originallyTypedWord);
}
- 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.
+ // according to new language or mode. Called from SubtypeSwitcher.
public void onRefreshKeyboard() {
// When the device locale is changed in SetupWizard etc., this method may get called via
// onConfigurationChanged before SoftInputWindow is shown.
+ initSuggest();
+ loadSettings();
if (mKeyboardSwitcher.getKeyboardView() != null) {
// Reload keyboard because the current language has been changed.
- mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
+ updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability();
}
- initSuggest();
- updateCorrectionMode();
- loadSettings();
// 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();
+ }
+
+ private void updateKeyboardViewGestureHandlingModeByMainDictionaryAvailability() {
+ final MainKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
+ if (keyboardView != null) {
+ final boolean shouldHandleGesture = mCurrentSettings.mGestureInputEnabled
+ && mIsMainDictionaryAvailable;
+ keyboardView.setGestureHandlingMode(shouldHandleGesture);
}
}
// 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());
}
+ // 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 +2134,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 +2154,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,10 +2209,10 @@ 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) {
+ public void showOptionDialog(AlertDialog dialog) {
final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
if (windowToken == null) return;
@@ -2470,13 +2239,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/NativeUtils.java b/java/src/com/android/inputmethod/latin/NativeUtils.java
new file mode 100644
index 000000000..9cc2bc02e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/NativeUtils.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+public class NativeUtils {
+ static {
+ JniUtils.loadNativeLibrary();
+ }
+
+ private NativeUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ /**
+ * This method just calls up libm's powf() directly.
+ */
+ public static native float powf(float x, float y);
+}
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..387d45a53
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
@@ -0,0 +1,134 @@
+/*
+ * 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;
+ }
+ }
+}
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..8b4c17322
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -0,0 +1,421 @@
+/*
+ * 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();
+ }
+
+ public void commitText(final CharSequence text, final int i) {
+ checkBatchEdit();
+ if (null != mIC) mIC.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);
+ }
+
+ public void performEditorAction(final int actionId) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) mIC.performEditorAction(actionId);
+ }
+
+ public void sendKeyEvent(final KeyEvent keyEvent) {
+ checkBatchEdit();
+ if (null != mIC) mIC.sendKeyEvent(keyEvent);
+ }
+
+ public void setComposingText(final CharSequence text, final int i) {
+ checkBatchEdit();
+ if (null != mIC) mIC.setComposingText(text, i);
+ }
+
+ public void setSelection(final int from, final int to) {
+ checkBatchEdit();
+ if (null != mIC) mIC.setSelection(from, to);
+ }
+
+ public void commitCorrection(final CorrectionInfo correctionInfo) {
+ checkBatchEdit();
+ if (null != mIC) mIC.commitCorrection(correctionInfo);
+ }
+
+ public void commitCompletion(final CompletionInfo completionInfo) {
+ checkBatchEdit();
+ if (null != mIC) mIC.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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_deleteSurroundingText(1);
+ }
+ }
+ }
+
+ 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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_deleteSurroundingText(2);
+ }
+ commitText(" ", 1);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
+ }
+ 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);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_deleteSurroundingText(2);
+ }
+ commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_revertSwapPunctuation();
+ }
+ return true;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 4bb21720b..45608f439 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
@@ -69,9 +70,8 @@ public class Settings extends InputMethodSettingsFragment
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 =
@@ -87,9 +87,7 @@ 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;
@@ -100,7 +98,6 @@ public class Settings extends InputMethodSettingsFragment
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));
}
@@ -128,7 +125,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 +151,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,19 +158,30 @@ 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);
}
+ mKeyPreviewPopupDismissDelay.setEnabled(
+ SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
}
final CheckBoxPreference includeOtherImesInLanguageSwitchList =
@@ -185,23 +189,6 @@ public class Settings extends InputMethodSettingsFragment
includeOtherImesInLanguageSwitchList.setEnabled(
!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 +198,12 @@ public class Settings extends InputMethodSettingsFragment
textCorrectionGroup.removePreference(dictionaryLink);
}
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ if (!gestureInputEnabledByBuildConfig) {
+ final Preference gestureInputPref = findPreference(PREF_GESTURE_INPUT);
+ miscSettings.removePreference(gestureInputPref);
+ }
final boolean showUsabilityStudyModeOption =
res.getBoolean(R.bool.config_enable_usability_study_mode_option)
|| ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS;
@@ -223,7 +216,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);
}
}
@@ -327,13 +321,15 @@ 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(
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index b07c3e59f..3ed981375 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,31 @@ 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;
+
+ // 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 +126,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 +155,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 +170,27 @@ 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);
+ 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 = new ArrayList<SuggestedWordInfo>();
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 +208,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 +225,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 +290,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 +411,9 @@ public class SettingsValues {
final String newStr = Utils.localeAndTimeHashMapToStr(map);
prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
}
+
+ // 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..6e7d985d6 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -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/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 664de6774..a7a5fcb5f 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -98,9 +98,9 @@ public class SubtypeSwitcher {
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");
}
@@ -113,7 +113,7 @@ public class SubtypeSwitcher {
// Only configuration changed event is allowed to call this because this is heavy.
private void updateAllParameters() {
mCurrentSystemLocale = mResources.getConfiguration().locale;
- updateSubtype(mImm.getCurrentInputMethodSubtype());
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype));
updateParametersOnStartInputView();
}
@@ -142,7 +142,7 @@ public class SubtypeSwitcher {
+ currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
Log.w(TAG, "Last subtype was disabled. Update to the current one.");
}
- updateSubtype(mImm.getCurrentInputMethodSubtype());
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(mService, mNoLanguageSubtype));
}
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 336a76f4b..5e2a04124 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,79 +34,55 @@ 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";
+
+ public interface SuggestInitializationListener {
+ public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
+ }
private static final boolean DBG = LatinImeLogger.sDBG;
- private boolean mHasMainDictionary;
- private Dictionary mContactsDict;
+ private Dictionary mMainDictionary;
+ private ContactsBinaryDictionary mContactsDict;
private WhitelistDictionary mWhiteListDictionary;
- private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
+ private final ConcurrentHashMap<String, Dictionary> mDictionaries =
new ConcurrentHashMap<String, Dictionary>();
- private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
-
- private int mPrefMaxSuggestions = 18;
- 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;
+ // Locale used for upper- and title-casing words
+ private final Locale mLocale;
+ private final SuggestInitializationListener mListener;
- private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
-
- public Suggest(final Context context, final Locale locale) {
+ public Suggest(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
initAsynchronously(context, locale);
+ mLocale = locale;
+ mListener = listener;
}
/* 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);
+ mLocale = locale;
+ mListener = null;
+ mMainDictionary = mainDict;
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
initWhitelistAndAutocorrectAndPool(context, locale);
}
private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
mWhiteListDictionary = new WhitelistDictionary(context, locale);
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_WHITELIST, mWhiteListDictionary);
}
private void initAsynchronously(final Context context, final Locale locale) {
@@ -129,15 +105,20 @@ public class Suggest implements Dictionary.WordCallback {
}
public void resetMainDict(final Context context, final Locale locale) {
- mHasMainDictionary = false;
+ mMainDictionary = null;
+ if (mListener != null) {
+ mListener.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 (mListener != null) {
+ mListener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ }
}
}.start();
}
@@ -145,27 +126,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 +154,190 @@ 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);
- }
-
- private static final WordComposer sEmptyWordComposer = new WordComposer();
- public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) {
+ public SuggestedWords getSuggestedWords(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
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);
+ } 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;
+ }
+
+ for (final String key : mDictionaries.keySet()) {
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestions(
+ wordComposerForLookup, prevWordForBigram, proximityInfo));
}
- final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase,
- mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord));
+ // 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.isWhitelistedOrNotAWord(
+ mDictionaries, consideredWord, wordComposer.isFirstCharCapitalized());
+
+ final CharSequence whitelistedWord =
+ mWhiteListDictionary.getWhitelistedWord(consideredWord);
+ if (whitelistedWord != null) {
+ // MAX_SCORE ensures this will be considered strong enough to be auto-corrected
+ suggestionsSet.add(new SuggestedWordInfo(whitelistedWord,
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST,
+ Dictionary.TYPE_WHITELIST));
+ }
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);
- } else {
+ // 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 =
+ new ArrayList<SuggestedWordInfo>(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) {
+ 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 UserUnigramDictionary and WhitelistDictionary to lookup
+ if (key.equals(Dictionary.TYPE_USER_HISTORY)
+ || key.equals(Dictionary.TYPE_WHITELIST)) {
+ continue;
}
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestions(
+ wordComposer, prevWordForBigram, proximityInfo));
}
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, prevWord, this);
+
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ new ArrayList<SuggestedWordInfo>(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ final boolean isFirstCharCapitalized = wordComposer.isAutoCapitalized();
+ // TODO: Handle the manual temporary shifted mode.
+ // TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
+ final boolean isAllUpperCase = false;
+ 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(
@@ -431,119 +366,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;
- }
-
- 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;
+ 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());
}
-
- 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());
+ 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..88fc006df 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -25,27 +25,27 @@ import java.util.HashSet;
public class SuggestedWords {
public static final SuggestedWords EMPTY = new SuggestedWords(
- new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false);
+ new ArrayList<SuggestedWordInfo>(0), 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 +55,7 @@ public class SuggestedWords {
return mSuggestedWordInfoList.size();
}
- public CharSequence getWord(int pos) {
+ public String getWord(int pos) {
return mSuggestedWordInfoList.get(pos).mWord;
}
@@ -67,12 +67,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,8 +76,7 @@ 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());
}
@@ -91,7 +86,8 @@ public class SuggestedWords {
final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
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;
@@ -103,7 +99,8 @@ public class SuggestedWords {
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));
+ 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 +117,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 +157,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 +179,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..3bb670c9a 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;
@@ -115,8 +118,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
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 +130,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 +160,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.
*/
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
index 28847745e..610652ac1 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
@@ -98,11 +98,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..1de95d7b8 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
+ * NativeUtils.powf(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..8f71de0e7 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -44,10 +44,9 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
-import java.util.Map;
public class Utils {
private Utils() {
@@ -206,18 +205,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);
+ }
+
public static class UsabilityStudyLogUtils {
// TODO: remove code duplication with ResearchLog class
private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
index a0de2f970..14476dcf0 100644
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
@@ -22,8 +22,11 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
@@ -37,7 +40,7 @@ public class WhitelistDictionary extends ExpandableDictionary {
// TODO: Conform to the async load contact of ExpandableDictionary
public WhitelistDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_WHITELIST);
+ super(context, Dictionary.TYPE_WHITELIST);
// TODO: Move whitelist dictionary into main dictionary.
final RunInLocale<Void> job = new RunInLocale<Void>() {
@Override
@@ -88,6 +91,13 @@ public class WhitelistDictionary extends ExpandableDictionary {
return null;
}
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ // Whitelist does not supply any suggestions or predictions.
+ 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
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index ca9caa1d3..5606a58e4 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -19,7 +19,6 @@ 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;
@@ -34,14 +33,15 @@ public class WordComposer {
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
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 int mDigitsCount;
private boolean mAutoCapitalized;
private int mTrailingSingleQuotesCount;
private int mCodePointSize;
@@ -54,28 +54,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;
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
mIsResumed = source.mIsResumed;
+ mIsBatchMode = source.mIsBatchMode;
refreshSize();
}
@@ -86,13 +82,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 +114,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 +123,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 +153,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, NOT_A_COORDINATE, 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, NOT_A_COORDINATE, NOT_A_COORDINATE);
}
/**
@@ -219,6 +217,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.
@@ -274,6 +273,13 @@ public class WordComposer {
}
/**
+ * Returns true if we have digits in the composing word.
+ */
+ public boolean hasDigits() {
+ return mDigitsCount > 0;
+ }
+
+ /**
* 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
@@ -318,19 +324,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 +347,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..2c3eee74c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -787,9 +787,9 @@ public class BinaryDictInputOutput {
// (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);
+ 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
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 88efc5a85..3bdfe1f27 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.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.UserBinaryDictionary;
import com.android.inputmethod.latin.WhitelistDictionary;
-import com.android.inputmethod.latin.WordComposer;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -66,18 +58,18 @@ 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, UserBinaryDictionary> mUserDictionaries =
+ Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>());
private Map<String, Dictionary> mWhitelistDictionaries =
Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- private Dictionary mContactsDictionary;
+ private ContactsBinaryDictionary mContactsDictionary;
// The threshold for a candidate to be offered as a suggestion.
private float mSuggestionThreshold;
@@ -92,8 +84,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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
@@ -130,7 +122,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 +146,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 +184,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;
@@ -242,9 +238,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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.
@@ -368,8 +363,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, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries;
+ mUserDictionaries =
+ Collections.synchronizedMap(new TreeMap<String, UserBinaryDictionary>());
final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries;
mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
new Thread("spellchecker_close_dicts") {
@@ -389,7 +385,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
// 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 +396,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,13 +417,9 @@ 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);
@@ -440,17 +432,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService
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 +447,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 +464,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..501a0e221
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -0,0 +1,152 @@
+/*
+ * 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 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 = new ArrayList<Integer>();
+ final ArrayList<Integer> additionalLengths = new ArrayList<Integer>();
+ final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
+ new ArrayList<SuggestionsInfo>();
+ 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..0171dc06d
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -0,0 +1,302 @@
+/*
+ * 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.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.takeOrGetNull();
+ if (null == 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, WordComposer.NOT_A_COORDINATE,
+ WordComposer.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.takeOrGetNull();
+ if (null == 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/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..b57ffd2de 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -57,21 +57,21 @@ 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.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 boolean addWordToUserDictionary(String word);
public void pickSuggestionManually(int index, CharSequence word, int x, int y);
}
@@ -98,24 +98,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 +129,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;
@@ -175,7 +175,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 +191,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 +337,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 +597,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 +632,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 +679,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,10 +720,6 @@ 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
@@ -763,7 +761,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 +861,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;
}
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..c9f3b476a
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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.text.Editable;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+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);
+ 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/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
new file mode 100644
index 000000000..18bf3c07f
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -0,0 +1,375 @@
+/*
+ * 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 com.android.inputmethod.research.ResearchLogger.LogUnit;
+
+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.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 JsonWriter NULL_JSON_WRITER = new JsonWriter(
+ new OutputStreamWriter(new NullOutputStream()));
+
+ final ScheduledExecutorService mExecutor;
+ /* package */ final File mFile;
+ private JsonWriter mJsonWriter = NULL_JSON_WRITER;
+
+ private int mLoggingState;
+ private static final int LOGGING_STATE_UNSTARTED = 0;
+ private static final int LOGGING_STATE_READY = 1; // don't create file until necessary
+ private static final int LOGGING_STATE_RUNNING = 2;
+ private static final int LOGGING_STATE_STOPPING = 3;
+ private static final int LOGGING_STATE_STOPPED = 4;
+ private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
+
+ 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(File outputFile) {
+ mExecutor = Executors.newSingleThreadScheduledExecutor();
+ if (outputFile == null) {
+ throw new IllegalArgumentException();
+ }
+ mFile = outputFile;
+ mLoggingState = LOGGING_STATE_UNSTARTED;
+ }
+
+ public synchronized void start() throws IOException {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ mLoggingState = LOGGING_STATE_READY;
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ break;
+ }
+ }
+
+ public synchronized void stop() {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ mLoggingState = LOGGING_STATE_STOPPED;
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ mJsonWriter.endArray();
+ mJsonWriter.flush();
+ mJsonWriter.close();
+ } finally {
+ boolean success = mFile.setWritable(false, false);
+ mLoggingState = LOGGING_STATE_STOPPED;
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ mLoggingState = LOGGING_STATE_STOPPING;
+ break;
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ }
+ }
+
+ public boolean isAlive() {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ return true;
+ }
+ return false;
+ }
+
+ public void waitUntilStopped(final int timeoutInMs) throws InterruptedException {
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ mExecutor.awaitTermination(timeoutInMs, TimeUnit.MILLISECONDS);
+ }
+
+ public synchronized void abort() {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ mLoggingState = LOGGING_STATE_STOPPED;
+ isAbortSuccessful = true;
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ mJsonWriter.endArray();
+ mJsonWriter.close();
+ } finally {
+ isAbortSuccessful = mFile.delete();
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ mLoggingState = LOGGING_STATE_STOPPING;
+ break;
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ }
+ }
+
+ private boolean isAbortSuccessful;
+ public boolean isAbortSuccessful() {
+ return isAbortSuccessful;
+ }
+
+ /* package */ synchronized void flush() {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ removeAnyScheduledFlush();
+ mExecutor.submit(mFlushCallable);
+ break;
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ }
+ }
+
+ private Callable<Object> mFlushCallable = new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ if (mLoggingState == LOGGING_STATE_RUNNING) {
+ 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 publishPublicEvents(final LogUnit logUnit) {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ logUnit.publishPublicEventsTo(ResearchLog.this);
+ scheduleFlush();
+ return null;
+ }
+ });
+ break;
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ }
+ }
+
+ public synchronized void publishAllEvents(final LogUnit logUnit) {
+ switch (mLoggingState) {
+ case LOGGING_STATE_UNSTARTED:
+ break;
+ case LOGGING_STATE_READY:
+ case LOGGING_STATE_RUNNING:
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ logUnit.publishAllEventsTo(ResearchLog.this);
+ scheduleFlush();
+ return null;
+ }
+ });
+ break;
+ case LOGGING_STATE_STOPPING:
+ case LOGGING_STATE_STOPPED:
+ }
+ }
+
+ 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.
+ try {
+ if (mJsonWriter == NULL_JSON_WRITER) {
+ mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
+ mJsonWriter.setLenient(true);
+ mJsonWriter.beginArray();
+ }
+ 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 String) {
+ mJsonWriter.value((String) value);
+ } 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/ResearchLogUploader.java b/java/src/com/android/inputmethod/research/ResearchLogUploader.java
new file mode 100644
index 000000000..3b1213009
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLogUploader.java
@@ -0,0 +1,223 @@
+/*
+ * 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.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.util.Log;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.R.string;
+
+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;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public final class ResearchLogUploader {
+ private static final String TAG = ResearchLogUploader.class.getSimpleName();
+ private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min
+ private static final int BUF_SIZE = 1024 * 8;
+
+ private final boolean mCanUpload;
+ private final Context mContext;
+ private final File mFilesDir;
+ private final URL mUrl;
+ private final ScheduledExecutorService mExecutor;
+
+ private Runnable doUploadRunnable = new UploadRunnable(null, false);
+
+ public ResearchLogUploader(final Context context, final File filesDir) {
+ mContext = context;
+ mFilesDir = filesDir;
+ final PackageManager packageManager = context.getPackageManager();
+ final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
+ context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ if (!hasPermission) {
+ mCanUpload = false;
+ mUrl = null;
+ mExecutor = null;
+ return;
+ }
+ URL tempUrl = null;
+ boolean canUpload = false;
+ ScheduledExecutorService executor = null;
+ try {
+ final String urlString = context.getString(R.string.research_logger_upload_url);
+ if (urlString == null || urlString.equals("")) {
+ return;
+ }
+ tempUrl = new URL(urlString);
+ canUpload = true;
+ executor = Executors.newSingleThreadScheduledExecutor();
+ } catch (MalformedURLException e) {
+ tempUrl = null;
+ e.printStackTrace();
+ return;
+ } finally {
+ mCanUpload = canUpload;
+ mUrl = tempUrl;
+ mExecutor = executor;
+ }
+ }
+
+ public void start() {
+ if (mCanUpload) {
+ Log.d(TAG, "scheduling regular uploading");
+ mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS,
+ UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS);
+ } else {
+ Log.d(TAG, "no permission to upload");
+ }
+ }
+
+ public void uploadNow(final Callback callback) {
+ // Perform an immediate upload. Note that this should happen even if there is
+ // another upload happening right now, as it may have missed the latest changes.
+ // TODO: Reschedule regular upload tests starting from now.
+ if (mCanUpload) {
+ mExecutor.submit(new UploadRunnable(callback, true));
+ }
+ }
+
+ public interface Callback {
+ public void onUploadCompleted(final boolean success);
+ }
+
+ private boolean isExternallyPowered() {
+ final Intent intent = mContext.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) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return wifiInfo.isConnected();
+ }
+
+ class UploadRunnable implements Runnable {
+ private final Callback mCallback;
+ private final boolean mForceUpload;
+
+ public UploadRunnable(final Callback callback, final boolean forceUpload) {
+ mCallback = callback;
+ mForceUpload = forceUpload;
+ }
+
+ @Override
+ public void run() {
+ doUpload();
+ }
+
+ private void doUpload() {
+ if (!mForceUpload && (!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;
+ }
+ }
+ if (mCallback != null) {
+ mCallback.onUploadCompleted(success);
+ }
+ }
+
+ 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 fileIs = null;
+ try {
+ fileIs = 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 = fileIs.read(buf)) != -1) {
+ os.write(buf, 0, numBytesRead);
+ }
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ Log.d(TAG, "upload failed: " + connection.getResponseCode());
+ InputStream netIs = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(netIs));
+ 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 (fileIs != null) {
+ try {
+ fileIs.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return success;
+ }
+ }
+}
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..68bd98a23
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -0,0 +1,1280 @@
+/*
+ * 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.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+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.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+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.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.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+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.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+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;
+
+ // 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 int ABORT_TIMEOUT_IN_MS = 10 * 1000; // timeout to notify user
+
+ 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;
+ // The mIntentionalResearchLog 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.
+ /* package */ ResearchLog mIntentionalResearchLog;
+ // LogUnits are queued here and released only when the user requests the intentional log.
+ private List<LogUnit> mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+
+ 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 KeyboardSwitcher mKeyboardSwitcher;
+ private InputMethodService mInputMethodService;
+
+ private ResearchLogUploader mResearchLogUploader;
+
+ private ResearchLogger() {
+ }
+
+ public static ResearchLogger getInstance() {
+ return sInstance;
+ }
+
+ public void init(final InputMethodService ims, final SharedPreferences prefs,
+ KeyboardSwitcher keyboardSwitcher) {
+ 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();
+ }
+ }
+ mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir);
+ mResearchLogUploader.start();
+ mKeyboardSwitcher = keyboardSwitcher;
+ mInputMethodService = ims;
+ mPrefs = prefs;
+ }
+
+ 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() {
+ maybeShowSplashScreen();
+ }
+
+ 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 = mKeyboardSwitcher.getKeyboardView().getWindowToken();
+ 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();
+ }
+
+ 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 start() {
+ maybeShowSplashScreen();
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ 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;
+ }
+ try {
+ if (mMainResearchLog == null || !mMainResearchLog.isAlive()) {
+ mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ }
+ mMainResearchLog.start();
+ if (mIntentionalResearchLog == null || !mIntentionalResearchLog.isAlive()) {
+ mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ }
+ mIntentionalResearchLog.start();
+ } catch (IOException e) {
+ Log.w(TAG, "Could not start ResearchLogger.");
+ }
+ }
+
+ /* package */ void stop() {
+ if (mMainResearchLog != null) {
+ mMainResearchLog.stop();
+ }
+ if (mIntentionalResearchLog != null) {
+ mIntentionalResearchLog.stop();
+ }
+ }
+
+ private void setLoggingAllowed(boolean enableLogging) {
+ if (mPrefs == null) {
+ return;
+ }
+ Editor e = mPrefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
+ e.apply();
+ sIsLogging = enableLogging;
+ }
+
+ public boolean abort() {
+ boolean didAbortMainLog = false;
+ if (mMainResearchLog != null) {
+ mMainResearchLog.abort();
+ try {
+ mMainResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS);
+ } catch (InterruptedException e) {
+ // interrupted early. carry on.
+ }
+ if (mMainResearchLog.isAbortSuccessful()) {
+ didAbortMainLog = true;
+ }
+ mMainResearchLog = null;
+ }
+ boolean didAbortIntentionalLog = false;
+ if (mIntentionalResearchLog != null) {
+ mIntentionalResearchLog.abort();
+ try {
+ mIntentionalResearchLog.waitUntilStopped(ABORT_TIMEOUT_IN_MS);
+ } catch (InterruptedException e) {
+ // interrupted early. carry on.
+ }
+ if (mIntentionalResearchLog.isAbortSuccessful()) {
+ didAbortIntentionalLog = true;
+ }
+ mIntentionalResearchLog = null;
+ }
+ return didAbortMainLog && didAbortIntentionalLog;
+ }
+
+ /* package */ void flush() {
+ if (mMainResearchLog != null) {
+ mMainResearchLog.flush();
+ }
+ }
+
+ 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();
+ }
+
+ 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 ResearchLog mFeedbackLog;
+ private List<LogUnit> mFeedbackQueue;
+ private ResearchLog mSavedMainResearchLog;
+ private ResearchLog mSavedIntentionalResearchLog;
+ private List<LogUnit> mSavedIntentionalResearchLogQueue;
+
+ private void saveLogsForFeedback() {
+ mFeedbackLog = mIntentionalResearchLog;
+ if (mIntentionalResearchLogQueue != null) {
+ mFeedbackQueue = new ArrayList<LogUnit>(mIntentionalResearchLogQueue);
+ } else {
+ mFeedbackQueue = null;
+ }
+ mSavedMainResearchLog = mMainResearchLog;
+ mSavedIntentionalResearchLog = mIntentionalResearchLog;
+ mSavedIntentionalResearchLogQueue = mIntentionalResearchLogQueue;
+
+ mMainResearchLog = null;
+ mIntentionalResearchLog = null;
+ mIntentionalResearchLogQueue = new ArrayList<LogUnit>();
+ }
+
+ private static final int LOG_DRAIN_TIMEOUT_IN_MS = 1000 * 5;
+ public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+ if (includeHistory && mFeedbackLog != null) {
+ try {
+ LogUnit headerLogUnit = new LogUnit();
+ headerLogUnit.addLogAtom(EVENTKEYS_INTENTIONAL_LOG, EVENTKEYS_NULLVALUES, false);
+ mFeedbackLog.publishAllEvents(headerLogUnit);
+ for (LogUnit logUnit : mFeedbackQueue) {
+ mFeedbackLog.publishAllEvents(logUnit);
+ }
+ userFeedback(mFeedbackLog, feedbackContents);
+ mFeedbackLog.stop();
+ try {
+ mFeedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mIntentionalResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ mIntentionalResearchLog.start();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ mIntentionalResearchLogQueue.clear();
+ }
+ mResearchLogUploader.uploadNow(null);
+ } else {
+ // create a separate ResearchLog just for feedback
+ final ResearchLog feedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ try {
+ feedbackLog.start();
+ userFeedback(feedbackLog, feedbackContents);
+ feedbackLog.stop();
+ feedbackLog.waitUntilStopped(LOG_DRAIN_TIMEOUT_IN_MS);
+ mResearchLogUploader.uploadNow(null);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void onLeavingSendFeedbackDialog() {
+ mInFeedbackDialog = false;
+ mMainResearchLog = mSavedMainResearchLog;
+ mIntentionalResearchLog = mSavedIntentionalResearchLog;
+ mIntentionalResearchLogQueue = mSavedIntentionalResearchLogQueue;
+ }
+
+ public void initSuggest(Suggest suggest) {
+ mSuggest = suggest;
+ }
+
+ private void setIsPasswordView(boolean isPasswordView) {
+ mIsPasswordView = isPasswordView;
+ }
+
+ private boolean isAllowedToLog() {
+ return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging;
+ }
+
+ public void requestIndicatorRedraw() {
+ if (!IS_SHOWING_INDICATOR) {
+ return;
+ }
+ if (mKeyboardSwitcher == null) {
+ return;
+ }
+ final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
+ if (keyboardView == null) {
+ return;
+ }
+ keyboardView.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 String CURRENT_TIME_KEY = "_ct";
+ private static final String UPTIME_KEY = "_ut";
+ private static final String EVENT_TYPE_KEY = "_ty";
+ private static final Object[] EVENTKEYS_NULLVALUES = {};
+
+ private LogUnit mCurrentLogUnit = new LogUnit();
+
+ /**
+ * 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.addLogAtom(keys, values, true);
+ }
+ }
+
+ /**
+ * 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.addLogAtom(keys, values, false);
+ }
+ }
+
+ // Used to track how often words are logged. Too-frequent logging can leak
+ // semantics, disclosing private data.
+ /* package for test */ static class LoggingFrequencyState {
+ private static final int DEFAULT_WORD_LOG_FREQUENCY = 10;
+ private int mWordsRemainingToSkip;
+ private final int mFrequency;
+
+ /**
+ * Tracks how often words may be uploaded.
+ *
+ * @param frequency 1=Every word, 2=Every other word, etc.
+ */
+ public LoggingFrequencyState(int frequency) {
+ mFrequency = frequency;
+ mWordsRemainingToSkip = mFrequency;
+ }
+
+ public void onWordLogged() {
+ mWordsRemainingToSkip = mFrequency;
+ }
+
+ public void onWordNotLogged() {
+ if (mWordsRemainingToSkip > 1) {
+ mWordsRemainingToSkip--;
+ }
+ }
+
+ public boolean isSafeToLog() {
+ return mWordsRemainingToSkip <= 1;
+ }
+ }
+
+ /* package for test */ LoggingFrequencyState mLoggingFrequencyState =
+ new LoggingFrequencyState(LoggingFrequencyState.DEFAULT_WORD_LOG_FREQUENCY);
+
+ /* package for test */ boolean isPrivacyThreat(String word) {
+ // Current checks:
+ // - Word not in dictionary
+ // - Word contains numbers
+ // - Privacy-safe word not logged recently
+ if (TextUtils.isEmpty(word)) {
+ return false;
+ }
+ if (!mLoggingFrequencyState.isSafeToLog()) {
+ return true;
+ }
+ final int length = word.length();
+ boolean hasLetter = false;
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = Character.codePointAt(word, i);
+ if (Character.isDigit(codePoint)) {
+ return true;
+ }
+ if (Character.isLetter(codePoint)) {
+ hasLetter = true;
+ break; // Word may contain digits, but will only be allowed if in the dictionary.
+ }
+ }
+ if (hasLetter) {
+ if (mDictionary == null && mSuggest != null && mSuggest.hasMainDictionary()) {
+ mDictionary = mSuggest.getMainDictionary();
+ }
+ if (mDictionary == null) {
+ // Can't access dictionary. Assume privacy threat.
+ return true;
+ }
+ return !(mDictionary.isValidWord(word));
+ }
+ // No letters, no numbers. Punctuation, space, or something else.
+ return false;
+ }
+
+ private void onWordComplete(String word) {
+ if (isPrivacyThreat(word)) {
+ publishLogUnit(mCurrentLogUnit, true);
+ mLoggingFrequencyState.onWordNotLogged();
+ } else {
+ publishLogUnit(mCurrentLogUnit, false);
+ mLoggingFrequencyState.onWordLogged();
+ }
+ mCurrentLogUnit = new LogUnit();
+ }
+
+ private void publishLogUnit(LogUnit logUnit, boolean isPrivacySensitive) {
+ if (!isAllowedToLog()) {
+ return;
+ }
+ if (mMainResearchLog == null) {
+ return;
+ }
+ if (isPrivacySensitive) {
+ mMainResearchLog.publishPublicEvents(logUnit);
+ } else {
+ mMainResearchLog.publishAllEvents(logUnit);
+ }
+ mIntentionalResearchLogQueue.add(logUnit);
+ }
+
+ /* package */ void publishCurrentLogUnit(ResearchLog researchLog, boolean isPrivacySensitive) {
+ publishLogUnit(mCurrentLogUnit, isPrivacySensitive);
+ }
+
+ static class LogUnit {
+ private final List<String[]> mKeysList = new ArrayList<String[]>();
+ private final List<Object[]> mValuesList = new ArrayList<Object[]>();
+ private final List<Boolean> mIsPotentiallyPrivate = new ArrayList<Boolean>();
+
+ private void addLogAtom(final String[] keys, final Object[] values,
+ final Boolean isPotentiallyPrivate) {
+ mKeysList.add(keys);
+ mValuesList.add(values);
+ mIsPotentiallyPrivate.add(isPotentiallyPrivate);
+ }
+
+ public void publishPublicEventsTo(ResearchLog researchLog) {
+ final int size = mKeysList.size();
+ for (int i = 0; i < size; i++) {
+ if (!mIsPotentiallyPrivate.get(i)) {
+ researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
+ }
+ }
+ }
+
+ public void publishAllEventsTo(ResearchLog researchLog) {
+ final int size = mKeysList.size();
+ for (int i = 0; i < size; i++) {
+ researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
+ }
+ }
+ }
+
+ 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;
+ }
+
+ // Special methods related to startup, shutdown, logging itself
+
+ private static final String[] EVENTKEYS_INTENTIONAL_LOG = {
+ "IntentionalLog"
+ };
+
+ 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();
+ if (researchLogger.mInFeedbackDialog) {
+ researchLogger.saveLogsForFeedback();
+ }
+ 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_LATINIME_COMMITTEXT = {
+ "LatinIMECommitText", "typedWord"
+ };
+
+ public static void latinIME_commitText(final CharSequence typedWord) {
+ final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
+ final Object[] values = {
+ scrubbedWord
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_COMMITTEXT, values);
+ researchLogger.onWordComplete(scrubbedWord);
+ }
+
+ private static final String[] EVENTKEYS_USER_FEEDBACK = {
+ "UserFeedback", "FeedbackContents"
+ };
+
+ private void userFeedback(ResearchLog researchLog, String feedbackContents) {
+ // this method is special; it directs the feedbackContents to a particular researchLog
+ final LogUnit logUnit = new LogUnit();
+ final Object[] values = {
+ feedbackContents
+ };
+ logUnit.addLogAtom(EVENTKEYS_USER_FEEDBACK, values, false);
+ researchLog.publishAllEvents(logUnit);
+ }
+
+ // 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 Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
+ }
+
+ private static final String[] EVENTKEYS_CORRECTION = {
+ "LogCorrection", "subgroup", "before", "after", "position"
+ };
+ public static void logCorrection(final String subgroup, final String before, final String after,
+ final int position) {
+ final Object[] values = {
+ subgroup, scrubDigitsFromString(before), scrubDigitsFromString(after), position
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_CORRECTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION = {
+ "LatinIMECommitCurrentAutoCorrection", "typedWord", "autoCorrection"
+ };
+ public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
+ final String autoCorrection) {
+ final Object[] values = {
+ scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection)
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_LATINIME_COMMITCURRENTAUTOCORRECTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT = {
+ "LatinIMEDeleteSurroundingText", "length"
+ };
+ public static void latinIME_deleteSurroundingText(final int length) {
+ final Object[] values = {
+ length
+ };
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_DELETESURROUNDINGTEXT, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD = {
+ "LatinIMEDoubleSpaceAutoPeriod"
+ };
+ public static void latinIME_doubleSpaceAutoPeriod() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_DOUBLESPACEAUTOPERIOD, EVENTKEYS_NULLVALUES);
+ }
+
+ 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) {
+ 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);
+ // Play it safe. Remove privacy-sensitive events.
+ researchLogger.publishLogUnit(researchLogger.mCurrentLogUnit, true);
+ researchLogger.mCurrentLogUnit = new LogUnit();
+ 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_PERFORMEDITORACTION = {
+ "LatinIMEPerformEditorAction", "imeActionNext"
+ };
+ public static void latinIME_performEditorAction(final int imeActionNext) {
+ final Object[] values = {
+ imeActionNext
+ };
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_PERFORMEDITORACTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION = {
+ "LatinIMEPickApplicationSpecifiedCompletion", "index", "text", "x", "y"
+ };
+ public static void latinIME_pickApplicationSpecifiedCompletion(final int index,
+ final CharSequence cs, int x, int y) {
+ final Object[] values = {
+ index, cs, x, y
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION, 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, int x, int y) {
+ final Object[] values = {
+ scrubDigitsFromString(replacedWord), index, suggestion == null ? null :
+ scrubDigitsFromString(suggestion.toString()), x, y
+ };
+ 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, int x, int y) {
+ final Object[] values = {
+ index, suggestion, x, y
+ };
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT = {
+ "LatinIMERevertDoubleSpaceWhileInBatchEdit"
+ };
+ public static void latinIME_revertDoubleSpaceWhileInBatchEdit() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT,
+ EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION = {
+ "LatinIMERevertSwapPunctuation"
+ };
+ public static void latinIME_revertSwapPunctuation() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_REVERTSWAPPUNCTUATION, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = {
+ "LatinIMESendKeyCodePoint", "code"
+ };
+ public static void latinIME_sendKeyCodePoint(final int code) {
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code))
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT = {
+ "LatinIMESwapSwapperAndSpaceWhileInBatchEdit"
+ };
+ public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT,
+ 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_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);
+ }
+}