aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java6
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java33
-rw-r--r--java/src/com/android/inputmethod/annotations/ExternallyReferenced.java24
-rw-r--r--java/src/com/android/inputmethod/annotations/UsedForTesting.java24
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java48
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java23
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java11
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java73
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java20
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java31
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java29
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java288
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java37
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java515
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java128
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java127
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java36
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java13
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java35
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java666
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java69
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java103
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java (renamed from java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java)20
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java24
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java29
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java23
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java75
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java8
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java17
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java72
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java19
-rw-r--r--java/src/com/android/inputmethod/latin/CoordinateUtils.java49
-rw-r--r--java/src/com/android/inputmethod/latin/DebugSettings.java18
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java15
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java6
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java14
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java81
-rw-r--r--java/src/com/android/inputmethod/latin/ImfUtils.java186
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java80
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java8
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java4
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java338
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java45
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodManager.java248
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java5
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java19
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java119
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java25
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java43
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java46
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java29
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionary.java32
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java15
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java25
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java728
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java205
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java18
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java74
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java26
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java43
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java3
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java11
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java33
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java166
-rw-r--r--java/src/com/android/inputmethod/research/MainLogBuffer.java11
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java89
-rw-r--r--java/src/com/android/inputmethod/research/Statistics.java12
82 files changed, 3457 insertions, 2150 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index b9b6362fc..0ab84f7b5 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -36,6 +36,7 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.CoordinateUtils;
/**
* Exposes a virtual view sub-tree for {@link KeyboardView} and generates
@@ -62,7 +63,7 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
private final Rect mTempBoundsInScreen = new Rect();
/** The parent view's cached on-screen location. */
- private final int[] mParentLocation = new int[2];
+ private final int[] mParentLocation = CoordinateUtils.newInstance();
/** The virtual view identifier for the focused node. */
private int mAccessibilityFocusedView = UNDEFINED;
@@ -180,7 +181,8 @@ public final class AccessibilityEntityProvider extends AccessibilityNodeProvider
// Calculate the key's in-screen bounds.
mTempBoundsInScreen.set(boundsInParent);
- mTempBoundsInScreen.offset(mParentLocation[0], mParentLocation[1]);
+ mTempBoundsInScreen.offset(
+ CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation));
final Rect boundsInScreen = mTempBoundsInScreen;
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 32618ad85..b87ae3a15 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -26,6 +26,7 @@ import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
@@ -61,17 +62,19 @@ public final class KeyCodeDescriptionMapper {
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
// 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);
- mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift);
- 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);
+ mKeyCodeMap.put(Constants.CODE_SPACE, R.string.spoken_description_space);
+ mKeyCodeMap.put(Constants.CODE_DELETE, R.string.spoken_description_delete);
+ mKeyCodeMap.put(Constants.CODE_ENTER, R.string.spoken_description_return);
+ mKeyCodeMap.put(Constants.CODE_SETTINGS, R.string.spoken_description_settings);
+ mKeyCodeMap.put(Constants.CODE_SHIFT, R.string.spoken_description_shift);
+ mKeyCodeMap.put(Constants.CODE_SHORTCUT, R.string.spoken_description_mic);
+ mKeyCodeMap.put(Constants.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
+ mKeyCodeMap.put(Constants.CODE_TAB, R.string.spoken_description_tab);
+ mKeyCodeMap.put(Constants.CODE_LANGUAGE_SWITCH,
+ R.string.spoken_description_language_switch);
+ mKeyCodeMap.put(Constants.CODE_ACTION_NEXT, R.string.spoken_description_action_next);
+ mKeyCodeMap.put(Constants.CODE_ACTION_PREVIOUS,
+ R.string.spoken_description_action_previous);
}
/**
@@ -97,17 +100,17 @@ public final class KeyCodeDescriptionMapper {
boolean shouldObscure) {
final int code = key.mCode;
- if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
if (description != null)
return description;
}
- if (code == Keyboard.CODE_SHIFT) {
+ if (code == Constants.CODE_SHIFT) {
return getDescriptionForShiftKey(context, keyboard);
}
- if (code == Keyboard.CODE_ACTION_ENTER) {
+ if (code == Constants.CODE_ACTION_ENTER) {
return getDescriptionForActionKey(context, keyboard, key);
}
@@ -121,7 +124,7 @@ public final class KeyCodeDescriptionMapper {
}
// Just attempt to speak the description.
- if (key.mCode != Keyboard.CODE_UNSPECIFIED) {
+ if (key.mCode != Constants.CODE_UNSPECIFIED) {
return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
}
diff --git a/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java
new file mode 100644
index 000000000..ea5f12ce2
--- /dev/null
+++ b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java
@@ -0,0 +1,24 @@
+/*
+ * 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.annotations;
+
+/**
+ * Denotes that the class, method or field should not be eliminated by ProGuard,
+ * because it is externally referenced. (See proguard.flags)
+ */
+public @interface ExternallyReferenced {
+}
diff --git a/java/src/com/android/inputmethod/annotations/UsedForTesting.java b/java/src/com/android/inputmethod/annotations/UsedForTesting.java
new file mode 100644
index 000000000..2ada091e4
--- /dev/null
+++ b/java/src/com/android/inputmethod/annotations/UsedForTesting.java
@@ -0,0 +1,24 @@
+/*
+ * 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.annotations;
+
+/**
+ * Denotes that the class, method or field should not be eliminated by ProGuard,
+ * so that unit tests can access it. (See proguard.flags)
+ */
+public @interface UsedForTesting {
+}
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index ab7bd4914..8bd1e5208 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -19,61 +19,21 @@ package com.android.inputmethod.compat;
import android.content.Context;
import android.os.IBinder;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import com.android.inputmethod.latin.ImfUtils;
import java.lang.reflect.Method;
-// TODO: Override this class with the concrete implementation if we need to take care of the
-// performance.
public final class InputMethodManagerCompatWrapper {
- private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE);
- private static final InputMethodManagerCompatWrapper sInstance =
- new InputMethodManagerCompatWrapper();
-
- private InputMethodManager mImm;
-
- private InputMethodManagerCompatWrapper() {
- // This wrapper class is not publicly instantiable.
- }
-
- public static InputMethodManagerCompatWrapper getInstance() {
- if (sInstance.mImm == null) {
- throw new RuntimeException(TAG + ".getInstance() is called before initialization");
- }
- return sInstance;
- }
-
- public static void init(Context context) {
- sInstance.mImm = ImfUtils.getInputMethodManager(context);
- }
-
- public InputMethodSubtype getCurrentInputMethodSubtype() {
- return mImm.getCurrentInputMethodSubtype();
- }
-
- public InputMethodSubtype getLastInputMethodSubtype() {
- return mImm.getLastInputMethodSubtype();
- }
+ public final InputMethodManager mImm;
- public boolean switchToLastInputMethod(IBinder token) {
- return mImm.switchToLastInputMethod(token);
+ public InputMethodManagerCompatWrapper(final Context context) {
+ mImm = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
}
- public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) {
+ public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToNextInputMethod, token,
onlyCurrentIme);
}
-
- public void setInputMethodAndSubtype(IBinder token, String id, InputMethodSubtype subtype) {
- mImm.setInputMethodAndSubtype(token, id, subtype);
- }
-
- public void showInputMethodPicker() {
- mImm.showInputMethodPicker();
- }
}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 159f43650..c1609ea28 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -83,14 +83,13 @@ public final class SuggestionSpanUtils {
}
public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
- Context context, CharSequence text) {
+ final Context context, final String text) {
if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null
|| OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTIONS_MAX_SIZE == null
|| OBJ_FLAG_MISSPELLED == null || OBJ_FLAG_EASY_CORRECT == null) {
return text;
}
- final Spannable spannable = text instanceof Spannable
- ? (Spannable) text : new SpannableString(text);
+ final Spannable spannable = new SpannableString(text);
final Object[] args =
{ context, null, new String[] {}, (int)OBJ_FLAG_AUTO_CORRECTION,
(Class<?>) SuggestionSpanPickedNotificationReceiver.class };
@@ -104,28 +103,24 @@ public final class SuggestionSpanUtils {
return spannable;
}
- public static CharSequence getTextWithSuggestionSpan(Context context,
- CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) {
+ public static CharSequence getTextWithSuggestionSpan(final Context context,
+ final String pickedWord, final SuggestedWords suggestedWords,
+ final boolean dictionaryAvailable) {
if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord)
|| CONSTRUCTOR_SuggestionSpan == null
- || suggestedWords == null || suggestedWords.size() == 0
- || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions
+ || suggestedWords.isEmpty() || suggestedWords.mIsPrediction
+ || suggestedWords.mIsPunctuationSuggestions
|| OBJ_SUGGESTIONS_MAX_SIZE == null) {
return pickedWord;
}
- final Spannable spannable;
- if (pickedWord instanceof Spannable) {
- spannable = (Spannable) pickedWord;
- } else {
- spannable = new SpannableString(pickedWord);
- }
+ final Spannable spannable = new SpannableString(pickedWord);
final ArrayList<String> suggestionsList = CollectionUtils.newArrayList();
for (int i = 0; i < suggestedWords.size(); ++i) {
if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) {
break;
}
- final CharSequence word = suggestedWords.getWord(i);
+ final String word = suggestedWords.getWord(i);
if (!TextUtils.equals(pickedWord, word)) {
suggestionsList.add(word.toString());
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 30812e8c3..7346a9c38 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -16,11 +16,11 @@
package com.android.inputmethod.keyboard;
-import static com.android.inputmethod.keyboard.Keyboard.CODE_OUTPUT_TEXT;
-import static com.android.inputmethod.keyboard.Keyboard.CODE_SHIFT;
-import static com.android.inputmethod.keyboard.Keyboard.CODE_SWITCH_ALPHA_SYMBOL;
-import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED;
import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
+import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
+import static com.android.inputmethod.latin.Constants.CODE_SHIFT;
+import static com.android.inputmethod.latin.Constants.CODE_SWITCH_ALPHA_SYMBOL;
+import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -39,6 +39,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.keyboard.internal.KeyboardRow;
import com.android.inputmethod.keyboard.internal.MoreKeySpec;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.StringUtils;
@@ -453,7 +454,7 @@ public class Key implements Comparable<Key> {
label = "/" + mLabel;
}
return String.format("%s%s %d,%d %dx%d %s/%s/%s",
- Keyboard.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel,
+ Constants.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel,
KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType));
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index aa683c1d7..0a91284d0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -118,7 +118,7 @@ public class KeyDetector {
}
public static String printableCode(Key key) {
- return key != null ? Keyboard.printableCode(key.mCode) : "none";
+ return key != null ? Constants.printableCode(key.mCode) : "none";
}
public static String printableCodes(int[] codes) {
@@ -127,7 +127,7 @@ public class KeyDetector {
for (final int code : codes) {
if (code == Constants.NOT_A_CODE) break;
if (addDelimiter) sb.append(", ");
- sb.append(Keyboard.printableCode(code));
+ sb.append(Constants.printableCode(code));
addDelimiter = true;
}
return "[" + sb + "]";
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index b7c7f415d..a1b1f5dad 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,15 +16,13 @@
package com.android.inputmethod.keyboard;
-import android.util.Log;
import android.util.SparseArray;
import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.CollectionUtils;
-
-
+import com.android.inputmethod.latin.Constants;
/**
* Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
@@ -45,46 +43,6 @@ import com.android.inputmethod.latin.CollectionUtils;
* </pre>
*/
public class Keyboard {
- private static final String TAG = Keyboard.class.getSimpleName();
-
- /** Some common keys code. Must be positive.
- * These should be aligned with values/keycodes.xml
- */
- public static final int CODE_ENTER = '\n';
- public static final int CODE_TAB = '\t';
- public static final int CODE_SPACE = ' ';
- public static final int CODE_PERIOD = '.';
- public static final int CODE_DASH = '-';
- public static final int CODE_SINGLE_QUOTE = '\'';
- public static final int CODE_DOUBLE_QUOTE = '"';
- public static final int CODE_QUESTION_MARK = '?';
- public static final int CODE_EXCLAMATION_MARK = '!';
- // TODO: Check how this should work for right-to-left languages. It seems to stand
- // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
- // managed by the font? Or is it a different char?
- public static final int CODE_CLOSING_PARENTHESIS = ')';
- public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
- public static final int CODE_CLOSING_CURLY_BRACKET = '}';
- public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
-
- /** Special keys code. Must be negative.
- * 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;
- public static final int CODE_OUTPUT_TEXT = -3;
- public static final int CODE_DELETE = -4;
- public static final int CODE_SETTINGS = -5;
- public static final int CODE_SHORTCUT = -6;
- public static final int CODE_ACTION_ENTER = -7;
- 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 = -12;
-
public final KeyboardId mId;
public final int mThemeId;
@@ -163,7 +121,7 @@ public class Keyboard {
}
public Key getKey(final int code) {
- if (code == CODE_UNSPECIFIED) {
+ if (code == Constants.CODE_UNSPECIFIED) {
return null;
}
synchronized (mKeyCache) {
@@ -197,10 +155,6 @@ public class Keyboard {
return false;
}
- public static boolean isLetterCode(final int code) {
- return code >= CODE_SPACE;
- }
-
@Override
public String toString() {
return mId.toString();
@@ -219,27 +173,4 @@ public class Keyboard {
final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
}
-
- public static String printableCode(final int code) {
- switch (code) {
- case CODE_SHIFT: return "shift";
- case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
- case CODE_OUTPUT_TEXT: return "text";
- case CODE_DELETE: return "delete";
- case CODE_SETTINGS: return "settings";
- case CODE_SHORTCUT: return "shortcut";
- case CODE_ACTION_ENTER: return "actionEnter";
- case CODE_ACTION_NEXT: return "actionNext";
- case CODE_ACTION_PREVIOUS: return "actionPrevious";
- case CODE_LANGUAGE_SWITCH: return "languageSwitch";
- case CODE_UNSPECIFIED: return "unspec";
- case CODE_TAB: return "tab";
- case CODE_ENTER: return "enter";
- default:
- if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
- if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
- if (code < 0x100) return String.format("'%c'", code);
- return String.format("'\\u%04x'", code);
- }
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 5c8f78f5e..14da9ebe6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -56,11 +56,11 @@ public interface KeyboardActionListener {
public void onCodeInput(int primaryCode, int x, int y);
/**
- * Sends a sequence of characters to the listener.
+ * Sends a string of characters to the listener.
*
- * @param text the sequence of characters to be displayed.
+ * @param text the string of characters to be registered.
*/
- public void onTextInput(CharSequence text);
+ public void onTextInput(String text);
/**
* Called when user started batch input.
@@ -80,6 +80,8 @@ public interface KeyboardActionListener {
*/
public void onEndBatchInput(InputPointers batchPointers);
+ public void onCancelBatchInput();
+
/**
* Called when user released a finger outside any key.
*/
@@ -99,7 +101,7 @@ public interface KeyboardActionListener {
@Override
public void onCodeInput(int primaryCode, int x, int y) {}
@Override
- public void onTextInput(CharSequence text) {}
+ public void onTextInput(String text) {}
@Override
public void onStartBatchInput() {}
@Override
@@ -107,18 +109,12 @@ public interface KeyboardActionListener {
@Override
public void onEndBatchInput(InputPointers batchPointers) {}
@Override
+ public void onCancelBatchInput() {}
+ @Override
public void onCancelInput() {}
@Override
public boolean onCustomRequest(int requestCode) {
return false;
}
-
- // TODO: Remove this method when the vertical correction is removed.
- public static boolean isInvalidCoordinate(int coordinate) {
- // Detect {@link Constants#NOT_A_COORDINATE},
- // {@link Constants#SUGGESTION_STRIP_COORDINATE}, and
- // {@link Constants#SPELL_CHECKER_COORDINATE}.
- return coordinate < 0;
- }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index c7813ab02..4d5d7e14e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -34,6 +34,7 @@ import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -194,12 +195,11 @@ public final class KeyboardLayoutSet {
final Params params = mParams;
final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
|| keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
- final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype);
- final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage;
- final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain);
+ final boolean hasShortcutKey = params.mVoiceKeyEnabled
+ && (isSymbols != params.mVoiceKeyOnMain);
return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor,
params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo,
- params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey,
+ params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey,
params.mLanguageSwitchKeyEnabled);
}
@@ -266,7 +266,7 @@ public final class KeyboardLayoutSet {
return this;
}
- // For test only
+ @UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mDisableTouchPositionCorrectionDataForTest = true;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index de8097b55..c1c105e8d 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -29,12 +29,12 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.latin.DebugSettings;
-import com.android.inputmethod.latin.ImfUtils;
+import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.InputView;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputMethodManager;
import com.android.inputmethod.latin.SettingsValues;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.WordComposer;
@@ -65,9 +65,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
};
+ private AudioAndHapticFeedbackManager mFeedbackManager;
private SubtypeSwitcher mSubtypeSwitcher;
private SharedPreferences mPrefs;
- private boolean mForceNonDistinctMultitouch;
private InputView mCurrentInputView;
private MainKeyboardView mKeyboardView;
@@ -102,12 +102,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void initInternal(LatinIME latinIme, SharedPreferences prefs) {
mLatinIME = latinIme;
mResources = latinIme.getResources();
+ mFeedbackManager = new AudioAndHapticFeedbackManager(latinIme);
mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mState = new KeyboardState(this);
setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs));
- mForceNonDistinctMultitouch = prefs.getBoolean(
- DebugSettings.FORCE_NON_DISTINCT_MULTITOUCH_KEY, false);
}
private static KeyboardTheme getKeyboardTheme(Context context, SharedPreferences prefs) {
@@ -126,7 +125,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
private void setContextThemeWrapper(Context context, KeyboardTheme keyboardTheme) {
- if (mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
+ if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
mKeyboardTheme = keyboardTheme;
mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
KeyboardLayoutSet.clearKeyboardCache();
@@ -143,10 +142,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
builder.setOptions(
settingsValues.isVoiceKeyEnabled(editorInfo),
settingsValues.isVoiceKeyOnMain(),
- settingsValues.isLanguageSwitchKeyEnabled(mThemeContext));
+ settingsValues.isLanguageSwitchKeyEnabled());
mKeyboardLayoutSet = builder.build();
try {
mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols));
+ mFeedbackManager.onSettingsChanged(settingsValues);
} catch (KeyboardLayoutSetException e) {
Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
@@ -154,6 +154,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
+ public void onRingerModeChanged() {
+ mFeedbackManager.onRingerModeChanged();
+ }
+
public void saveKeyboardState() {
if (getKeyboard() != null) {
mState.onSaveKeyboardState();
@@ -183,7 +187,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
keyboard.mId.mLocale);
keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
- ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true));
+ RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
}
public Keyboard getKeyboard() {
@@ -208,7 +212,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
public void onPressKey(int code) {
if (isVibrateAndSoundFeedbackRequired()) {
- mLatinIME.hapticAndAudioFeedback(code);
+ mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
}
mState.onPressKey(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
@@ -320,7 +324,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void hapticAndAudioFeedback(int code) {
- mLatinIME.hapticAndAudioFeedback(code);
+ mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
}
public void onLongPressTimeout(int code) {
@@ -339,10 +343,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
return mKeyboardView != null && mKeyboardView.getPointerCount() == 1;
}
- public boolean hasDistinctMultitouch() {
- return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch();
- }
-
/**
* Updates state machine to figure out when to automatically switch back to the previous mode.
*/
@@ -369,9 +369,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
}
mKeyboardView.setKeyboardActionListener(mLatinIME);
- if (mForceNonDistinctMultitouch) {
- mKeyboardView.setDistinctMultitouch(false);
- }
// This always needs to be set since the accessibility state can
// potentially change without the input view being re-created.
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 472f74b12..992282bf9 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -45,6 +45,7 @@ import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.CoordinateUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
@@ -134,7 +135,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Preview placer view
private final PreviewPlacerView mPreviewPlacerView;
- private final int[] mCoordinates = new int[2];
+ private final int[] mOriginCoords = CoordinateUtils.newInstance();
// Key preview
private static final int PREVIEW_ALPHA = 240;
@@ -832,10 +833,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// In transient state.
return;
}
- final int[] viewOrigin = new int[2];
- getLocationInWindow(viewOrigin);
+ getLocationInWindow(mOriginCoords);
final DisplayMetrics dm = getResources().getDisplayMetrics();
- if (viewOrigin[1] < dm.heightPixels / 4) {
+ if (CoordinateUtils.y(mOriginCoords) < dm.heightPixels / 4) {
// In transient state.
return;
}
@@ -850,10 +850,21 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
} else {
windowContentView.addView(mPreviewPlacerView);
- mPreviewPlacerView.setKeyboardViewGeometry(viewOrigin[0], viewOrigin[1], width, height);
+ mPreviewPlacerView.setKeyboardViewGeometry(mOriginCoords, width, height);
}
}
+ @Override
+ public void showSlidingKeyInputPreview(final PointerTracker tracker) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.showSlidingKeyInputPreview(tracker);
+ }
+
+ @Override
+ public void dismissSlidingKeyInputPreview() {
+ mPreviewPlacerView.dismissSlidingKeyInputPreview();
+ }
+
public void showGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
locatePreviewPlacerView();
mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
@@ -935,12 +946,13 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// The distance between the top edge of the parent key and the bottom of the visible part
// of the key preview background.
previewParams.mPreviewVisibleOffset = mPreviewOffset - previewText.getPaddingBottom();
- getLocationInWindow(mCoordinates);
+ getLocationInWindow(mOriginCoords);
// The key preview is horizontally aligned with the center of the visible part of the
// parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
// the left/right background is used if such background is specified.
final int statePosition;
- int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0];
+ int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2
+ + CoordinateUtils.x(mOriginCoords);
if (previewX < 0) {
previewX = 0;
statePosition = STATE_LEFT;
@@ -952,7 +964,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// The key preview is placed vertically above the top edge of the parent key with an
// arbitrary offset.
- final int previewY = key.mY - previewHeight + mPreviewOffset + mCoordinates[1];
+ final int previewY = key.mY - previewHeight + mPreviewOffset
+ + CoordinateUtils.y(mOriginCoords);
if (background != null) {
final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 3e6f92c2a..b9ce4fb1f 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard;
import android.animation.AnimatorInflater;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -28,7 +29,8 @@ import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
-import android.text.TextUtils;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -41,11 +43,14 @@ import android.widget.PopupWindow;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.annotations.ExternallyReferenced;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
-import com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler;
+import com.android.inputmethod.keyboard.internal.TouchScreenRegulator;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.CoordinateUtils;
+import com.android.inputmethod.latin.DebugSettings;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
@@ -95,7 +100,7 @@ import java.util.WeakHashMap;
* @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
*/
public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
- SuddenJumpingTouchEventHandler.ProcessMotionEvent {
+ TouchScreenRegulator.ProcessMotionEvent {
private static final String TAG = MainKeyboardView.class.getSimpleName();
// TODO: Kill process when the usability study mode was changed.
@@ -138,10 +143,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
new WeakHashMap<Key, MoreKeysPanel>();
private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
- private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
+ private final TouchScreenRegulator mTouchScreenRegulator;
protected KeyDetector mKeyDetector;
- private boolean mHasDistinctMultitouch;
+ private final boolean mHasDistinctMultitouch;
private int mOldPointerCount = 1;
private Key mOldKey;
@@ -153,12 +158,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private static final int MSG_REPEAT_KEY = 1;
private static final int MSG_LONGPRESS_KEY = 2;
private static final int MSG_DOUBLE_TAP = 3;
+ private static final int MSG_UPDATE_BATCH_INPUT = 4;
private final int mKeyRepeatStartTimeout;
private final int mKeyRepeatInterval;
private final int mLongPressKeyTimeout;
private final int mLongPressShiftKeyTimeout;
private final int mIgnoreAltCodeKeyTimeout;
+ private final int mGestureRecognitionUpdateTime;
public KeyTimerHandler(final MainKeyboardView outerInstance,
final TypedArray mainKeyboardViewAttr) {
@@ -174,6 +181,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
+ mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureRecognitionUpdateTime, 0);
}
@Override
@@ -198,12 +207,18 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
}
break;
+ case MSG_UPDATE_BATCH_INPUT:
+ tracker.updateBatchInputByTimer(SystemClock.uptimeMillis());
+ startUpdateBatchInputTimer(tracker);
+ break;
}
}
private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
final Key key = tracker.getKey();
- if (key == null) return;
+ if (key == null) {
+ return;
+ }
sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
}
@@ -226,7 +241,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
cancelLongPressTimer();
final int delay;
switch (code) {
- case Keyboard.CODE_SHIFT:
+ case Constants.CODE_SHIFT:
delay = mLongPressShiftKeyTimeout;
break;
default:
@@ -247,7 +262,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final Key key = tracker.getKey();
final int delay;
switch (key.mCode) {
- case Keyboard.CODE_SHIFT:
+ case Constants.CODE_SHIFT:
delay = mLongPressShiftKeyTimeout;
break;
default:
@@ -304,7 +319,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// When user hits the space or the enter key, just cancel the while-typing timer.
final int typedCode = typedKey.mCode;
- if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) {
+ if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
startWhileTypingFadeinAnimation(keyboardView);
return;
}
@@ -344,8 +359,24 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
cancelLongPressTimer();
}
+ @Override
+ public void startUpdateBatchInputTimer(final PointerTracker tracker) {
+ if (mGestureRecognitionUpdateTime <= 0) {
+ return;
+ }
+ removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_BATCH_INPUT, tracker),
+ mGestureRecognitionUpdateTime);
+ }
+
+ @Override
+ public void cancelAllUpdateBatchInputTimers() {
+ removeMessages(MSG_UPDATE_BATCH_INPUT);
+ }
+
public void cancelAllMessages() {
cancelKeyTimers();
+ cancelAllUpdateBatchInputTimers();
}
}
@@ -356,15 +387,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
- mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
+ mTouchScreenRegulator = new TouchScreenRegulator(context, this);
- mHasDistinctMultitouch = context.getPackageManager()
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ final boolean forceNonDistinctMultitouch = prefs.getBoolean(
+ DebugSettings.FORCE_NON_DISTINCT_MULTITOUCH_KEY, false);
+ final boolean hasDistinctMultitouch = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
final Resources res = getResources();
final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
ResourceUtils.getDeviceOverrideValue(res,
R.array.phantom_sudden_move_event_device_list, "false"));
- PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
+ PointerTracker.init(needsPhantomSuddenMoveEventHack);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
@@ -408,7 +443,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
- if (resId == 0) return null;
+ if (resId == 0) {
+ return null;
+ }
final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
getContext(), resId);
if (animator != null) {
@@ -417,20 +454,23 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return animator;
}
- // Getter/setter methods for {@link ObjectAnimator}.
+ @ExternallyReferenced
public int getLanguageOnSpacebarAnimAlpha() {
return mLanguageOnSpacebarAnimAlpha;
}
+ @ExternallyReferenced
public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
mLanguageOnSpacebarAnimAlpha = alpha;
invalidateKey(mSpaceKey);
}
+ @ExternallyReferenced
public int getAltCodeKeyWhileTypingAnimAlpha() {
return mAltCodeKeyWhileTypingAnimAlpha;
}
+ @ExternallyReferenced
public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
mAltCodeKeyWhileTypingAnimAlpha = alpha;
updateAltCodeKeyWhileTyping();
@@ -480,10 +520,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
PointerTracker.setKeyDetector(mKeyDetector);
- mTouchScreenRegulator.setKeyboard(keyboard);
+ mTouchScreenRegulator.setKeyboardGeometry(keyboard.mOccupiedWidth);
mMoreKeysPanelCache.clear();
- mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
+ mSpaceKey = keyboard.getKey(Constants.CODE_SPACE);
mSpaceIcon = (mSpaceKey != null)
? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
@@ -506,18 +546,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
}
- /**
- * Returns whether the device has distinct multi-touch panel.
- * @return true if the device has distinct multi-touch panel.
- */
- public boolean hasDistinctMultitouch() {
- return mHasDistinctMultitouch;
- }
-
- public void setDistinctMultitouch(final boolean hasDistinctMultitouch) {
- mHasDistinctMultitouch = hasDistinctMultitouch;
- }
-
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -553,21 +581,25 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
// Check if we are already displaying popup panel.
- if (mMoreKeysPanel != null)
+ if (mMoreKeysPanel != null) {
return false;
- if (parentKey == null)
+ }
+ if (parentKey == null) {
return false;
+ }
return onLongPress(parentKey, tracker);
}
// This default implementation returns a more keys panel.
protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
- if (parentKey.mMoreKeys == null)
+ if (parentKey.mMoreKeys == null) {
return null;
+ }
final View container = LayoutInflater.from(getContext()).inflate(mMoreKeysLayout, null);
- if (container == null)
+ if (container == null) {
throw new NullPointerException();
+ }
final MoreKeysKeyboardView moreKeysKeyboardView =
(MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view);
@@ -600,7 +632,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
KeyboardSwitcher.getInstance().hapticAndAudioFeedback(primaryCode);
return true;
}
- if (primaryCode == Keyboard.CODE_SPACE || primaryCode == Keyboard.CODE_LANGUAGE_SWITCH) {
+ if (primaryCode == Constants.CODE_SPACE || primaryCode == Constants.CODE_LANGUAGE_SWITCH) {
// Long pressing the space key invokes IME switcher dialog.
if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) {
tracker.onLongPressed();
@@ -628,8 +660,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
if (moreKeysPanel == null) {
moreKeysPanel = onCreateMoreKeysPanel(parentKey);
- if (moreKeysPanel == null)
+ if (moreKeysPanel == null) {
return false;
+ }
mMoreKeysPanelCache.put(parentKey, moreKeysPanel);
}
if (mMoreKeysWindow == null) {
@@ -640,12 +673,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mMoreKeysPanel = moreKeysPanel;
mMoreKeysPanelPointerTrackerId = tracker.mPointerId;
+ final int[] lastCoords = CoordinateUtils.newInstance();
+ tracker.getLastCoordinates(lastCoords);
final boolean keyPreviewEnabled = isKeyPreviewPopupEnabled() && !parentKey.noKeyPreview();
// The more keys keyboard is usually horizontally aligned with the center of the parent key.
// If showMoreKeysKeyboardAtTouchedPoint is true and the key preview is disabled, the more
// keys keyboard is placed at the touch point of the parent key.
final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
- ? tracker.getLastX()
+ ? CoordinateUtils.x(lastCoords)
: parentKey.mX + parentKey.mWidth / 2;
// The more keys keyboard is usually vertically aligned with the top edge of the parent key
// (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
@@ -655,8 +690,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
moreKeysPanel.showMoreKeysPanel(
this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
- final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
- final int translatedY = moreKeysPanel.translateY(tracker.getLastY());
+ final int translatedX = moreKeysPanel.translateX(CoordinateUtils.x(lastCoords));
+ final int translatedY = moreKeysPanel.translateY(CoordinateUtils.y(lastCoords));
tracker.onShowMoreKeysPanel(translatedX, translatedY, moreKeysPanel);
dimEntireKeyboard(true);
return true;
@@ -665,9 +700,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
public boolean isInSlidingKeyInput() {
if (mMoreKeysPanel != null) {
return true;
- } else {
- return PointerTracker.isAnyInSlidingKeyInput();
}
+ return PointerTracker.isAnyInSlidingKeyInput();
}
public int getPointerCount() {
@@ -716,39 +750,15 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
x = (int)me.getX(index);
y = (int)me.getY(index);
}
- if (ENABLE_USABILITY_STUDY_LOG) {
- final String eventTag;
- switch (action) {
- 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: // Skip this as being logged below
- eventTag = "";
- break;
- default:
- eventTag = "[Action" + action + "]";
- break;
- }
- if (!TextUtils.isEmpty(eventTag)) {
- final float size = me.getSize(index);
- final float pressure = me.getPressure(index);
- UsabilityStudyLogUtils.getInstance().write(
- eventTag + eventTime + "," + id + "," + x + "," + y + ","
- + size + "," + pressure);
- }
+ // TODO: This might be moved to the tracker.processMotionEvent() call below.
+ if (ENABLE_USABILITY_STUDY_LOG && action != MotionEvent.ACTION_MOVE) {
+ writeUsabilityStudyLog(me, action, eventTime, index, id, x, y);
}
+ // TODO: This should be moved to the tracker.processMotionEvent() call below.
+ // Currently the same "move" event is being logged twice.
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id,
- x, y);
+ ResearchLogger.mainKeyboardView_processMotionEvent(
+ me, action, eventTime, index, id, x, y);
}
if (mKeyTimerHandler.isInKeyRepeat()) {
@@ -774,16 +784,18 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
final Key newKey = tracker.getKeyOn(x, y);
if (mOldKey != newKey) {
tracker.onDownEvent(x, y, eventTime, this);
- if (action == MotionEvent.ACTION_UP)
+ if (action == MotionEvent.ACTION_UP) {
tracker.onUpEvent(x, y, eventTime);
+ }
}
} else if (pointerCount == 2 && oldPointerCount == 1) {
// Single-touch to multi-touch transition.
// Send an up event for the last pointer.
- final int lastX = tracker.getLastX();
- final int lastY = tracker.getLastY();
- mOldKey = tracker.getKeyOn(lastX, lastY);
- tracker.onUpEvent(lastX, lastY, eventTime);
+ final int[] lastCoords = CoordinateUtils.newInstance();
+ mOldKey = tracker.getKeyOn(
+ CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords));
+ tracker.onUpEvent(
+ CoordinateUtils.x(lastCoords), CoordinateUtils.y(lastCoords), eventTime);
} else if (pointerCount == 1 && oldPointerCount == 1) {
tracker.processMotionEvent(action, x, y, eventTime, this);
} else {
@@ -812,15 +824,11 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
tracker.onMoveEvent(px, py, eventTime, motionEvent);
if (ENABLE_USABILITY_STUDY_LOG) {
- final float pointerSize = me.getSize(i);
- final float pointerPressure = me.getPressure(i);
- UsabilityStudyLogUtils.getInstance().write("[Move]" + eventTime + ","
- + pointerId + "," + px + "," + py + ","
- + pointerSize + "," + pointerPressure);
+ writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime,
- i, pointerId, px, py);
+ ResearchLogger.mainKeyboardView_processMotionEvent(
+ me, action, eventTime, i, pointerId, px, py);
}
}
} else {
@@ -831,6 +839,35 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return true;
}
+ private static void writeUsabilityStudyLog(final MotionEvent me, final int action,
+ final long eventTime, final int index, final int id, final int x, final int y) {
+ final String eventTag;
+ switch (action) {
+ 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;
+ default:
+ eventTag = "[Action" + action + "]";
+ break;
+ }
+ final float size = me.getSize(index);
+ final float pressure = me.getPressure(index);
+ UsabilityStudyLogUtils.getInstance().write(
+ eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + "," + pressure);
+ }
+
@Override
public void closing() {
super.closing();
@@ -840,14 +877,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
@Override
public boolean dismissMoreKeysPanel() {
- if (mMoreKeysWindow != null && mMoreKeysWindow.isShowing()) {
- mMoreKeysWindow.dismiss();
- mMoreKeysPanel = null;
- mMoreKeysPanelPointerTrackerId = -1;
- dimEntireKeyboard(false);
- return true;
+ if (mMoreKeysWindow == null || !mMoreKeysWindow.isShowing()) {
+ return false;
}
- return false;
+ mMoreKeysWindow.dismiss();
+ mMoreKeysPanel = null;
+ mMoreKeysPanelPointerTrackerId = -1;
+ dimEntireKeyboard(false);
+ return true;
}
/**
@@ -870,16 +907,22 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
public void updateShortcutKey(final boolean available) {
final Keyboard keyboard = getKeyboard();
- if (keyboard == null) return;
- final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
- if (shortcutKey == null) return;
+ if (keyboard == null) {
+ return;
+ }
+ final Key shortcutKey = keyboard.getKey(Constants.CODE_SHORTCUT);
+ if (shortcutKey == null) {
+ return;
+ }
shortcutKey.setEnabled(available);
invalidateKey(shortcutKey);
}
private void updateAltCodeKeyWhileTyping() {
final Keyboard keyboard = getKeyboard();
- if (keyboard == null) return;
+ if (keyboard == null) {
+ return;
+ }
for (final Key key : keyboard.mAltCodeKeysWhileTyping) {
invalidateKey(key);
}
@@ -909,7 +952,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
}
public void updateAutoCorrectionState(final boolean isAutoCorrection) {
- if (!mAutoCorrectionSpacebarLedEnabled) return;
+ if (!mAutoCorrectionSpacebarLedEnabled) {
+ return;
+ }
mAutoCorrectionSpacebarLedOn = isAutoCorrection;
invalidateKey(mSpaceKey);
}
@@ -920,13 +965,13 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
if (key.altCodeWhileTyping() && key.isEnabled()) {
params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
}
- if (key.mCode == Keyboard.CODE_SPACE) {
+ if (key.mCode == Constants.CODE_SPACE) {
drawSpacebar(key, canvas, paint);
// Whether space key needs to show the "..." popup hint for special purposes
if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
drawKeyPopupHint(key, canvas, paint, params);
}
- } else if (key.mCode == Keyboard.CODE_LANGUAGE_SWITCH) {
+ } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
super.onDrawKeyTopVisuals(key, canvas, paint, params);
drawKeyPopupHint(key, canvas, paint, params);
} else {
@@ -937,10 +982,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
paint.setTextScaleX(1.0f);
final float textWidth = getLabelWidth(text, paint);
- if (textWidth < width) return true;
+ if (textWidth < width) {
+ return true;
+ }
final float scaleX = width / textWidth;
- if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) return false;
+ if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
+ return false;
+ }
paint.setTextScaleX(scaleX);
return getLabelWidth(text, paint) < width;
@@ -950,19 +999,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
final int width) {
// Choose appropriate language name to fit into the width.
- String text = getFullDisplayName(subtype, getResources());
- if (fitsTextIntoWidth(width, text, paint)) {
- return text;
+ final String fullText = getFullDisplayName(subtype, getResources());
+ if (fitsTextIntoWidth(width, fullText, paint)) {
+ return fullText;
}
- text = getMiddleDisplayName(subtype);
- if (fitsTextIntoWidth(width, text, paint)) {
- return text;
+ final String middleText = getMiddleDisplayName(subtype);
+ if (fitsTextIntoWidth(width, middleText, paint)) {
+ return middleText;
}
- text = getShortDisplayName(subtype);
- if (fitsTextIntoWidth(width, text, paint)) {
- return text;
+ final String shortText = getShortDisplayName(subtype);
+ if (fitsTextIntoWidth(width, shortText, paint)) {
+ return shortText;
}
return "";
@@ -1009,18 +1058,19 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
// InputMethodSubtype's display name for spacebar text in its locale.
// isAdditionalSubtype (T=true, F=false)
- // locale layout | Short Middle Full
- // ------ ------ - ---- --------- ----------------------
- // en_US qwerty F En English English (US) exception
- // en_GB qwerty F En English English (UK) exception
- // fr azerty F Fr Français Français
- // fr_CA qwerty F Fr Français Français (Canada)
- // de qwertz F De Deutsch Deutsch
- // zz qwerty F QWERTY QWERTY
- // fr qwertz T Fr Français Français (QWERTZ)
- // de qwerty T De Deutsch Deutsch (QWERTY)
- // en_US azerty T En English English (US) (AZERTY)
- // zz azerty T AZERTY AZERTY
+ // locale layout | Short Middle Full
+ // ------ ------- - ---- --------- ----------------------
+ // en_US qwerty F En English English (US) exception
+ // en_GB qwerty F En English English (UK) exception
+ // es_US spanish F Es Español Español (EE.UU.) exception
+ // fr azerty F Fr Français Français
+ // fr_CA qwerty F Fr Français Français (Canada)
+ // de qwertz F De Deutsch Deutsch
+ // zz qwerty F QWERTY QWERTY
+ // fr qwertz T Fr Français Français (QWERTZ)
+ // de qwerty T De Deutsch Deutsch (QWERTY)
+ // en_US azerty T En English English (US) (AZERTY)
+ // zz azerty T AZERTY AZERTY
// Get InputMethodSubtype's full display name in its locale.
static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index d7d4be40b..3826a39a4 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -20,6 +20,7 @@ import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.view.View;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -39,7 +40,7 @@ public final class MoreKeysKeyboard extends Keyboard {
return mDefaultKeyCoordX;
}
- /* package for test */
+ @UsedForTesting
static class MoreKeysKeyboardParams extends KeyboardParams {
public boolean mIsFixedOrder;
/* package */int mTopRowAdjustment;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a50617693..a698b0bd8 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -26,6 +26,7 @@ import android.widget.PopupWindow;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.CoordinateUtils;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.R;
@@ -34,7 +35,7 @@ import com.android.inputmethod.latin.R;
* detecting key presses and touch movements.
*/
public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
- private final int[] mCoordinates = new int[2];
+ private final int[] mCoordinates = CoordinateUtils.newInstance();
private final KeyDetector mKeyDetector;
@@ -48,7 +49,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
private final KeyboardActionListener mMoreKeysKeyboardListener =
new KeyboardActionListener.Adapter() {
@Override
- public void onCodeInput(int primaryCode, int x, int y) {
+ public void onCodeInput(final int primaryCode, final int x, final int y) {
// Because a more keys keyboard doesn't need proximity characters correction, we don't
// send touch event coordinates.
mListener.onCodeInput(
@@ -56,7 +57,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onTextInput(CharSequence text) {
+ public void onTextInput(final String text) {
mListener.onTextInput(text);
}
@@ -66,12 +67,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onUpdateBatchInput(InputPointers batchPointers) {
+ public void onUpdateBatchInput(final InputPointers batchPointers) {
mListener.onUpdateBatchInput(batchPointers);
}
@Override
- public void onEndBatchInput(InputPointers batchPointers) {
+ public void onEndBatchInput(final InputPointers batchPointers) {
mListener.onEndBatchInput(batchPointers);
}
@@ -81,21 +82,22 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onPressKey(int primaryCode) {
+ public void onPressKey(final int primaryCode) {
mListener.onPressKey(primaryCode);
}
@Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {
+ public void onReleaseKey(final int primaryCode, final boolean withSliding) {
mListener.onReleaseKey(primaryCode, withSliding);
}
};
- public MoreKeysKeyboardView(Context context, AttributeSet attrs) {
+ public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
}
- public MoreKeysKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
@@ -105,7 +107,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard != null) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
@@ -117,7 +119,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void setKeyboard(Keyboard keyboard) {
+ public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
-getPaddingTop() + mVerticalCorrection);
@@ -144,15 +146,16 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
// More keys keyboard needs no pop-up key preview displayed, so we pass always false with a
// delay of 0. The delay does not matter actually since the popup is not shown anyway.
super.setKeyPreviewPopupEnabled(false, 0);
}
@Override
- public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
- PopupWindow window, KeyboardActionListener listener) {
+ public void showMoreKeysPanel(final View parentView, final Controller controller,
+ final int pointX, final int pointY, final PopupWindow window,
+ final KeyboardActionListener listener) {
mController = controller;
mListener = listener;
final View container = (View)getParent();
@@ -167,7 +170,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
window.setHeight(container.getMeasuredHeight());
parentView.getLocationInWindow(mCoordinates);
window.showAtLocation(parentView, Gravity.NO_GRAVITY,
- x + mCoordinates[0], y + mCoordinates[1]);
+ x + CoordinateUtils.x(mCoordinates), y + CoordinateUtils.y(mCoordinates));
mOriginX = x + container.getPaddingLeft();
mOriginY = y + container.getPaddingTop();
@@ -185,12 +188,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public int translateX(int x) {
+ public int translateX(final int x) {
return x - mOriginX;
}
@Override
- public int translateY(int y) {
+ public int translateY(final int y) {
return y - mOriginY;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d0f7cb276..20a299e49 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -27,6 +27,8 @@ import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokePara
import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.CoordinateUtils;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
@@ -79,6 +81,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public void invalidateKey(Key key);
public void showKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public void showSlidingKeyInputPreview(PointerTracker tracker);
+ public void dismissSlidingKeyInputPreview();
public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
}
@@ -93,6 +97,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public void cancelDoubleTapTimer();
public boolean isInDoubleTapTimeout();
public void cancelKeyTimers();
+ public void startUpdateBatchInputTimer(PointerTracker tracker);
+ public void cancelAllUpdateBatchInputTimers();
public static class Adapter implements TimerProxy {
@Override
@@ -115,6 +121,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
public boolean isInDoubleTapTimeout() { return false; }
@Override
public void cancelKeyTimers() {}
+ @Override
+ public void startUpdateBatchInputTimer(PointerTracker tracker) {}
+ @Override
+ public void cancelAllUpdateBatchInputTimers() {}
}
}
@@ -156,7 +166,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
- private static PointerTrackerQueue sPointerTrackerQueue;
+ private static final PointerTrackerQueue sPointerTrackerQueue = new PointerTrackerQueue();
public final int mPointerId;
@@ -289,6 +299,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// The position and time at which first down event occurred.
private long mDownTime;
+ private int[] mDownCoordinates = CoordinateUtils.newInstance();
private long mUpTime;
// The current key where this pointer is.
@@ -304,8 +315,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
- // true if event is already translated to a key action.
- private boolean mKeyAlreadyProcessed;
+ // true if this pointer is no longer tracking touch event.
+ private boolean mIsTrackingCanceled;
// true if this pointer has been long-pressed and is showing a more keys panel.
private boolean mIsShowingMoreKeysPanel;
@@ -325,13 +336,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
- public static void init(boolean hasDistinctMultitouch,
- boolean needsPhantomSuddenMoveEventHack) {
- if (hasDistinctMultitouch) {
- sPointerTrackerQueue = new PointerTrackerQueue();
- } else {
- sPointerTrackerQueue = null;
- }
+ public static void init(final boolean needsPhantomSuddenMoveEventHack) {
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
sParams = PointerTrackerParams.DEFAULT;
sGestureStrokeParams = GestureStrokeParams.DEFAULT;
@@ -375,7 +380,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
public static boolean isAnyInSlidingKeyInput() {
- return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
+ return sPointerTrackerQueue.isAnyInSlidingKeyInput();
}
public static void setKeyboardActionListener(final KeyboardActionListener listener) {
@@ -453,8 +458,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
final int code = altersCode ? key.getAltCode() : primaryCode;
if (DEBUG_LISTENER) {
- final String output = code == Keyboard.CODE_OUTPUT_TEXT
- ? key.getOutputText() : Keyboard.printableCode(code);
+ final String output = code == Constants.CODE_OUTPUT_TEXT
+ ? key.getOutputText() : Constants.printableCode(code);
Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
output, ignoreModifierKey ? " ignoreModifier" : "",
altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
@@ -469,9 +474,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
if (key.isEnabled() || altersCode) {
sTimeRecorder.onCodeInput(code, eventTime);
- if (code == Keyboard.CODE_OUTPUT_TEXT) {
+ if (code == Constants.CODE_OUTPUT_TEXT) {
mListener.onTextInput(key.getOutputText());
- } else if (code != Keyboard.CODE_UNSPECIFIED) {
+ } else if (code != Constants.CODE_UNSPECIFIED) {
mListener.onCodeInput(code, x, y);
}
}
@@ -487,7 +492,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId,
- Keyboard.printableCode(primaryCode),
+ Constants.printableCode(primaryCode),
withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
key.isEnabled() ? "": " disabled"));
}
@@ -522,7 +527,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
mKeyboard = keyDetector.getKeyboard();
final int keyWidth = mKeyboard.mMostCommonKeyWidth;
final int keyHeight = mKeyboard.mMostCommonKeyHeight;
- mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth);
+ mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth, mKeyboard.mOccupiedHeight);
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
if (newKey != mCurrentKey) {
if (mDrawingProxy != null) {
@@ -539,6 +544,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return mIsInSlidingKeyInput;
}
+ public boolean isInSlidingKeyInputFromModifier() {
+ return mIsInSlidingKeyInputFromModifier;
+ }
+
public Key getKey() {
return mCurrentKey;
}
@@ -641,20 +650,21 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return mGestureStrokeWithPreviewPoints;
}
- public int getLastX() {
- return mLastX;
- }
-
- public int getLastY() {
- return mLastY;
+ public void getLastCoordinates(final int[] outCoords) {
+ CoordinateUtils.set(outCoords, mLastX, mLastY);
}
public long getDownTime() {
return mDownTime;
}
+ public void getDownCoordinates(final int[] outCoords) {
+ CoordinateUtils.copy(outCoords, mDownCoordinates);
+ }
+
private Key onDownKey(final int x, final int y, final long eventTime) {
mDownTime = eventTime;
+ CoordinateUtils.set(mDownCoordinates, x, y);
mBogusMoveEventDetector.onDownKey();
return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
}
@@ -682,7 +692,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
}
private static int getActivePointerTrackerCount() {
- return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
+ return sPointerTrackerQueue.size();
+ }
+
+ private static boolean isOldestTrackerInQueue(final PointerTracker tracker) {
+ return sPointerTrackerQueue.getOldestElement() == tracker;
}
private void mayStartBatchInput(final Key key) {
@@ -702,47 +716,74 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sLastRecognitionTime = 0;
mListener.onStartBatchInput();
}
- final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
- mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
+ mTimerProxy.cancelLongPressTimer();
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
+ }
+
+ public void updateBatchInputByTimer(final long eventTime) {
+ final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
+ mGestureStrokeWithPreviewPoints.duplicateLastPointWith(gestureTime);
+ updateBatchInput(eventTime);
}
private void mayUpdateBatchInput(final long eventTime, final Key key) {
if (key != null) {
- synchronized (sAggregratedPointers) {
- final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
- stroke.appendIncrementalBatchPoints(sAggregratedPointers);
- final int size = sAggregratedPointers.getPointerSize();
- if (size > sLastRecognitionPointSize
- && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
- sLastRecognitionPointSize = size;
- sLastRecognitionTime = eventTime;
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
- mPointerId, size));
- }
- mListener.onUpdateBatchInput(sAggregratedPointers);
+ updateBatchInput(eventTime);
+ }
+ if (mIsTrackingCanceled) {
+ return;
+ }
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
+ }
+
+ private void updateBatchInput(final long eventTime) {
+ synchronized (sAggregratedPointers) {
+ final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
+ stroke.appendIncrementalBatchPoints(sAggregratedPointers);
+ final int size = sAggregratedPointers.getPointerSize();
+ if (size > sLastRecognitionPointSize
+ && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
+ sLastRecognitionPointSize = size;
+ sLastRecognitionTime = eventTime;
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
+ size));
}
+ mTimerProxy.startUpdateBatchInputTimer(this);
+ mListener.onUpdateBatchInput(sAggregratedPointers);
}
}
- final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
- mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
}
private void mayEndBatchInput(final long eventTime) {
synchronized (sAggregratedPointers) {
mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
if (getActivePointerTrackerCount() == 1) {
- if (DEBUG_LISTENER) {
- Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
- mPointerId, sAggregratedPointers.getPointerSize()));
- }
sInGesture = false;
sTimeRecorder.onEndBatchInput(eventTime);
- mListener.onEndBatchInput(sAggregratedPointers);
+ mTimerProxy.cancelAllUpdateBatchInputTimers();
+ if (!mIsTrackingCanceled) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
+ mPointerId, sAggregratedPointers.getPointerSize()));
+ }
+ mListener.onEndBatchInput(sAggregratedPointers);
+ }
}
}
- final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
- mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
+ if (mIsTrackingCanceled) {
+ return;
+ }
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTrackerInQueue(this));
+ }
+
+ private void cancelBatchInput() {
+ sPointerTrackerQueue.cancelAllPointerTracker();
+ sInGesture = false;
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onCancelBatchInput", mPointerId));
+ }
+ mListener.onCancelBatchInput();
}
public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
@@ -787,29 +828,26 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
}
- mKeyAlreadyProcessed = true;
+ cancelTracking();
return;
}
}
final Key key = getKeyOn(x, y);
mBogusMoveEventDetector.onActualDownEvent(x, y);
- final PointerTrackerQueue queue = sPointerTrackerQueue;
- if (queue != null) {
- if (key != null && key.isModifier()) {
- // Before processing a down event of modifier key, all pointers already being
- // tracked should be released.
- queue.releaseAllPointers(eventTime);
- }
- queue.add(this);
+ if (key != null && key.isModifier()) {
+ // Before processing a down event of modifier key, all pointers already being
+ // tracked should be released.
+ sPointerTrackerQueue.releaseAllPointers(eventTime);
}
+ sPointerTrackerQueue.add(this);
onDownEventInternal(x, y, eventTime);
if (!sShouldHandleGesture) {
return;
}
- // A gesture should start only from the letter key.
+ // A gesture should start only from a non-modifier key.
mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
- && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode);
+ && !mIsShowingMoreKeysPanel && key != null && !key.isModifier();
if (mIsDetectingGesture) {
if (getActivePointerTrackerCount() == 1) {
sGestureFirstDownTime = eventTime;
@@ -827,7 +865,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
|| (key != null && key.isModifier())
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
- mKeyAlreadyProcessed = false;
+ mIsTrackingCanceled = false;
resetSlidingKeyInput();
if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at
@@ -853,13 +891,19 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
private void resetSlidingKeyInput() {
mIsInSlidingKeyInput = false;
mIsInSlidingKeyInputFromModifier = false;
+ mDrawingProxy.dismissSlidingKeyInputPreview();
}
private void onGestureMoveEvent(final int x, final int y, final long eventTime,
final boolean isMajorEvent, final Key key) {
final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
if (mIsDetectingGesture) {
- mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
+ final boolean onValidArea = mGestureStrokeWithPreviewPoints.addPointOnKeyboard(
+ x, y, gestureTime, isMajorEvent);
+ if (!onValidArea) {
+ cancelBatchInput();
+ return;
+ }
mayStartBatchInput(key);
if (sInGesture) {
mayUpdateBatchInput(eventTime, key);
@@ -871,7 +915,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (DEBUG_MOVE_EVENT) {
printTouchEvent("onMoveEvent:", x, y, eventTime);
}
- if (mKeyAlreadyProcessed) {
+ if (mIsTrackingCanceled) {
return;
}
@@ -891,138 +935,153 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
onMoveEventInternal(x, y, eventTime);
}
+ private void processSlidingKeyInput(final Key newKey, final int x, final int y,
+ final long eventTime) {
+ // This onPress call may have changed keyboard layout. Those cases are detected
+ // at {@link #setKeyboard}. In those cases, we should update key according
+ // to the new keyboard layout.
+ Key key = newKey;
+ if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
+ key = onMoveKey(x, y);
+ }
+ onMoveToNewKey(key, x, y);
+ startLongPressTimer(key);
+ setPressedKeyGraphics(key, eventTime);
+ }
+
+ private void processPhantomSuddenMoveHack(final Key key, final int x, final int y,
+ final long eventTime, final Key oldKey, final int lastX, final int lastY) {
+ if (DEBUG_MODE) {
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " phantom sudden move event (distance=%d) is translated to "
+ + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
+ getDistance(x, y, lastX, lastY),
+ lastX, lastY, Constants.printableCode(oldKey.mCode),
+ x, y, Constants.printableCode(key.mCode)));
+ }
+ // TODO: This should be moved to outside of this nested if-clause?
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
+ }
+ onUpEventInternal(eventTime);
+ onDownEventInternal(x, y, eventTime);
+ }
+
+ private void processProximateBogusDownMoveUpEventHack(final Key key, final int x, final int y,
+ final long eventTime, final Key oldKey, final int lastX, final int lastY) {
+ if (DEBUG_MODE) {
+ final float keyDiagonal = (float)Math.hypot(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
+ final float radiusRatio =
+ mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
+ / keyDiagonal;
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " bogus down-move-up event (raidus=%.2f key diagonal) is "
+ + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
+ mPointerId, radiusRatio,
+ lastX, lastY, Constants.printableCode(oldKey.mCode),
+ x, y, Constants.printableCode(key.mCode)));
+ }
+ onUpEventInternal(eventTime);
+ onDownEventInternal(x, y, eventTime);
+ }
+
+ private void processSildeOutFromOldKey(final Key oldKey) {
+ setReleasedKeyGraphics(oldKey);
+ callListenerOnRelease(oldKey, oldKey.mCode, true);
+ startSlidingKeyInput(oldKey);
+ mTimerProxy.cancelKeyTimers();
+ }
+
+ private void slideFromOldKeyToNewKey(final Key key, final int x, final int y,
+ final long eventTime, final Key oldKey, final int lastX, final int lastY) {
+ // The pointer has been slid in to the new key from the previous key, we must call
+ // onRelease() first to notify that the previous key has been released, then call
+ // onPress() to notify that the new key is being pressed.
+ processSildeOutFromOldKey(oldKey);
+ startRepeatKey(key);
+ if (mIsAllowedSlidingKeyInput) {
+ processSlidingKeyInput(key, x, y, eventTime);
+ }
+ // HACK: On some devices, quick successive touches may be reported as a sudden move by
+ // touch panel firmware. This hack detects such cases and translates the move event to
+ // successive up and down events.
+ // TODO: Should find a way to balance gesture detection and this hack.
+ else if (sNeedsPhantomSuddenMoveEventHack
+ && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
+ processPhantomSuddenMoveHack(key, x, y, eventTime, oldKey, lastX, lastY);
+ }
+ // HACK: On some devices, quick successive proximate touches may be reported as a bogus
+ // down-move-up event by touch panel firmware. This hack detects such cases and breaks
+ // these events into separate up and down events.
+ else if (sNeedsProximateBogusDownMoveUpEventHack && sTimeRecorder.isInFastTyping(eventTime)
+ && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
+ processProximateBogusDownMoveUpEventHack(key, x, y, eventTime, oldKey, lastX, lastY);
+ }
+ // 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.
+ else if (getActivePointerTrackerCount() > 1
+ && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
+ if (DEBUG_MODE) {
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " detected sliding finger while multi touching", mPointerId));
+ }
+ onUpEvent(x, y, eventTime);
+ cancelTracking();
+ setReleasedKeyGraphics(oldKey);
+ } else {
+ if (!mIsDetectingGesture) {
+ cancelTracking();
+ }
+ setReleasedKeyGraphics(oldKey);
+ }
+ }
+
+ private void slideOutFromOldKey(final Key oldKey, final int x, final int y) {
+ // The pointer has been slid out from the previous key, we must call onRelease() to
+ // notify that the previous key has been released.
+ processSildeOutFromOldKey(oldKey);
+ if (mIsAllowedSlidingKeyInput) {
+ onMoveToNewKey(null, x, y);
+ } else {
+ if (!mIsDetectingGesture) {
+ cancelTracking();
+ }
+ }
+ }
+
private void onMoveEventInternal(final int x, final int y, final long eventTime) {
final int lastX = mLastX;
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
- Key key = onMoveKey(x, y);
+ final Key newKey = onMoveKey(x, y);
if (sShouldHandleGesture) {
// Register move event on gesture tracker.
- onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
+ onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, newKey);
if (sInGesture) {
- mTimerProxy.cancelLongPressTimer();
mCurrentKey = null;
setReleasedKeyGraphics(oldKey);
return;
}
}
- if (key != null) {
- if (oldKey == null) {
+ if (newKey != null) {
+ if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
+ slideFromOldKeyToNewKey(newKey, x, y, eventTime, oldKey, lastX, lastY);
+ } else if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys.
// In this case, we must call onPress() to notify that the new key is being pressed.
- // This onPress call may have changed keyboard layout. Those cases are detected at
- // {@link #setKeyboard}. In those cases, we should update key according to the
- // new keyboard layout.
- if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
- key = onMoveKey(x, y);
- }
- onMoveToNewKey(key, x, y);
- startLongPressTimer(key);
- setPressedKeyGraphics(key, eventTime);
- } else if (isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
- // The pointer has been slid in to the new key from the previous key, we must call
- // onRelease() first to notify that the previous key has been released, then call
- // onPress() to notify that the new key is being pressed.
- setReleasedKeyGraphics(oldKey);
- callListenerOnRelease(oldKey, oldKey.mCode, true);
- startSlidingKeyInput(oldKey);
- mTimerProxy.cancelKeyTimers();
- startRepeatKey(key);
- if (mIsAllowedSlidingKeyInput) {
- // This onPress call may have changed keyboard layout. Those cases are detected
- // at {@link #setKeyboard}. In those cases, we should update key according
- // to the new keyboard layout.
- if (callListenerOnPressAndCheckKeyboardLayoutChange(key)) {
- key = onMoveKey(x, y);
- }
- onMoveToNewKey(key, x, y);
- startLongPressTimer(key);
- setPressedKeyGraphics(key, eventTime);
- } else {
- // HACK: On some devices, quick successive touches may be reported as a sudden
- // move by touch panel firmware. This hack detects such cases and translates the
- // move event to successive up and down events.
- // TODO: Should find a way to balance gesture detection and this hack.
- if (sNeedsPhantomSuddenMoveEventHack
- && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
- if (DEBUG_MODE) {
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " phantom sudden move event (distance=%d) is translated to "
- + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
- getDistance(x, y, lastX, lastY),
- lastX, lastY, Keyboard.printableCode(oldKey.mCode),
- x, y, Keyboard.printableCode(key.mCode)));
- }
- // TODO: This should be moved to outside of this nested if-clause?
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
- }
- onUpEventInternal(eventTime);
- onDownEventInternal(x, y, eventTime);
- }
- // HACK: On some devices, quick successive proximate touches may be reported as
- // a bogus down-move-up event by touch panel firmware. This hack detects such
- // cases and breaks these events into separate up and down events.
- else if (sNeedsProximateBogusDownMoveUpEventHack
- && sTimeRecorder.isInFastTyping(eventTime)
- && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
- if (DEBUG_MODE) {
- final float keyDiagonal = (float)Math.hypot(
- mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
- final float radiusRatio =
- mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
- / keyDiagonal;
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " bogus down-move-up event (raidus=%.2f key diagonal) is "
- + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
- mPointerId, radiusRatio,
- lastX, lastY, Keyboard.printableCode(oldKey.mCode),
- x, y, Keyboard.printableCode(key.mCode)));
- }
- onUpEventInternal(eventTime);
- onDownEventInternal(x, y, eventTime);
- } else {
- // 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 (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
- && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
- if (DEBUG_MODE) {
- Log.w(TAG, String.format("[%d] onMoveEvent:"
- + " detected sliding finger while multi touching",
- mPointerId));
- }
- onUpEvent(x, y, eventTime);
- mKeyAlreadyProcessed = true;
- }
- if (!mIsDetectingGesture) {
- mKeyAlreadyProcessed = true;
- }
- setReleasedKeyGraphics(oldKey);
- }
- }
+ processSlidingKeyInput(newKey, x, y, eventTime);
}
- } else {
- if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
- // The pointer has been slid out from the previous key, we must call onRelease() to
- // notify that the previous key has been released.
- setReleasedKeyGraphics(oldKey);
- callListenerOnRelease(oldKey, oldKey.mCode, true);
- startSlidingKeyInput(oldKey);
- mTimerProxy.cancelLongPressTimer();
- if (mIsAllowedSlidingKeyInput) {
- onMoveToNewKey(key, x, y);
- } else {
- if (!mIsDetectingGesture) {
- mKeyAlreadyProcessed = true;
- }
- }
+ } else { // newKey == null
+ if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, newKey)) {
+ slideOutFromOldKey(oldKey, x, y);
}
}
+ mDrawingProxy.showSlidingKeyInputPreview(this);
}
public void onUpEvent(final int x, final int y, final long eventTime) {
@@ -1030,22 +1089,17 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
printTouchEvent("onUpEvent :", x, y, eventTime);
}
- final PointerTrackerQueue queue = sPointerTrackerQueue;
- if (queue != null) {
- if (!sInGesture) {
- 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 (!sInGesture) {
+ if (mCurrentKey != null && mCurrentKey.isModifier()) {
+ // Before processing an up event of modifier key, all pointers already being
+ // tracked should be released.
+ sPointerTrackerQueue.releaseAllPointersExcept(this, eventTime);
+ } else {
+ sPointerTrackerQueue.releaseAllPointersOlderThan(this, eventTime);
}
}
onUpEventInternal(eventTime);
- if (queue != null) {
- queue.remove(this);
- }
+ sPointerTrackerQueue.remove(this);
}
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
@@ -1054,10 +1108,10 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
@Override
public void onPhantomUpEvent(final long eventTime) {
if (DEBUG_EVENT) {
- printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
+ printTouchEvent("onPhntEvent:", mLastX, mLastY, eventTime);
}
onUpEventInternal(eventTime);
- mKeyAlreadyProcessed = true;
+ cancelTracking();
}
private void onUpEventInternal(final long eventTime) {
@@ -1081,7 +1135,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
return;
}
- if (mKeyAlreadyProcessed) {
+ if (mIsTrackingCanceled) {
return;
}
if (currentKey != null && !currentKey.isRepeatable()) {
@@ -1095,13 +1149,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
}
+ @Override
+ public void cancelTracking() {
+ mIsTrackingCanceled = true;
+ }
+
public void onLongPressed() {
- mKeyAlreadyProcessed = true;
+ resetSlidingKeyInput();
+ cancelTracking();
setReleasedKeyGraphics(mCurrentKey);
- final PointerTrackerQueue queue = sPointerTrackerQueue;
- if (queue != null) {
- queue.remove(this);
- }
+ sPointerTrackerQueue.remove(this);
}
public void onCancelEvent(final int x, final int y, final long eventTime) {
@@ -1109,11 +1166,11 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
printTouchEvent("onCancelEvt:", x, y, eventTime);
}
- final PointerTrackerQueue queue = sPointerTrackerQueue;
- if (queue != null) {
- queue.releaseAllPointersExcept(this, eventTime);
- queue.remove(this);
+ if (sInGesture) {
+ cancelBatchInput();
}
+ sPointerTrackerQueue.cancelAllPointerTracker();
+ sPointerTrackerQueue.releaseAllPointers(eventTime);
onCancelEventInternal();
}
@@ -1149,38 +1206,38 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final Key curKey = mCurrentKey;
if (newKey == curKey) {
return false;
- } else if (curKey != null) {
- final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
- mIsInSlidingKeyInputFromModifier);
- final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
- if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
- if (DEBUG_MODE) {
- final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
- / mKeyboard.mMostCommonKeyWidth;
- Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
- +" %.2f key width from key edge",
- mPointerId, distanceToEdgeRatio));
- }
- return true;
+ }
+ if (curKey == null /* && newKey != null */) {
+ return true;
+ }
+ // Here curKey points to the different key from newKey.
+ final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
+ mIsInSlidingKeyInputFromModifier);
+ final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
+ if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
+ if (DEBUG_MODE) {
+ final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
+ / mKeyboard.mMostCommonKeyWidth;
+ Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
+ +" %.2f key width from key edge", mPointerId, distanceToEdgeRatio));
}
- if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
- && sTimeRecorder.isInFastTyping(eventTime)
- && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
- if (DEBUG_MODE) {
- final float keyDiagonal = (float)Math.hypot(
- mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
- final float lengthFromDownRatio =
- mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
- Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
- + " %.2f key diagonal from virtual down point",
- mPointerId, lengthFromDownRatio));
- }
- return true;
+ return true;
+ }
+ if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
+ && sTimeRecorder.isInFastTyping(eventTime)
+ && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
+ if (DEBUG_MODE) {
+ final float keyDiagonal = (float)Math.hypot(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
+ final float lengthFromDownRatio =
+ mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
+ Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
+ + " %.2f key diagonal from virtual down point",
+ mPointerId, lengthFromDownRatio));
}
- return false;
- } else { // curKey == null && newKey != null
return true;
}
+ return false;
}
private void startLongPressTimer(final Key key) {
@@ -1205,6 +1262,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final Key key = mKeyDetector.detectHitKey(x, y);
final String code = KeyDetector.printableCode(key);
Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
- (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
+ (mIsTrackingCanceled ? "-" : " "), title, 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 06a9e9252..b5ba98d85 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard;
import android.graphics.Rect;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
import com.android.inputmethod.latin.Constants;
@@ -26,11 +27,14 @@ import com.android.inputmethod.latin.JniUtils;
import java.util.Arrays;
public final class ProximityInfo {
+ private static final String TAG = ProximityInfo.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
/** 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;
+ private static final float SEARCH_DISTANCE = 1.2f;
private static final Key[] EMPTY_KEY_ARRAY = new Key[0];
private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f;
@@ -95,6 +99,7 @@ public final class ProximityInfo {
JniUtils.loadNativeLibrary();
}
+ // TODO: Stop passing proximityCharsArray
private native long setProximityInfoNative(
String locale, int maxProximityCharsSize, int displayWidth,
int displayHeight, int gridWidth, int gridHeight,
@@ -105,22 +110,56 @@ public final class ProximityInfo {
private native void releaseProximityInfoNative(long nativeProximityInfo);
- private final long createNativeProximityInfo(
- final TouchPositionCorrection touchPositionCorrection) {
+ private static boolean needsProximityInfo(final Key key) {
+ // Don't include special keys into ProximityInfo.
+ return key.mCode >= Constants.CODE_SPACE;
+ }
+
+ private static int getProximityInfoKeysCount(final Key[] keys) {
+ int count = 0;
+ for (final Key key : keys) {
+ if (needsProximityInfo(key)) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private long createNativeProximityInfo(final TouchPositionCorrection touchPositionCorrection) {
final Key[][] gridNeighborKeys = mGridNeighbors;
- final int keyboardWidth = mKeyboardMinWidth;
- final int keyboardHeight = mKeyboardHeight;
- final Key[] keys = mKeys;
final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE);
for (int i = 0; i < mGridSize; ++i) {
final int proximityCharsLength = gridNeighborKeys[i].length;
+ int infoIndex = i * MAX_PROXIMITY_CHARS_SIZE;
for (int j = 0; j < proximityCharsLength; ++j) {
- proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
- gridNeighborKeys[i][j].mCode;
+ final Key neighborKey = gridNeighborKeys[i][j];
+ // Excluding from proximityCharsArray
+ if (!needsProximityInfo(neighborKey)) {
+ continue;
+ }
+ proximityCharsArray[infoIndex] = neighborKey.mCode;
+ infoIndex++;
}
}
- final int keyCount = keys.length;
+ if (DEBUG) {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mGridSize; i++) {
+ sb.setLength(0);
+ for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; j++) {
+ final int code = proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j];
+ if (code == Constants.NOT_A_CODE) {
+ break;
+ }
+ if (sb.length() > 0) sb.append(" ");
+ sb.append(Constants.printableCode(code));
+ }
+ Log.d(TAG, "proxmityChars["+i+"]: " + sb);
+ }
+ }
+
+ final Key[] keys = mKeys;
+ final int keyCount = getProximityInfoKeysCount(keys);
final int[] keyXCoordinates = new int[keyCount];
final int[] keyYCoordinates = new int[keyCount];
final int[] keyWidths = new int[keyCount];
@@ -130,46 +169,73 @@ public final class ProximityInfo {
final float[] sweetSpotCenterYs;
final float[] sweetSpotRadii;
- for (int i = 0; i < keyCount; ++i) {
- final Key key = keys[i];
- keyXCoordinates[i] = key.mX;
- keyYCoordinates[i] = key.mY;
- keyWidths[i] = key.mWidth;
- keyHeights[i] = key.mHeight;
- keyCharCodes[i] = key.mCode;
+ for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) {
+ final Key key = keys[keyIndex];
+ // Excluding from key coordinate arrays
+ if (!needsProximityInfo(key)) {
+ continue;
+ }
+ keyXCoordinates[infoIndex] = key.mX;
+ keyYCoordinates[infoIndex] = key.mY;
+ keyWidths[infoIndex] = key.mWidth;
+ keyHeights[infoIndex] = key.mHeight;
+ keyCharCodes[infoIndex] = key.mCode;
+ infoIndex++;
}
if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
+ if (DEBUG) {
+ Log.d(TAG, "touchPositionCorrection: ON");
+ }
sweetSpotCenterXs = new float[keyCount];
sweetSpotCenterYs = new float[keyCount];
sweetSpotRadii = new float[keyCount];
+ final int rows = touchPositionCorrection.getRows();
final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
* (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight);
- for (int i = 0; i < keyCount; i++) {
- final Key key = keys[i];
+ for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) {
+ final Key key = keys[keyIndex];
+ // Excluding from touch position correction arrays
+ if (!needsProximityInfo(key)) {
+ continue;
+ }
final Rect hitBox = key.mHitBox;
- sweetSpotCenterXs[i] = hitBox.exactCenterX();
- sweetSpotCenterYs[i] = hitBox.exactCenterY();
- sweetSpotRadii[i] = defaultRadius;
+ sweetSpotCenterXs[infoIndex] = hitBox.exactCenterX();
+ sweetSpotCenterYs[infoIndex] = hitBox.exactCenterY();
+ sweetSpotRadii[infoIndex] = defaultRadius;
final int row = hitBox.top / mMostCommonKeyHeight;
- if (row < touchPositionCorrection.getRows()) {
+ if (row < rows) {
final int hitBoxWidth = hitBox.width();
final int hitBoxHeight = hitBox.height();
final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight);
- sweetSpotCenterXs[i] += touchPositionCorrection.getX(row) * hitBoxWidth;
- sweetSpotCenterYs[i] += touchPositionCorrection.getY(row) * hitBoxHeight;
- sweetSpotRadii[i] = touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
+ sweetSpotCenterXs[infoIndex] +=
+ touchPositionCorrection.getX(row) * hitBoxWidth;
+ sweetSpotCenterYs[infoIndex] +=
+ touchPositionCorrection.getY(row) * hitBoxHeight;
+ sweetSpotRadii[infoIndex] =
+ touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
}
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ " [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", infoIndex, row,
+ sweetSpotCenterXs[infoIndex], sweetSpotCenterYs[infoIndex],
+ sweetSpotRadii[infoIndex], (row < rows ? "correct" : "default"),
+ Constants.printableCode(key.mCode)));
+ }
+ infoIndex++;
}
} else {
sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
+ if (DEBUG) {
+ Log.d(TAG, "touchPositionCorrection: OFF");
+ }
}
+ // TODO: Stop passing proximityCharsArray
return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE,
- keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth,
- proximityCharsArray,
- keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
- sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+ mKeyboardMinWidth, mKeyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth,
+ proximityCharsArray, keyCount, keyXCoordinates, keyYCoordinates, keyWidths,
+ keyHeights, keyCharCodes, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
}
public long getNativeProximityInfo() {
@@ -221,7 +287,7 @@ public final class ProximityInfo {
return;
}
int index = 0;
- if (primaryKeyCode > Keyboard.CODE_SPACE) {
+ if (primaryKeyCode > Constants.CODE_SPACE) {
dest[index++] = primaryKeyCode;
}
final Key[] nearestKeys = getNearestKeys(x, y);
@@ -230,7 +296,7 @@ public final class ProximityInfo {
break;
}
final int code = key.mCode;
- if (code <= Keyboard.CODE_SPACE) {
+ if (code <= Constants.CODE_SPACE) {
break;
}
dest[index++] = code;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
index 699aaeaef..3a57f673a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -19,7 +19,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.SystemClock;
import com.android.inputmethod.latin.Constants;
@@ -118,98 +117,7 @@ final class GesturePreviewTrail {
/ params.mTrailLingerDuration, 0.0f);
}
- static final class WorkingSet {
- // Input
- // Previous point (P1) coordinates and trail radius.
- public float p1x, p1y;
- public float r1;
- // Current point (P2) coordinates and trail radius.
- public float p2x, p2y;
- public float r2;
-
- // Output
- // Closing point of arc at P1.
- public float p1ax, p1ay;
- // Opening point of arc at P1.
- public float p1bx, p1by;
- // Opening point of arc at P2.
- public float p2ax, p2ay;
- // Closing point of arc at P2.
- public float p2bx, p2by;
- // Start angle of the trail arcs.
- public float aa;
- // Sweep angle of the trail arc at P1.
- public float a1;
- public RectF arc1 = new RectF();
- // Sweep angle of the trail arc at P2.
- public float a2;
- public RectF arc2 = new RectF();
- }
-
- private static final float RIGHT_ANGLE = (float)(Math.PI / 2.0d);
- private static final float RADIAN_TO_DEGREE = (float)(180.0d / Math.PI);
-
- private static boolean calculatePathPoints(final WorkingSet w) {
- final float dx = w.p2x - w.p1x;
- final float dy = w.p2y - w.p1y;
- // Distance of the points.
- final double l = Math.hypot(dx, dy);
- if (Double.compare(0.0d, l) == 0) {
- return false;
- }
- // Angle of the line p1-p2
- final float a = (float)Math.atan2(dy, dx);
- // Difference of trail cap radius.
- final float dr = w.r2 - w.r1;
- // Variation of angle at trail cap.
- final float ar = (float)Math.asin(dr / l);
- // The start angle of trail cap arc at P1.
- final float aa = a - (RIGHT_ANGLE + ar);
- // The end angle of trail cap arc at P2.
- final float ab = a + (RIGHT_ANGLE + ar);
- final float cosa = (float)Math.cos(aa);
- final float sina = (float)Math.sin(aa);
- final float cosb = (float)Math.cos(ab);
- final float sinb = (float)Math.sin(ab);
- w.p1ax = w.p1x + w.r1 * cosa;
- w.p1ay = w.p1y + w.r1 * sina;
- w.p1bx = w.p1x + w.r1 * cosb;
- w.p1by = w.p1y + w.r1 * sinb;
- w.p2ax = w.p2x + w.r2 * cosa;
- w.p2ay = w.p2y + w.r2 * sina;
- w.p2bx = w.p2x + w.r2 * cosb;
- w.p2by = w.p2y + w.r2 * sinb;
- w.aa = aa * RADIAN_TO_DEGREE;
- final float ar2degree = ar * 2.0f * RADIAN_TO_DEGREE;
- w.a1 = -180.0f + ar2degree;
- w.a2 = 180.0f + ar2degree;
- w.arc1.set(w.p1x, w.p1y, w.p1x, w.p1y);
- w.arc1.inset(-w.r1, -w.r1);
- w.arc2.set(w.p2x, w.p2y, w.p2x, w.p2y);
- w.arc2.inset(-w.r2, -w.r2);
- return true;
- }
-
- private static void createPath(final Path path, final WorkingSet w) {
- path.rewind();
- // Trail cap at P1.
- path.moveTo(w.p1x, w.p1y);
- path.arcTo(w.arc1, w.aa, w.a1);
- // Trail cap at P2.
- path.moveTo(w.p2x, w.p2y);
- path.arcTo(w.arc2, w.aa, w.a2);
- // Two trapezoids connecting P1 and P2.
- path.moveTo(w.p1ax, w.p1ay);
- path.lineTo(w.p1x, w.p1y);
- path.lineTo(w.p1bx, w.p1by);
- path.lineTo(w.p2bx, w.p2by);
- path.lineTo(w.p2x, w.p2y);
- path.lineTo(w.p2ax, w.p2ay);
- path.close();
- }
-
- private final WorkingSet mWorkingSet = new WorkingSet();
- private final Path mPath = new Path();
+ private final RoundedLine mRoundedLine = new RoundedLine();
/**
* Draw gesture preview trail
@@ -243,36 +151,35 @@ final class GesturePreviewTrail {
if (startIndex < trailSize) {
paint.setColor(params.mTrailColor);
paint.setStyle(Paint.Style.FILL);
- final Path path = mPath;
- final WorkingSet w = mWorkingSet;
- w.p1x = getXCoordValue(xCoords[startIndex]);
- w.p1y = yCoords[startIndex];
+ final RoundedLine line = mRoundedLine;
+ int p1x = getXCoordValue(xCoords[startIndex]);
+ int p1y = yCoords[startIndex];
int lastTime = sinceDown - eventTimes[startIndex];
float maxWidth = getWidth(lastTime, params);
- w.r1 = maxWidth / 2.0f;
+ float r1 = maxWidth / 2.0f;
// Initialize bounds rectangle.
- outBoundsRect.set((int)w.p1x, (int)w.p1y, (int)w.p1x, (int)w.p1y);
- for (int i = startIndex + 1; i < trailSize - 1; i++) {
+ outBoundsRect.set(p1x, p1y, p1x, p1y);
+ for (int i = startIndex + 1; i < trailSize; i++) {
final int elapsedTime = sinceDown - eventTimes[i];
- w.p2x = getXCoordValue(xCoords[i]);
- w.p2y = yCoords[i];
+ final int p2x = getXCoordValue(xCoords[i]);
+ final int p2y = yCoords[i];
+ final float width = getWidth(elapsedTime, params);
+ final float r2 = width / 2.0f;
// Draw trail line only when the current point isn't a down point.
if (!isDownEventXCoord(xCoords[i])) {
final int alpha = getAlpha(elapsedTime, params);
paint.setAlpha(alpha);
- final float width = getWidth(elapsedTime, params);
- w.r2 = width / 2.0f;
- if (calculatePathPoints(w)) {
- createPath(path, w);
+ final Path path = line.makePath(p1x, p1y, r1, p2x, p2y, r2);
+ if (path != null) {
canvas.drawPath(path, paint);
- outBoundsRect.union((int)w.p2x, (int)w.p2y);
+ outBoundsRect.union(p2x, p2y);
}
// Take union for the bounds.
maxWidth = Math.max(maxWidth, width);
}
- w.p1x = w.p2x;
- w.p1y = w.p2y;
- w.r1 = w.r2;
+ p1x = p2x;
+ p1y = p2y;
+ r1 = r2;
lastTime = elapsedTime;
}
// Take care of trail line width.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
index f8244dd5b..a43e94a75 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -27,6 +27,10 @@ public class GestureStroke {
private static final boolean DEBUG = false;
private static final boolean DEBUG_SPEED = false;
+ // The height of extra area above the keyboard to draw gesture trails.
+ // Proportional to the keyboard height.
+ public static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
+
public static final int DEFAULT_CAPACITY = 128;
private final int mPointerId;
@@ -37,6 +41,8 @@ public class GestureStroke {
private final GestureStrokeParams mParams;
private int mKeyWidth; // pixel
+ private int mMinYCoordinate; // pixel
+ private int mMaxYCoordinate; // pixel
// Static threshold for starting gesture detection
private int mDetectFastMoveSpeedThreshold; // pixel /sec
private int mDetectFastMoveTime;
@@ -135,8 +141,10 @@ public class GestureStroke {
mParams = params;
}
- public void setKeyboardGeometry(final int keyWidth) {
+ public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
mKeyWidth = keyWidth;
+ mMinYCoordinate = -(int)(keyboardHeight * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
+ mMaxYCoordinate = keyboardHeight - 1;
// TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
mGestureDynamicDistanceThresholdFrom =
@@ -167,7 +175,7 @@ public class GestureStroke {
elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
}
final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
- addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
+ addPointOnKeyboard(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
}
private int getGestureDynamicDistanceThreshold(final int deltaTime) {
@@ -220,6 +228,17 @@ public class GestureStroke {
return isStartOfAGesture;
}
+ public void duplicateLastPointWith(final int time) {
+ final int lastIndex = mEventTimes.getLength() - 1;
+ if (lastIndex >= 0) {
+ final int x = mXCoordinates.get(lastIndex);
+ final int y = mYCoordinates.get(lastIndex);
+ // TODO: Have appendMajorPoint()
+ appendPoint(x, y, time);
+ updateIncrementalRecognitionSize(x, y, time);
+ }
+ }
+
protected void reset() {
mIncrementalRecognitionSize = 0;
mLastIncrementalBatchSize = 0;
@@ -277,7 +296,17 @@ public class GestureStroke {
return dist;
}
- public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
+ /**
+ * Add a touch event as a gesture point. Returns true if the touch event is on the valid
+ * gesture area.
+ * @param x the x-coordinate of the touch event
+ * @param y the y-coordinate of the touch event
+ * @param time the elapsed time in millisecond from the first gesture down
+ * @param isMajorEvent false if this is a historical move event
+ * @return true if the touch event is on the valid gesture area
+ */
+ public boolean addPointOnKeyboard(final int x, final int y, final int time,
+ final boolean isMajorEvent) {
final int size = mEventTimes.getLength();
if (size <= 0) {
// Down event
@@ -293,6 +322,7 @@ public class GestureStroke {
updateIncrementalRecognitionSize(x, y, time);
updateMajorEvent(x, y, time);
}
+ return y >= mMinYCoordinate && y < mMaxYCoordinate;
}
private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
index 8192c9076..7ab7e9aad 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -56,8 +56,8 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
}
@Override
- public void setKeyboardGeometry(final int keyWidth) {
- super.setKeyboardGeometry(keyWidth);
+ public void setKeyboardGeometry(final int keyWidth, final int keyboardHeight) {
+ super.setKeyboardGeometry(keyWidth, keyboardHeight);
final float sampleLength = keyWidth * MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH;
mMinPreviewSampleLengthSquare = (int)(sampleLength * sampleLength);
}
@@ -69,8 +69,9 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
}
@Override
- public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
- super.addPoint(x, y, time, isMajorEvent);
+ public boolean addPointOnKeyboard(final int x, final int y, final int time,
+ final boolean isMajorEvent) {
+ final boolean onValidArea = super.addPointOnKeyboard(x, y, time, isMajorEvent);
if (isMajorEvent || needsSampling(x, y)) {
mPreviewEventTimes.add(time);
mPreviewXCoordinates.add(x);
@@ -78,6 +79,7 @@ public final class GestureStrokeWithPreviewPoints extends GestureStroke {
mLastX = x;
mLastY = y;
}
+ return onValidArea;
}
public void appendPreviewStroke(final ResizableIntArray eventTimes,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index 2caa5eb02..9f6e2f37e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -16,12 +16,13 @@
package com.android.inputmethod.keyboard.internal;
-import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED;
+import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
+import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.StringUtils;
@@ -172,7 +173,7 @@ public final class KeySpecParser {
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec);
}
- return parseCode(moreKeySpec.substring(end + 1), codesSet, Keyboard.CODE_UNSPECIFIED);
+ return parseCode(moreKeySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED);
}
final String outputText = getOutputTextInternal(moreKeySpec);
if (outputText != null) {
@@ -181,14 +182,14 @@ public final class KeySpecParser {
if (StringUtils.codePointCount(outputText) == 1) {
return outputText.codePointAt(0);
}
- return Keyboard.CODE_OUTPUT_TEXT;
+ return CODE_OUTPUT_TEXT;
}
final String label = getLabel(moreKeySpec);
// Code is automatically generated for one letter label.
if (StringUtils.codePointCount(label) == 1) {
return label.codePointAt(0);
}
- return Keyboard.CODE_OUTPUT_TEXT;
+ return CODE_OUTPUT_TEXT;
}
public static int parseCode(final String text, final KeyboardCodesSet codesSet,
@@ -468,7 +469,7 @@ public final class KeySpecParser {
public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
final Locale locale) {
- if (!Keyboard.isLetterCode(code) || !needsToUpperCase) return code;
+ if (!Constants.isLetterCode(code) || !needsToUpperCase) return code;
final String text = new String(new int[] { code } , 0, 1);
final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
text, needsToUpperCase, locale);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index b314a3795..36342688e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -27,6 +27,7 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.InflateException;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
@@ -177,7 +178,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
return this;
}
- // For test only
+ @UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mTouchPositionCorrection.setEnabled(false);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 840d7133d..428e31ccd 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -16,8 +16,8 @@
package com.android.inputmethod.keyboard.internal;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import java.util.HashMap;
@@ -74,21 +74,21 @@ public final class KeyboardCodesSet {
private static final int CODE_RIGHT_CURLY_BRACKET = '}';
private static final int[] DEFAULT = {
- Keyboard.CODE_TAB,
- Keyboard.CODE_ENTER,
- Keyboard.CODE_SPACE,
- Keyboard.CODE_SHIFT,
- Keyboard.CODE_SWITCH_ALPHA_SYMBOL,
- Keyboard.CODE_OUTPUT_TEXT,
- Keyboard.CODE_DELETE,
- Keyboard.CODE_SETTINGS,
- Keyboard.CODE_SHORTCUT,
- Keyboard.CODE_ACTION_ENTER,
- Keyboard.CODE_ACTION_NEXT,
- Keyboard.CODE_ACTION_PREVIOUS,
- Keyboard.CODE_LANGUAGE_SWITCH,
- Keyboard.CODE_RESEARCH,
- Keyboard.CODE_UNSPECIFIED,
+ Constants.CODE_TAB,
+ Constants.CODE_ENTER,
+ Constants.CODE_SPACE,
+ Constants.CODE_SHIFT,
+ Constants.CODE_SWITCH_ALPHA_SYMBOL,
+ Constants.CODE_OUTPUT_TEXT,
+ Constants.CODE_DELETE,
+ Constants.CODE_SETTINGS,
+ Constants.CODE_SHORTCUT,
+ Constants.CODE_ACTION_ENTER,
+ Constants.CODE_ACTION_NEXT,
+ Constants.CODE_ACTION_PREVIOUS,
+ Constants.CODE_LANGUAGE_SWITCH,
+ Constants.CODE_RESEARCH,
+ Constants.CODE_UNSPECIFIED,
CODE_LEFT_PARENTHESIS,
CODE_RIGHT_PARENTHESIS,
CODE_LESS_THAN_SIGN,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index e6fe50e02..642e1a18c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -19,9 +19,9 @@ package com.android.inputmethod.keyboard.internal;
import android.util.SparseIntArray;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import java.util.ArrayList;
import java.util.TreeSet;
@@ -89,7 +89,7 @@ public class KeyboardParams {
mKeys.add(key);
updateHistogram(key);
}
- if (key.mCode == Keyboard.CODE_SHIFT) {
+ if (key.mCode == Constants.CODE_SHIFT) {
mShiftKeys.add(key);
}
if (key.altCodeWhileTyping()) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 631e647e8..25a1c6a00 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
import android.util.Log;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.Constants;
/**
@@ -316,12 +315,12 @@ public final class KeyboardState {
public void onPressKey(int code, boolean isSinglePointer, int autoCaps) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onPressKey: code=" + Keyboard.printableCode(code)
+ Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code)
+ " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this);
}
- if (code == Keyboard.CODE_SHIFT) {
+ if (code == Constants.CODE_SHIFT) {
onPressShift();
- } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
onPressSymbol();
} else {
mSwitchActions.cancelDoubleTapTimer();
@@ -349,12 +348,12 @@ public final class KeyboardState {
public void onReleaseKey(int code, boolean withSliding) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onReleaseKey: code=" + Keyboard.printableCode(code)
+ Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code)
+ " sliding=" + withSliding + " " + this);
}
- if (code == Keyboard.CODE_SHIFT) {
+ if (code == Constants.CODE_SHIFT) {
onReleaseShift(withSliding);
- } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ } else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
onReleaseSymbol(withSliding);
}
}
@@ -381,9 +380,9 @@ public final class KeyboardState {
public void onLongPressTimeout(int code) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " + this);
+ Log.d(TAG, "onLongPressTimeout: code=" + Constants.printableCode(code) + " " + this);
}
- if (mIsAlphabetMode && code == Keyboard.CODE_SHIFT) {
+ if (mIsAlphabetMode && code == Constants.CODE_SHIFT) {
mLongPressShiftLockFired = true;
mSwitchActions.hapticAndAudioFeedback(code);
}
@@ -459,7 +458,7 @@ public final class KeyboardState {
setShifted(MANUAL_SHIFT);
mShiftKeyState.onPress();
}
- mSwitchActions.startLongPressTimer(Keyboard.CODE_SHIFT);
+ mSwitchActions.startLongPressTimer(Constants.CODE_SHIFT);
}
} else {
// In symbol mode, just toggle symbol and symbol more keyboard.
@@ -544,7 +543,7 @@ public final class KeyboardState {
}
private static boolean isSpaceCharacter(int c) {
- return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
+ return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
}
private boolean isLayoutSwitchBackCharacter(int c) {
@@ -555,14 +554,14 @@ public final class KeyboardState {
public void onCodeInput(int code, boolean isSinglePointer, int autoCaps) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onCodeInput: code=" + Keyboard.printableCode(code)
+ Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code)
+ " single=" + isSinglePointer
+ " autoCaps=" + autoCaps + " " + this);
}
switch (mSwitchState) {
case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
- if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
+ if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
// Detected only the mode change key has been pressed, and then released.
if (mIsAlphabetMode) {
mSwitchState = SWITCH_STATE_ALPHA;
@@ -578,7 +577,7 @@ public final class KeyboardState {
}
break;
case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
- if (code == Keyboard.CODE_SHIFT) {
+ if (code == Constants.CODE_SHIFT) {
// Detected only the shift key has been pressed on symbol layout, and then released.
mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
} else if (isSinglePointer) {
@@ -589,8 +588,8 @@ public final class KeyboardState {
}
break;
case SWITCH_STATE_SYMBOL_BEGIN:
- if (!isSpaceCharacter(code) && (Keyboard.isLetterCode(code)
- || code == Keyboard.CODE_OUTPUT_TEXT)) {
+ if (!isSpaceCharacter(code) && (Constants.isLetterCode(code)
+ || code == Constants.CODE_OUTPUT_TEXT)) {
mSwitchState = SWITCH_STATE_SYMBOL;
}
// Switch back to alpha keyboard mode immediately if user types one of the switch back
@@ -611,7 +610,7 @@ public final class KeyboardState {
}
// If the code is a letter, update keyboard shift state.
- if (Keyboard.isLetterCode(code)) {
+ if (Constants.isLetterCode(code)) {
updateAlphabetShiftState(autoCaps);
}
}
@@ -639,7 +638,7 @@ public final class KeyboardState {
@Override
public String toString() {
return "[keyboard=" + (mIsAlphabetMode ? mAlphabetShiftState.toString()
- : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS"))
+ : (mIsSymbolShifted ? "SYMBOLS_SHIFTED" : "SYMBOLS"))
+ " shift=" + mShiftKeyState
+ " symbol=" + mSymbolKeyState
+ " switch=" + switchStateToString(mSwitchState) + "]";
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 3bb272f8c..9d0564315 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
@@ -64,7 +65,7 @@ public final class KeyboardTextsSet {
loadStringResourcesInternal(context, RESOURCE_NAMES, R.string.english_ime_name);
}
- /* package for test */
+ @UsedForTesting
void loadStringResourcesInternal(Context context, final String[] resourceNames,
int referenceId) {
final Resources res = context.getResources();
@@ -150,89 +151,88 @@ public final class KeyboardTextsSet {
/* 43 */ "more_keys_for_double_quote",
/* 44 */ "more_keys_for_tablet_double_quote",
/* 45 */ "more_keys_for_currency_dollar",
- /* 46 */ "more_keys_for_currency_euro",
- /* 47 */ "more_keys_for_currency_pound",
- /* 48 */ "more_keys_for_currency_general",
- /* 49 */ "more_keys_for_punctuation",
- /* 50 */ "more_keys_for_star",
- /* 51 */ "more_keys_for_bullet",
- /* 52 */ "more_keys_for_plus",
- /* 53 */ "more_keys_for_left_parenthesis",
- /* 54 */ "more_keys_for_right_parenthesis",
- /* 55 */ "more_keys_for_less_than",
- /* 56 */ "more_keys_for_greater_than",
- /* 57 */ "more_keys_for_arabic_diacritics",
- /* 58 */ "keyhintlabel_for_arabic_diacritics",
- /* 59 */ "keylabel_for_symbols_1",
- /* 60 */ "keylabel_for_symbols_2",
- /* 61 */ "keylabel_for_symbols_3",
- /* 62 */ "keylabel_for_symbols_4",
- /* 63 */ "keylabel_for_symbols_5",
- /* 64 */ "keylabel_for_symbols_6",
- /* 65 */ "keylabel_for_symbols_7",
- /* 66 */ "keylabel_for_symbols_8",
- /* 67 */ "keylabel_for_symbols_9",
- /* 68 */ "keylabel_for_symbols_0",
- /* 69 */ "additional_more_keys_for_symbols_1",
- /* 70 */ "additional_more_keys_for_symbols_2",
- /* 71 */ "additional_more_keys_for_symbols_3",
- /* 72 */ "additional_more_keys_for_symbols_4",
- /* 73 */ "additional_more_keys_for_symbols_5",
- /* 74 */ "additional_more_keys_for_symbols_6",
- /* 75 */ "additional_more_keys_for_symbols_7",
- /* 76 */ "additional_more_keys_for_symbols_8",
- /* 77 */ "additional_more_keys_for_symbols_9",
- /* 78 */ "additional_more_keys_for_symbols_0",
- /* 79 */ "more_keys_for_symbols_1",
- /* 80 */ "more_keys_for_symbols_2",
- /* 81 */ "more_keys_for_symbols_3",
- /* 82 */ "more_keys_for_symbols_4",
- /* 83 */ "more_keys_for_symbols_5",
- /* 84 */ "more_keys_for_symbols_6",
- /* 85 */ "more_keys_for_symbols_7",
- /* 86 */ "more_keys_for_symbols_8",
- /* 87 */ "more_keys_for_symbols_9",
- /* 88 */ "more_keys_for_symbols_0",
- /* 89 */ "keylabel_for_comma",
- /* 90 */ "more_keys_for_comma",
- /* 91 */ "keylabel_for_symbols_question",
- /* 92 */ "keylabel_for_symbols_semicolon",
- /* 93 */ "keylabel_for_symbols_percent",
- /* 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_q",
- /* 107 */ "more_keys_for_x",
- /* 108 */ "keylabel_for_q",
- /* 109 */ "keylabel_for_w",
- /* 110 */ "keylabel_for_y",
- /* 111 */ "keylabel_for_x",
- /* 112 */ "keylabel_for_spanish_row2_10",
- /* 113 */ "more_keys_for_am_pm",
- /* 114 */ "settings_as_more_key",
- /* 115 */ "shortcut_as_more_key",
- /* 116 */ "action_next_as_more_key",
- /* 117 */ "action_previous_as_more_key",
- /* 118 */ "label_to_more_symbol_key",
- /* 119 */ "label_to_more_symbol_for_tablet_key",
- /* 120 */ "label_tab_key",
- /* 121 */ "label_to_phone_numeric_key",
- /* 122 */ "label_to_phone_symbols_key",
- /* 123 */ "label_time_am",
- /* 124 */ "label_time_pm",
- /* 125 */ "label_to_symbol_key_pcqwerty",
- /* 126 */ "keylabel_for_popular_domain",
- /* 127 */ "more_keys_for_popular_domain",
- /* 128 */ "more_keys_for_smiley",
+ /* 46 */ "keylabel_for_currency_generic",
+ /* 47 */ "more_keys_for_currency_generic",
+ /* 48 */ "more_keys_for_punctuation",
+ /* 49 */ "more_keys_for_star",
+ /* 50 */ "more_keys_for_bullet",
+ /* 51 */ "more_keys_for_plus",
+ /* 52 */ "more_keys_for_left_parenthesis",
+ /* 53 */ "more_keys_for_right_parenthesis",
+ /* 54 */ "more_keys_for_less_than",
+ /* 55 */ "more_keys_for_greater_than",
+ /* 56 */ "more_keys_for_arabic_diacritics",
+ /* 57 */ "keyhintlabel_for_arabic_diacritics",
+ /* 58 */ "keylabel_for_symbols_1",
+ /* 59 */ "keylabel_for_symbols_2",
+ /* 60 */ "keylabel_for_symbols_3",
+ /* 61 */ "keylabel_for_symbols_4",
+ /* 62 */ "keylabel_for_symbols_5",
+ /* 63 */ "keylabel_for_symbols_6",
+ /* 64 */ "keylabel_for_symbols_7",
+ /* 65 */ "keylabel_for_symbols_8",
+ /* 66 */ "keylabel_for_symbols_9",
+ /* 67 */ "keylabel_for_symbols_0",
+ /* 68 */ "additional_more_keys_for_symbols_1",
+ /* 69 */ "additional_more_keys_for_symbols_2",
+ /* 70 */ "additional_more_keys_for_symbols_3",
+ /* 71 */ "additional_more_keys_for_symbols_4",
+ /* 72 */ "additional_more_keys_for_symbols_5",
+ /* 73 */ "additional_more_keys_for_symbols_6",
+ /* 74 */ "additional_more_keys_for_symbols_7",
+ /* 75 */ "additional_more_keys_for_symbols_8",
+ /* 76 */ "additional_more_keys_for_symbols_9",
+ /* 77 */ "additional_more_keys_for_symbols_0",
+ /* 78 */ "more_keys_for_symbols_1",
+ /* 79 */ "more_keys_for_symbols_2",
+ /* 80 */ "more_keys_for_symbols_3",
+ /* 81 */ "more_keys_for_symbols_4",
+ /* 82 */ "more_keys_for_symbols_5",
+ /* 83 */ "more_keys_for_symbols_6",
+ /* 84 */ "more_keys_for_symbols_7",
+ /* 85 */ "more_keys_for_symbols_8",
+ /* 86 */ "more_keys_for_symbols_9",
+ /* 87 */ "more_keys_for_symbols_0",
+ /* 88 */ "keylabel_for_comma",
+ /* 89 */ "more_keys_for_comma",
+ /* 90 */ "keylabel_for_symbols_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_q",
+ /* 106 */ "more_keys_for_x",
+ /* 107 */ "keylabel_for_q",
+ /* 108 */ "keylabel_for_w",
+ /* 109 */ "keylabel_for_y",
+ /* 110 */ "keylabel_for_x",
+ /* 111 */ "keylabel_for_spanish_row2_10",
+ /* 112 */ "more_keys_for_am_pm",
+ /* 113 */ "settings_as_more_key",
+ /* 114 */ "shortcut_as_more_key",
+ /* 115 */ "action_next_as_more_key",
+ /* 116 */ "action_previous_as_more_key",
+ /* 117 */ "label_to_more_symbol_key",
+ /* 118 */ "label_to_more_symbol_for_tablet_key",
+ /* 119 */ "label_tab_key",
+ /* 120 */ "label_to_phone_numeric_key",
+ /* 121 */ "label_to_phone_symbols_key",
+ /* 122 */ "label_time_am",
+ /* 123 */ "label_time_pm",
+ /* 124 */ "label_to_symbol_key_pcqwerty",
+ /* 125 */ "keylabel_for_popular_domain",
+ /* 126 */ "more_keys_for_popular_domain",
+ /* 127 */ "more_keys_for_smiley",
};
private static final String EMPTY = "";
@@ -258,26 +258,25 @@ public final class KeyboardTextsSet {
// U+00A5: "¥" YEN SIGN
// U+20B1: "₱" PESO SIGN
/* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- /* 46 */ "\u00A2,\u00A3,$,\u00A5,\u20B1",
- /* 47 */ "\u00A2,$,\u20AC,\u00A5,\u20B1",
- /* 48 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1",
- /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
+ /* 46 */ "$",
+ /* 47 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
+ /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
// U+2020: "†" DAGGER
// U+2021: "‡" DOUBLE DAGGER
// U+2605: "★" BLACK STAR
- /* 50 */ "\u2020,\u2021,\u2605",
+ /* 49 */ "\u2020,\u2021,\u2605",
// U+266A: "♪" EIGHTH NOTE
// U+2665: "♥" BLACK HEART SUIT
// U+2660: "♠" BLACK SPADE SUIT
// U+2666: "♦" BLACK DIAMOND SUIT
// U+2663: "♣" BLACK CLUB SUIT
- /* 51 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
+ /* 50 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
// U+00B1: "±" PLUS-MINUS SIGN
- /* 52 */ "\u00B1",
+ /* 51 */ "\u00B1",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 53 */ "!fixedColumnOrder!3,<,{,[",
- /* 54 */ "!fixedColumnOrder!3,>,},]",
+ /* 52 */ "!fixedColumnOrder!3,<,{,[",
+ /* 53 */ "!fixedColumnOrder!3,>,},]",
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
// U+2264: "≤" LESS-THAN OR EQUAL TO
@@ -293,103 +292,103 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 55 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
- /* 56 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
+ /* 54 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
+ /* 55 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
+ /* 56 */ EMPTY,
/* 57 */ EMPTY,
- /* 58 */ EMPTY,
- /* 59 */ "1",
- /* 60 */ "2",
- /* 61 */ "3",
- /* 62 */ "4",
- /* 63 */ "5",
- /* 64 */ "6",
- /* 65 */ "7",
- /* 66 */ "8",
- /* 67 */ "9",
- /* 68 */ "0",
- /* 69~ */
+ /* 58 */ "1",
+ /* 59 */ "2",
+ /* 60 */ "3",
+ /* 61 */ "4",
+ /* 62 */ "5",
+ /* 63 */ "6",
+ /* 64 */ "7",
+ /* 65 */ "8",
+ /* 66 */ "9",
+ /* 67 */ "0",
+ /* 68~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~78 */
+ /* ~77 */
// U+00B9: "¹" SUPERSCRIPT ONE
// U+00BD: "½" VULGAR FRACTION ONE HALF
// U+2153: "⅓" VULGAR FRACTION ONE THIRD
// U+00BC: "¼" VULGAR FRACTION ONE QUARTER
// U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
- /* 79 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
+ /* 78 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
// U+00B2: "²" SUPERSCRIPT TWO
// U+2154: "⅔" VULGAR FRACTION TWO THIRDS
- /* 80 */ "\u00B2,\u2154",
+ /* 79 */ "\u00B2,\u2154",
// U+00B3: "³" SUPERSCRIPT THREE
// U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
// U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
- /* 81 */ "\u00B3,\u00BE,\u215C",
+ /* 80 */ "\u00B3,\u00BE,\u215C",
// U+2074: "⁴" SUPERSCRIPT FOUR
- /* 82 */ "\u2074",
+ /* 81 */ "\u2074",
// U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
- /* 83 */ "\u215D",
- /* 84 */ EMPTY,
+ /* 82 */ "\u215D",
+ /* 83 */ EMPTY,
// U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
- /* 85 */ "\u215E",
+ /* 84 */ "\u215E",
+ /* 85 */ EMPTY,
/* 86 */ EMPTY,
- /* 87 */ EMPTY,
// U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
// U+2205: "∅" EMPTY SET
- /* 88 */ "\u207F,\u2205",
- /* 89 */ ",",
- /* 90 */ EMPTY,
- /* 91 */ "?",
- /* 92 */ ";",
- /* 93 */ "%",
+ /* 87 */ "\u207F,\u2205",
+ /* 88 */ ",",
+ /* 89 */ EMPTY,
+ /* 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 */ "\"",
+ /* 105 */ EMPTY,
/* 106 */ EMPTY,
- /* 107 */ EMPTY,
- /* 108 */ "q",
- /* 109 */ "w",
- /* 110 */ "y",
- /* 111 */ "x",
+ /* 107 */ "q",
+ /* 108 */ "w",
+ /* 109 */ "y",
+ /* 110 */ "x",
// U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- /* 112 */ "\u00F1",
- /* 113 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
- /* 114 */ "!icon/settings_key|!code/key_settings",
- /* 115 */ "!icon/shortcut_key|!code/key_shortcut",
- /* 116 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
- /* 117 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
+ /* 111 */ "\u00F1",
+ /* 112 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
+ /* 113 */ "!icon/settings_key|!code/key_settings",
+ /* 114 */ "!icon/shortcut_key|!code/key_shortcut",
+ /* 115 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
+ /* 116 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
- /* 118 */ "= \\ <",
+ /* 117 */ "= \\ <",
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
- /* 119 */ "~ \\ {",
+ /* 118 */ "~ \\ {",
// Label for "Tab" key. Must be short to fit on key!
- /* 120 */ "Tab",
+ /* 119 */ "Tab",
// Label for "switch to phone numeric" key. Must be short to fit on key!
- /* 121 */ "123",
+ /* 120 */ "123",
// Label for "switch to phone symbols" key. Must be short to fit on key!
// U+FF0A: "*" FULLWIDTH ASTERISK
// U+FF03: "#" FULLWIDTH NUMBER SIGN
- /* 122 */ "\uFF0A\uFF03",
+ /* 121 */ "\uFF0A\uFF03",
// Key label for "ante meridiem"
- /* 123 */ "AM",
+ /* 122 */ "AM",
// Key label for "post meridiem"
- /* 124 */ "PM",
+ /* 123 */ "PM",
// Label for "switch to symbols" key on PC QWERTY layout
- /* 125 */ "Sym",
- /* 126 */ ".com",
+ /* 124 */ "Sym",
+ /* 125 */ ".com",
// popular web domains for the locale - most popular, displayed on the keyboard
- /* 127 */ "!hasLabels!,.net,.org,.gov,.edu",
- /* 128 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+ /* 126 */ "!hasLabels!,.net,.org,.gov,.edu",
+ /* 127 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
};
/* Language af: Afrikaans */
@@ -458,25 +457,31 @@ public final class KeyboardTextsSet {
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
/* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 45~ */
- null, null, null, null,
- /* ~48 */
+ // U+00A2: "¢" CENT SIGN
+ // U+00A3: "£" POUND SIGN
+ // U+20AC: "€" EURO SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+20B1: "₱" PESO SIGN
+ // U+FDFC: "﷼" RIAL SIGN
+ /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1,\uFDFC",
+ /* 46 */ null,
+ /* 47 */ null,
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 50 */ "\u2605,\u066D",
+ /* 49 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 51 */ "\u266A",
- /* 52 */ null,
+ /* 50 */ "\u266A",
+ /* 51 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -492,8 +497,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0654: "ٔ" ARABIC HAMZA ABOVE
// U+0652: "ْ" ARABIC SUKUN
@@ -509,64 +514,64 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 57 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
- /* 58 */ "\u0651",
+ /* 56 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 57 */ "\u0651",
// U+0661: "١" ARABIC-INDIC DIGIT ONE
- /* 59 */ "\u0661",
+ /* 58 */ "\u0661",
// U+0662: "٢" ARABIC-INDIC DIGIT TWO
- /* 60 */ "\u0662",
+ /* 59 */ "\u0662",
// U+0663: "٣" ARABIC-INDIC DIGIT THREE
- /* 61 */ "\u0663",
+ /* 60 */ "\u0663",
// U+0664: "٤" ARABIC-INDIC DIGIT FOUR
- /* 62 */ "\u0664",
+ /* 61 */ "\u0664",
// U+0665: "٥" ARABIC-INDIC DIGIT FIVE
- /* 63 */ "\u0665",
+ /* 62 */ "\u0665",
// U+0666: "٦" ARABIC-INDIC DIGIT SIX
- /* 64 */ "\u0666",
+ /* 63 */ "\u0666",
// U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
- /* 65 */ "\u0667",
+ /* 64 */ "\u0667",
// U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
- /* 66 */ "\u0668",
+ /* 65 */ "\u0668",
// U+0669: "٩" ARABIC-INDIC DIGIT NINE
- /* 67 */ "\u0669",
+ /* 66 */ "\u0669",
// U+0660: "٠" ARABIC-INDIC DIGIT ZERO
- /* 68 */ "\u0660",
- /* 69 */ "1",
- /* 70 */ "2",
- /* 71 */ "3",
- /* 72 */ "4",
- /* 73 */ "5",
- /* 74 */ "6",
- /* 75 */ "7",
- /* 76 */ "8",
- /* 77 */ "9",
+ /* 67 */ "\u0660",
+ /* 68 */ "1",
+ /* 69 */ "2",
+ /* 70 */ "3",
+ /* 71 */ "4",
+ /* 72 */ "5",
+ /* 73 */ "6",
+ /* 74 */ "7",
+ /* 75 */ "8",
+ /* 76 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 78 */ "0,\u066B,\u066C",
- /* 79~ */
+ /* 77 */ "0,\u066B,\u066C",
+ /* 78~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~88 */
+ /* ~87 */
// U+060C: "،" ARABIC COMMA
- /* 89 */ "\u060C",
- /* 90 */ "\\,",
- /* 91 */ "\u061F",
- /* 92 */ "\u061B",
+ /* 88 */ "\u060C",
+ /* 89 */ "\\,",
+ /* 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 */
@@ -998,20 +1003,20 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~105 */
- /* 106 */ "q",
- /* 107 */ "x",
+ null, null, null, null, null, null, null, null, null, null,
+ /* ~104 */
+ /* 105 */ "q",
+ /* 106 */ "x",
// U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
- /* 108 */ "\u015D",
+ /* 107 */ "\u015D",
// U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
- /* 109 */ "\u011D",
+ /* 108 */ "\u011D",
// U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
- /* 110 */ "\u016D",
+ /* 109 */ "\u016D",
// U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
- /* 111 */ "\u0109",
+ /* 110 */ "\u0109",
// U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
- /* 112 */ "\u0135",
+ /* 111 */ "\u0135",
};
/* Language es: Spanish */
@@ -1069,22 +1074,25 @@ public final class KeyboardTextsSet {
/* 8~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~48 */
+ null, null, null, null, null, null, null, null, null, null,
+ /* ~47 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 49 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
- /* 50~ */
+ /* 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, null, null, null, null,
null, null, null, null, null,
- /* ~99 */
+ /* ~98 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 100 */ "!,\u00A1",
- /* 101 */ null,
+ /* 99 */ "!,\u00A1",
+ /* 100 */ null,
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 102 */ "?,\u00BF",
+ /* 101 */ "?,\u00BF",
+ /* 102 */ "\"",
+ /* 103 */ "\'",
+ /* 104 */ "\'",
};
/* Language et: Estonian */
@@ -1200,25 +1208,32 @@ public final class KeyboardTextsSet {
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
/* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 45~ */
- null, null, null, null,
- /* ~48 */
+ /* 45 */ null,
+ // U+FDFC: "﷼" RIAL SIGN
+ // U+060B: "؋" AFGHANI SIGN
+ // U+00A2: "¢" CENT SIGN
+ // U+00A3: "£" POUND SIGN
+ // U+20AC: "€" EURO SIGN
+ // U+00A5: "¥" YEN SIGN
+ // U+20B1: "₱" PESO SIGN
+ /* 46 */ "\uFDFC",
+ /* 47 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1,\u060B",
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 50 */ "\u2605,\u066D",
+ /* 49 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 51 */ "\u266A",
- /* 52 */ null,
+ /* 50 */ "\u266A",
+ /* 51 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1234,8 +1249,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
- /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
+ /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
+ /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0652: "ْ" ARABIC SUKUN
// U+0651: "ّ" ARABIC SHADDA
@@ -1251,68 +1266,68 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 57 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
- /* 58 */ "\u064B",
+ /* 56 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 57 */ "\u064B",
// U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
- /* 59 */ "\u06F1",
+ /* 58 */ "\u06F1",
// U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
- /* 60 */ "\u06F2",
+ /* 59 */ "\u06F2",
// U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
- /* 61 */ "\u06F3",
+ /* 60 */ "\u06F3",
// U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
- /* 62 */ "\u06F4",
+ /* 61 */ "\u06F4",
// U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
- /* 63 */ "\u06F5",
+ /* 62 */ "\u06F5",
// U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
- /* 64 */ "\u06F6",
+ /* 63 */ "\u06F6",
// U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
- /* 65 */ "\u06F7",
+ /* 64 */ "\u06F7",
// U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
- /* 66 */ "\u06F8",
+ /* 65 */ "\u06F8",
// U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
- /* 67 */ "\u06F9",
+ /* 66 */ "\u06F9",
// U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
- /* 68 */ "\u06F0",
- /* 69 */ "1",
- /* 70 */ "2",
- /* 71 */ "3",
- /* 72 */ "4",
- /* 73 */ "5",
- /* 74 */ "6",
- /* 75 */ "7",
- /* 76 */ "8",
- /* 77 */ "9",
+ /* 67 */ "\u06F0",
+ /* 68 */ "1",
+ /* 69 */ "2",
+ /* 70 */ "3",
+ /* 71 */ "4",
+ /* 72 */ "5",
+ /* 73 */ "6",
+ /* 74 */ "7",
+ /* 75 */ "8",
+ /* 76 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 78 */ "0,\u066B,\u066C",
- /* 79~ */
+ /* 77 */ "0,\u066B,\u066C",
+ /* 78~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~88 */
+ /* ~87 */
// U+060C: "،" ARABIC COMMA
- /* 89 */ "\u060C",
- /* 90 */ "\\,",
- /* 91 */ "\u061F",
- /* 92 */ "\u061B",
+ /* 88 */ "\u060C",
+ /* 89 */ "\\,",
+ /* 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 */
@@ -1421,38 +1436,43 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~58 */
+ null,
+ /* ~45 */
+ // U+20B9: "₹" INDIAN RUPEE SIGN
+ /* 46 */ "\u20B9",
+ /* 47~ */
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~57 */
// U+0967: "१" DEVANAGARI DIGIT ONE
- /* 59 */ "\u0967",
+ /* 58 */ "\u0967",
// U+0968: "२" DEVANAGARI DIGIT TWO
- /* 60 */ "\u0968",
+ /* 59 */ "\u0968",
// U+0969: "३" DEVANAGARI DIGIT THREE
- /* 61 */ "\u0969",
+ /* 60 */ "\u0969",
// U+096A: "४" DEVANAGARI DIGIT FOUR
- /* 62 */ "\u096A",
+ /* 61 */ "\u096A",
// U+096B: "५" DEVANAGARI DIGIT FIVE
- /* 63 */ "\u096B",
+ /* 62 */ "\u096B",
// U+096C: "६" DEVANAGARI DIGIT SIX
- /* 64 */ "\u096C",
+ /* 63 */ "\u096C",
// U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* 65 */ "\u096D",
+ /* 64 */ "\u096D",
// U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* 66 */ "\u096E",
+ /* 65 */ "\u096E",
// U+096F: "९" DEVANAGARI DIGIT NINE
- /* 67 */ "\u096F",
+ /* 66 */ "\u096F",
// U+0966: "०" DEVANAGARI DIGIT ZERO
- /* 68 */ "\u0966",
- /* 69 */ "1",
- /* 70 */ "2",
- /* 71 */ "3",
- /* 72 */ "4",
- /* 73 */ "5",
- /* 74 */ "6",
- /* 75 */ "7",
- /* 76 */ "8",
- /* 77 */ "9",
- /* 78 */ "0",
+ /* 67 */ "\u0966",
+ /* 68 */ "1",
+ /* 69 */ "2",
+ /* 70 */ "3",
+ /* 71 */ "4",
+ /* 72 */ "5",
+ /* 73 */ "6",
+ /* 74 */ "7",
+ /* 75 */ "8",
+ /* 76 */ "9",
+ /* 77 */ "0",
};
/* Language hr: Croatian */
@@ -1648,19 +1668,21 @@ public final class KeyboardTextsSet {
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
/* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 45~ */
- null, null, null, null, null,
- /* ~49 */
+ /* 45 */ null,
+ // U+20AA: "₪" NEW SHEQEL SIGN
+ /* 46 */ "\u20AA",
+ /* 47 */ null,
+ /* 48 */ null,
// U+2605: "★" BLACK STAR
- /* 50 */ "\u2605",
- /* 51 */ null,
+ /* 49 */ "\u2605",
+ /* 50 */ null,
// U+00B1: "±" PLUS-MINUS SIGN
// U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
- /* 52 */ "\u00B1,\uFB29",
+ /* 51 */ "\u00B1,\uFB29",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 53 */ "!fixedColumnOrder!3,<|>,{|},[|]",
- /* 54 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
+ /* 52 */ "!fixedColumnOrder!3,<|>,{|},[|]",
+ /* 53 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1676,8 +1698,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
};
/* Language ky: Kirghiz */
@@ -1928,6 +1950,18 @@ public final class KeyboardTextsSet {
/* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
};
+ /* Language mn: Mongolian */
+ private static final String[] LANGUAGE_mn = {
+ /* 0~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null,
+ /* ~45 */
+ // U+20AE: "₮" TUGRIK SIGN
+ /* 46 */ "\u20AE",
+ };
+
/* Language nb: Norwegian Bokmål */
private static final String[] LANGUAGE_nb = {
// U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -2465,6 +2499,18 @@ public final class KeyboardTextsSet {
/* 15 */ "g\'",
};
+ /* Language th: Thai */
+ private static final String[] LANGUAGE_th = {
+ /* 0~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null,
+ /* ~45 */
+ // U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
+ /* 46 */ "\u0E3F",
+ };
+
/* Language tl: Tagalog */
private static final String[] LANGUAGE_tl = {
// U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -2589,6 +2635,11 @@ public final class KeyboardTextsSet {
/* 34 */ null,
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
/* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null, null, null, null, null, null, null,
+ /* ~45 */
+ // U+20B4: "₴" HRYVNIA SIGN
+ /* 46 */ "\u20B4",
};
/* Language vi: Vietnamese */
@@ -2670,6 +2721,13 @@ public final class KeyboardTextsSet {
/* 8 */ "\u1EF3,\u00FD,\u1EF7,\u1EF9,\u1EF5",
// U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
/* 9 */ "\u0111",
+ /* 10~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null,
+ /* ~45 */
+ // U+20AB: "₫" DONG SIGN
+ /* 46 */ "\u20AB",
};
/* Language zu: Zulu */
@@ -2868,6 +2926,7 @@ public final class KeyboardTextsSet {
"lt", LANGUAGE_lt, /* Lithuanian */
"lv", LANGUAGE_lv, /* Latvian */
"mk", LANGUAGE_mk, /* Macedonian */
+ "mn", LANGUAGE_mn, /* Mongolian */
"nb", LANGUAGE_nb, /* Norwegian Bokmål */
"nl", LANGUAGE_nl, /* Dutch */
"pl", LANGUAGE_pl, /* Polish */
@@ -2880,6 +2939,7 @@ public final class KeyboardTextsSet {
"sr", LANGUAGE_sr, /* Serbian */
"sv", LANGUAGE_sv, /* Swedish */
"sw", LANGUAGE_sw, /* Swahili */
+ "th", LANGUAGE_th, /* Thai */
"tl", LANGUAGE_tl, /* Tagalog */
"tr", LANGUAGE_tr, /* Turkish */
"uk", LANGUAGE_uk, /* Ukrainian */
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
index 550391b49..ca16163d4 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.StringUtils;
import java.util.Locale;
@@ -35,10 +35,10 @@ public final class MoreKeySpec {
KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale);
final int code = KeySpecParser.toUpperCaseOfCodeForLocale(
KeySpecParser.getCode(moreKeySpec, codesSet), needsToUpperCase, locale);
- if (code == Keyboard.CODE_UNSPECIFIED) {
+ if (code == Constants.CODE_UNSPECIFIED) {
// Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
// upper case representation ("SS").
- mCode = Keyboard.CODE_OUTPUT_TEXT;
+ mCode = Constants.CODE_OUTPUT_TEXT;
mOutputText = mLabel;
} else {
mCode = code;
@@ -75,8 +75,8 @@ public final class MoreKeySpec {
public String toString() {
final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
: KeySpecParser.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
- final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText
- : Keyboard.printableCode(mCode));
+ final String output = (mCode == Constants.CODE_OUTPUT_TEXT ? mOutputText
+ : Constants.printableCode(mCode));
if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
return output;
} else {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index a52f202aa..00fc885e8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -30,6 +30,7 @@ public final class PointerTrackerQueue {
public boolean isModifier();
public boolean isInSlidingKeyInput();
public void onPhantomUpEvent(long eventTime);
+ public void cancelTracking();
}
private static final int INITIAL_CAPACITY = 10;
@@ -182,6 +183,15 @@ public final class PointerTrackerQueue {
return false;
}
+ public synchronized void cancelAllPointerTracker() {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ element.cancelTracking();
+ }
+ }
+
@Override
public synchronized String toString() {
final StringBuilder sb = new StringBuilder();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index 776ac0204..bc734b08d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -36,14 +36,11 @@ import android.widget.RelativeLayout;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.Params;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.CoordinateUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
public final class PreviewPlacerView extends RelativeLayout {
- // The height of extra area above the keyboard to draw gesture trails.
- // Proportional to the keyboard height.
- private static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
-
private final int mGestureFloatingPreviewTextColor;
private final int mGestureFloatingPreviewTextOffset;
private final int mGestureFloatingPreviewColor;
@@ -51,8 +48,7 @@ public final class PreviewPlacerView extends RelativeLayout {
private final float mGestureFloatingPreviewVerticalPadding;
private final float mGestureFloatingPreviewRoundRadius;
- private int mKeyboardViewOriginX;
- private int mKeyboardViewOriginY;
+ private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance();
private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
CollectionUtils.newSparseArray();
@@ -72,11 +68,14 @@ public final class PreviewPlacerView extends RelativeLayout {
private final int mGestureFloatingPreviewTextHeight;
// {@link RectF} is needed for {@link Canvas#drawRoundRect(RectF, float, float, Paint)}.
private final RectF mGestureFloatingPreviewRectangle = new RectF();
- private int mLastPointerX;
- private int mLastPointerY;
+ private final int[] mLastPointerCoords = CoordinateUtils.newInstance();
private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
private boolean mDrawsGestureFloatingPreviewText;
+ private boolean mShowSlidingKeyInputPreview;
+ private final int[] mRubberBandFrom = CoordinateUtils.newInstance();
+ private final int[] mRubberBandTo = CoordinateUtils.newInstance();
+
private final DrawingHandler mDrawingHandler;
private static final class DrawingHandler extends StaticInnerHandlerWrapper<PreviewPlacerView> {
@@ -172,10 +171,9 @@ public final class PreviewPlacerView extends RelativeLayout {
setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
}
- public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) {
- mKeyboardViewOriginX = x;
- mKeyboardViewOriginY = y;
- mOffscreenOffsetY = (int)(h * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
+ public void setKeyboardViewGeometry(final int[] originCoords, final int w, final int h) {
+ CoordinateUtils.copy(mKeyboardViewOrigin, originCoords);
+ mOffscreenOffsetY = (int)(h * GestureStroke.EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
mOffscreenWidth = w;
mOffscreenHeight = mOffscreenOffsetY + h;
}
@@ -190,8 +188,7 @@ public final class PreviewPlacerView extends RelativeLayout {
final boolean needsToUpdateLastPointer =
isOldestTracker && mDrawsGestureFloatingPreviewText;
if (needsToUpdateLastPointer) {
- mLastPointerX = tracker.getLastX();
- mLastPointerY = tracker.getLastY();
+ tracker.getLastCoordinates(mLastPointerCoords);
}
if (mDrawsGesturePreviewTrail) {
@@ -212,6 +209,21 @@ public final class PreviewPlacerView extends RelativeLayout {
}
}
+ public void showSlidingKeyInputPreview(final PointerTracker tracker) {
+ if (!tracker.isInSlidingKeyInputFromModifier()) {
+ mShowSlidingKeyInputPreview = false;
+ return;
+ }
+ tracker.getDownCoordinates(mRubberBandFrom);
+ tracker.getLastCoordinates(mRubberBandTo);
+ mShowSlidingKeyInputPreview = true;
+ invalidate();
+ }
+
+ public void dismissSlidingKeyInputPreview() {
+ mShowSlidingKeyInputPreview = false;
+ }
+
@Override
protected void onDetachedFromWindow() {
freeOffscreenBuffer();
@@ -238,6 +250,8 @@ public final class PreviewPlacerView extends RelativeLayout {
@Override
public void onDraw(final Canvas canvas) {
super.onDraw(canvas);
+ final int originX = CoordinateUtils.x(mKeyboardViewOrigin);
+ final int originY = CoordinateUtils.y(mKeyboardViewOrigin);
if (mDrawsGesturePreviewTrail) {
mayAllocateOffscreenBuffer();
// Draw gesture trails to offscreen buffer.
@@ -245,11 +259,11 @@ public final class PreviewPlacerView extends RelativeLayout {
mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect);
// Transfer offscreen buffer to screen.
if (!mOffscreenDirtyRect.isEmpty()) {
- final int offsetY = mKeyboardViewOriginY - mOffscreenOffsetY;
- canvas.translate(mKeyboardViewOriginX, offsetY);
+ final int offsetY = originY - mOffscreenOffsetY;
+ canvas.translate(originX, offsetY);
canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect,
mGesturePaint);
- canvas.translate(-mKeyboardViewOriginX, -offsetY);
+ canvas.translate(-originX, -offsetY);
// Note: Defer clearing the dirty rectangle here because we will get cleared
// rectangle on the canvas.
}
@@ -258,9 +272,14 @@ public final class PreviewPlacerView extends RelativeLayout {
}
}
if (mDrawsGestureFloatingPreviewText) {
- canvas.translate(mKeyboardViewOriginX, mKeyboardViewOriginY);
+ canvas.translate(originX, originY);
drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
- canvas.translate(-mKeyboardViewOriginX, -mKeyboardViewOriginY);
+ canvas.translate(-originX, -originY);
+ }
+ if (mShowSlidingKeyInputPreview) {
+ canvas.translate(originX, originY);
+ drawSlidingKeyInputPreview(canvas);
+ canvas.translate(-originX, -originY);
}
}
@@ -321,8 +340,6 @@ public final class PreviewPlacerView extends RelativeLayout {
final Paint paint = mTextPaint;
final RectF rectangle = mGestureFloatingPreviewRectangle;
- // TODO: Figure out how we should deal with the floating preview text with multiple moving
- // fingers.
// Paint the round rectangle background.
final int textHeight = mGestureFloatingPreviewTextHeight;
@@ -332,9 +349,11 @@ public final class PreviewPlacerView extends RelativeLayout {
final float rectWidth = textWidth + hPad * 2.0f;
final float rectHeight = textHeight + vPad * 2.0f;
final int canvasWidth = canvas.getWidth();
- final float rectX = Math.min(Math.max(mLastPointerX - rectWidth / 2.0f, 0.0f),
+ final float rectX = Math.min(
+ Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f),
canvasWidth - rectWidth);
- final float rectY = mLastPointerY - mGestureFloatingPreviewTextOffset - rectHeight;
+ final float rectY = CoordinateUtils.y(mLastPointerCoords)
+ - mGestureFloatingPreviewTextOffset - rectHeight;
rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
final float round = mGestureFloatingPreviewRoundRadius;
paint.setColor(mGestureFloatingPreviewColor);
@@ -345,4 +364,8 @@ public final class PreviewPlacerView extends RelativeLayout {
final float textY = rectY + vPad + textHeight;
canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
}
+
+ private void drawSlidingKeyInputPreview(final Canvas canvas) {
+ // TODO: Implement rubber band preview
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java b/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java
new file mode 100644
index 000000000..1f5252077
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/RoundedLine.java
@@ -0,0 +1,103 @@
+/*
+ * 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.Path;
+import android.graphics.RectF;
+
+public final class RoundedLine {
+ private final RectF mArc1 = new RectF();
+ private final RectF mArc2 = new RectF();
+ private final Path mPath = new Path();
+
+ private static final double RADIAN_TO_DEGREE = 180.0d / Math.PI;
+ private static final double RIGHT_ANGLE = Math.PI / 2.0d;
+
+ /**
+ * Make a rounded line path
+ *
+ * @param p1x the x-coordinate of the start point.
+ * @param p1y the y-coordinate of the start point.
+ * @param r1 the radius at the start point
+ * @param p2x the x-coordinate of the end point.
+ * @param p2y the y-coordinate of the end point.
+ * @param r2 the radius at the end point
+ * @return the path of rounded line
+ */
+ public Path makePath(final float p1x, final float p1y, final float r1,
+ final float p2x, final float p2y, final float r2) {
+ final double dx = p2x - p1x;
+ final double dy = p2y - p1y;
+ // Distance of the points.
+ final double l = Math.hypot(dx, dy);
+ if (Double.compare(0.0d, l) == 0) {
+ return null;
+ }
+ // Angle of the line p1-p2
+ final double a = Math.atan2(dy, dx);
+ // Difference of trail cap radius.
+ final double dr = r2 - r1;
+ // Variation of angle at trail cap.
+ final double ar = Math.asin(dr / l);
+ // The start angle of trail cap arc at P1.
+ final double aa = a - (RIGHT_ANGLE + ar);
+ // The end angle of trail cap arc at P2.
+ final double ab = a + (RIGHT_ANGLE + ar);
+ final float cosa = (float)Math.cos(aa);
+ final float sina = (float)Math.sin(aa);
+ final float cosb = (float)Math.cos(ab);
+ final float sinb = (float)Math.sin(ab);
+ // Closing point of arc at P1.
+ final float p1ax = p1x + r1 * cosa;
+ final float p1ay = p1y + r1 * sina;
+ // Opening point of arc at P1.
+ final float p1bx = p1x + r1 * cosb;
+ final float p1by = p1y + r1 * sinb;
+ // Opening point of arc at P2.
+ final float p2ax = p2x + r2 * cosa;
+ final float p2ay = p2y + r2 * sina;
+ // Closing point of arc at P2.
+ final float p2bx = p2x + r2 * cosb;
+ final float p2by = p2y + r2 * sinb;
+ // Start angle of the trail arcs.
+ final float angle = (float)(aa * RADIAN_TO_DEGREE);
+ final float ar2degree = (float)(ar * 2.0d * RADIAN_TO_DEGREE);
+ // Sweep angle of the trail arc at P1.
+ final float a1 = -180.0f + ar2degree;
+ // Sweep angle of the trail arc at P2.
+ final float a2 = 180.0f + ar2degree;
+ mArc1.set(p1x, p1y, p1x, p1y);
+ mArc1.inset(-r1, -r1);
+ mArc2.set(p2x, p2y, p2x, p2y);
+ mArc2.inset(-r2, -r2);
+
+ mPath.rewind();
+ // Trail cap at P1.
+ mPath.moveTo(p1x, p1y);
+ mPath.arcTo(mArc1, angle, a1);
+ // Trail cap at P2.
+ mPath.moveTo(p2x, p2y);
+ mPath.arcTo(mArc2, angle, a2);
+ // Two trapezoids connecting P1 and P2.
+ mPath.moveTo(p1ax, p1ay);
+ mPath.lineTo(p1x, p1y);
+ mPath.lineTo(p1bx, p1by);
+ mPath.lineTo(p2bx, p2by);
+ mPath.lineTo(p2x, p2y);
+ mPath.lineTo(p2ax, p2ay);
+ mPath.close();
+ return mPath;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
index d8950a713..d7a2b6f39 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.keyboard.internal;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.LatinImeLogger;
public final class TouchPositionCorrection {
@@ -66,7 +67,7 @@ public final class TouchPositionCorrection {
}
}
- // For test only
+ @UsedForTesting
public void setEnabled(final boolean enabled) {
mEnabled = enabled;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java
index c53428fe5..c795d5322 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java
@@ -20,7 +20,6 @@ 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;
@@ -28,8 +27,8 @@ import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.research.ResearchLogger;
-public final class SuddenJumpingTouchEventHandler {
- private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
+public final class TouchScreenRegulator {
+ private static final String TAG = TouchScreenRegulator.class.getSimpleName();
private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
public interface ProcessMotionEvent {
@@ -50,17 +49,18 @@ public final class SuddenJumpingTouchEventHandler {
private int mJumpThresholdSquare = Integer.MAX_VALUE;
private int mLastX;
private int mLastY;
+ // One-seventh of the keyboard width seems like a reasonable threshold
+ private static final float JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH = 1.0f / 7.0f;
- public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
+ public TouchScreenRegulator(final Context context, final ProcessMotionEvent view) {
mView = view;
mNeedsSuddenJumpingHack = Boolean.parseBoolean(ResourceUtils.getDeviceOverrideValue(
context.getResources(), R.array.sudden_jumping_touch_event_device_list, "false"));
}
- public void setKeyboard(Keyboard newKeyboard) {
- // One-seventh of the keyboard width seems like a reasonable threshold
- final int jumpThreshold = newKeyboard.mOccupiedWidth / 7;
- mJumpThresholdSquare = jumpThreshold * jumpThreshold;
+ public void setKeyboardGeometry(final int keyboardWidth) {
+ final float jumpThreshold = keyboardWidth * JUMP_THRESHOLD_RATIO_TO_KEYBOARD_WIDTH;
+ mJumpThresholdSquare = (int)(jumpThreshold * jumpThreshold);
}
/**
@@ -74,7 +74,7 @@ public final class SuddenJumpingTouchEventHandler {
* @return true if the event was consumed, so that it doesn't continue to be handled by
* {@link MainKeyboardView}.
*/
- private boolean handleSuddenJumping(MotionEvent me) {
+ private boolean handleSuddenJumping(final MotionEvent me) {
if (!mNeedsSuddenJumpingHack)
return false;
final int action = me.getAction();
@@ -140,7 +140,7 @@ public final class SuddenJumpingTouchEventHandler {
return result;
}
- public boolean onTouchEvent(MotionEvent me) {
+ public boolean onTouchEvent(final MotionEvent me) {
// If there was a sudden jump, return without processing the actual motion event.
if (handleSuddenJumping(me)) {
if (DEBUG_MODE)
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 939451899..08f08d24e 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -50,6 +50,7 @@ import java.util.ArrayList;
import java.util.TreeSet;
public final class AdditionalSubtypeSettings extends PreferenceFragment {
+ private RichInputMethodManager mRichImm;
private SharedPreferences mPrefs;
private SubtypeLocaleAdapter mSubtypeLocaleAdapter;
private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
@@ -63,6 +64,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
"is_subtype_enabler_notification_dialog_open";
private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
+
static final class SubtypeLocaleItem extends Pair<String, String>
implements Comparable<SubtypeLocaleItem> {
public SubtypeLocaleItem(final String localeString, final String displayName) {
@@ -93,7 +95,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet();
- final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context);
+ final InputMethodInfo imi = RichInputMethodManager.getInstance()
+ .getInputMethodInfoOfThisIme();
final int count = imi.getSubtypeCount();
for (int i = 0; i < count; i++) {
final InputMethodSubtype subtype = imi.getSubtypeAt(i);
@@ -383,10 +386,11 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mPrefs = getPreferenceManager().getSharedPreferences();
+ RichInputMethodManager.init(getActivity(), mPrefs);
+ mRichImm = RichInputMethodManager.getInstance();
addPreferencesFromResource(R.xml.additional_subtype_settings);
setHasOptionsMenu(true);
-
- mPrefs = getPreferenceManager().getSharedPreferences();
}
@Override
@@ -439,7 +443,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
mIsAddingNewSubtype = false;
final PreferenceGroup group = getPreferenceScreen();
group.removePreference(subtypePref);
- ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+ mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
}
@Override
@@ -449,7 +453,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
return;
}
if (findDuplicatedSubtype(subtype) == null) {
- ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+ mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
return;
}
@@ -466,7 +470,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
mIsAddingNewSubtype = false;
final InputMethodSubtype subtype = subtypePref.getSubtype();
if (findDuplicatedSubtype(subtype) == null) {
- ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), getSubtypes());
+ mRichImm.setAdditionalInputMethodSubtypes(getSubtypes());
mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey();
mSubtypeEnablerNotificationDialog = createDialog(subtypePref);
mSubtypeEnablerNotificationDialog.show();
@@ -501,8 +505,8 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
- return ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
- getActivity(), localeString, keyboardLayoutSetName);
+ return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ localeString, keyboardLayoutSetName);
}
private AlertDialog createDialog(
@@ -515,7 +519,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
final Intent intent = CompatUtils.getInputLanguageSelectionIntent(
- ImfUtils.getInputMethodIdOfThisIme(getActivity()),
+ mRichImm.getInputMethodIdOfThisIme(),
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -573,7 +577,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
} finally {
editor.apply();
}
- ImfUtils.setAdditionalInputMethodSubtypes(getActivity(), subtypes);
+ mRichImm.setAdditionalInputMethodSubtypes(subtypes);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 59ef5e09f..024726391 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -21,7 +21,6 @@ import android.media.AudioManager;
import android.view.HapticFeedbackConstants;
import android.view.View;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.VibratorUtils;
/**
@@ -31,17 +30,15 @@ import com.android.inputmethod.latin.VibratorUtils;
* complexity of settings and the like.
*/
public final class AudioAndHapticFeedbackManager {
- final private SettingsValues mSettingsValues;
- final private AudioManager mAudioManager;
- final private VibratorUtils mVibratorUtils;
+ private final AudioManager mAudioManager;
+ private final VibratorUtils mVibratorUtils;
+
+ private SettingsValues mSettingsValues;
private boolean mSoundOn;
- public AudioAndHapticFeedbackManager(final LatinIME latinIme,
- final SettingsValues settingsValues) {
- mSettingsValues = settingsValues;
+ public AudioAndHapticFeedbackManager(final LatinIME latinIme) {
mVibratorUtils = VibratorUtils.getInstance(latinIme);
mAudioManager = (AudioManager) latinIme.getSystemService(Context.AUDIO_SERVICE);
- mSoundOn = reevaluateIfSoundIsOn();
}
public void hapticAndAudioFeedback(final int primaryCode,
@@ -51,7 +48,7 @@ public final class AudioAndHapticFeedbackManager {
}
private boolean reevaluateIfSoundIsOn() {
- if (!mSettingsValues.mSoundOn || mAudioManager == null) {
+ if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) {
return false;
} else {
return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
@@ -64,13 +61,13 @@ public final class AudioAndHapticFeedbackManager {
if (mSoundOn) {
final int sound;
switch (primaryCode) {
- case Keyboard.CODE_DELETE:
+ case Constants.CODE_DELETE:
sound = AudioManager.FX_KEYPRESS_DELETE;
break;
- case Keyboard.CODE_ENTER:
+ case Constants.CODE_ENTER:
sound = AudioManager.FX_KEYPRESS_RETURN;
break;
- case Keyboard.CODE_SPACE:
+ case Constants.CODE_SPACE:
sound = AudioManager.FX_KEYPRESS_SPACEBAR;
break;
default:
@@ -81,8 +78,7 @@ public final class AudioAndHapticFeedbackManager {
}
}
- // TODO: make this private when LatinIME does not call it any more
- public void vibrate(final View viewToPerformHapticFeedbackOn) {
+ private void vibrate(final View viewToPerformHapticFeedbackOn) {
if (!mSettingsValues.mVibrateOn) {
return;
}
@@ -98,6 +94,11 @@ public final class AudioAndHapticFeedbackManager {
}
}
+ public void onSettingsChanged(final SettingsValues settingsValues) {
+ mSettingsValues = settingsValues;
+ mSoundOn = reevaluateIfSoundIsOn();
+ }
+
public void onRingerModeChanged() {
mSoundOn = reevaluateIfSoundIsOn();
}
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index 84fad158f..fa35922b0 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -33,11 +33,11 @@ public final class AutoCorrection {
}
public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
- CharSequence word, boolean ignoreCase) {
+ final String word, final boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
return false;
}
- final CharSequence lowerCasedWord = word.toString().toLowerCase();
+ final String lowerCasedWord = word.toLowerCase();
for (final String key : dictionaries.keySet()) {
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
@@ -57,7 +57,7 @@ public final class AutoCorrection {
}
public static int getMaxFrequency(final ConcurrentHashMap<String, Dictionary> dictionaries,
- CharSequence word) {
+ final String word) {
if (TextUtils.isEmpty(word)) {
return Dictionary.NOT_A_PROBABILITY;
}
@@ -76,12 +76,13 @@ public final class AutoCorrection {
// Returns true if this is in any of the dictionaries.
public static boolean isInTheDictionary(
final ConcurrentHashMap<String, Dictionary> dictionaries,
- final CharSequence word, final boolean ignoreCase) {
+ final String word, final boolean ignoreCase) {
return isValidWord(dictionaries, word, ignoreCase);
}
- public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
- CharSequence consideredWord, float autoCorrectionThreshold) {
+ public static boolean suggestionExceedsAutoCorrectionThreshold(
+ final SuggestedWordInfo suggestion, final String consideredWord,
+ final float autoCorrectionThreshold) {
if (null != suggestion) {
// Shortlist a whitelisted word
if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true;
@@ -89,8 +90,7 @@ public final class AutoCorrection {
// 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(), suggestion.mWord.toString(),
- autoCorrectionSuggestionScore);
+ consideredWord, suggestion.mWord, autoCorrectionSuggestionScore);
if (DBG) {
Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
@@ -100,8 +100,7 @@ public final class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
- suggestion.mWord);
+ return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord);
}
}
return false;
@@ -110,7 +109,7 @@ public final class AutoCorrection {
// TODO: Resolve the inconsistencies between the native auto correction algorithms and
// this safety net
public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final CharSequence suggestion) {
+ final String 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
@@ -123,7 +122,7 @@ public final class AutoCorrection {
}
final int maxEditDistanceOfNativeDictionary =
(typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
- final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
+ final int distance = BinaryDictionary.editDistance(typedWord, suggestion);
if (DBG) {
Log.d(TAG, "Autocorrected edit distance = " + distance
+ ", " + maxEditDistanceOfNativeDictionary);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 7184f1d8a..6048a3308 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -31,7 +31,7 @@ import java.util.Locale;
* Implements a static, compacted, binary dictionary of standard words.
*/
public final class BinaryDictionary extends Dictionary {
-
+ private static final String TAG = BinaryDictionary.class.getSimpleName();
public static final String DICTIONARY_PACK_AUTHORITY =
"com.android.inputmethod.latin.dictionarypack";
@@ -45,17 +45,13 @@ public final class BinaryDictionary extends Dictionary {
public static final int MAX_WORDS = 18;
public static final int MAX_SPACES = 16;
- private static final String TAG = BinaryDictionary.class.getSimpleName();
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 long mNativeDict;
private final Locale mLocale;
private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
- // TODO: The below should be int[] mOutputCodePoints
- private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
+ private final int[] mOutputCodePoints = new int[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];
@@ -67,7 +63,7 @@ public final class BinaryDictionary extends Dictionary {
// TODO: There should be a way to remove used DicTraverseSession objects from
// {@code mDicTraverseSessions}.
- private DicTraverseSession getTraverseSession(int traverseSessionId) {
+ private DicTraverseSession getTraverseSession(final int traverseSessionId) {
synchronized(mDicTraverseSessions) {
DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
if (traverseSession == null) {
@@ -84,7 +80,6 @@ public final class BinaryDictionary extends Dictionary {
/**
* Constructor for the binary dictionary. This is supposed to be called from the
* dictionary factory.
- * All implementations should pass null into flagArray, except for testing purposes.
* @param context the context to access the environment from.
* @param filename the name of the file to read through native code.
* @param offset the offset of the dictionary data within the file.
@@ -92,9 +87,9 @@ public final class BinaryDictionary extends Dictionary {
* @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, final String dictType) {
+ public BinaryDictionary(final Context context, final String filename, final long offset,
+ final long length, final boolean useFullEditDistance, final Locale locale,
+ final String dictType) {
super(dictType);
mLocale = locale;
mUseFullEditDistance = useFullEditDistance;
@@ -106,40 +101,40 @@ public final class BinaryDictionary extends Dictionary {
}
private native long openNative(String sourceDir, long dictOffset, long dictSize,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
- int maxPredictions);
+ int maxWordLength, int maxWords, int maxPredictions);
private native void closeNative(long dict);
private native int getFrequencyNative(long dict, int[] word);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession,
int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds,
int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture,
- int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
+ int[] prevWordCodePointArray, boolean useFullEditDistance, int[] outputCodePoints,
int[] outputScores, int[] outputIndices, int[] outputTypes);
- private static native float calcNormalizedScoreNative(char[] before, char[] after, int score);
- private static native int editDistanceNative(char[] before, char[] after);
+ private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
+ private static native int editDistanceNative(int[] before, int[] after);
// TODO: Move native dict into session
- private final void loadDictionary(String path, long startOffset, long length) {
- mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
- FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
+ private final void loadDictionary(final String path, final long startOffset,
+ final long length) {
+ mNativeDict = openNative(path, startOffset, length, MAX_WORD_LENGTH, MAX_WORDS,
+ MAX_PREDICTIONS);
}
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
}
@Override
public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ final String prevWord, final ProximityInfo proximityInfo, int sessionId) {
if (!isValidDictionary()) return null;
Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
// TODO: toLowerCase in the native code
final int[] prevWordCodePointArray = (null == prevWord)
- ? null : StringUtils.toCodePointArray(prevWord.toString());
+ ? null : StringUtils.toCodePointArray(prevWord);
final int composerSize = composer.size();
final boolean isGesture = composer.isBatchMode();
@@ -157,7 +152,8 @@ public final class BinaryDictionary extends Dictionary {
proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(),
ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
- mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
+ mUseFullEditDistance, mOutputCodePoints, mOutputScores, mSpaceIndices,
+ mOutputTypes);
final int count = Math.min(tmpCount, MAX_PREDICTIONS);
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
@@ -165,53 +161,56 @@ public final class BinaryDictionary extends Dictionary {
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 && mOutputCodePoints[start + len] != 0) {
++len;
}
if (len > 0) {
final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
- suggestions.add(new SuggestedWordInfo(
- new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType));
+ suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
+ score, mOutputTypes[j], mDictType));
}
}
return suggestions;
}
- /* package for test */ boolean isValidDictionary() {
+ public boolean isValidDictionary() {
return mNativeDict != 0;
}
- public static float calcNormalizedScore(String before, String after, int score) {
- return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score);
+ public static float calcNormalizedScore(final String before, final String after,
+ final int score) {
+ return calcNormalizedScoreNative(StringUtils.toCodePointArray(before),
+ StringUtils.toCodePointArray(after), score);
}
- public static int editDistance(String before, String after) {
+ public static int editDistance(final String before, final String after) {
if (before == null || after == null) {
throw new IllegalArgumentException();
}
- return editDistanceNative(before.toCharArray(), after.toCharArray());
+ return editDistanceNative(StringUtils.toCodePointArray(before),
+ StringUtils.toCodePointArray(after));
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
return getFrequency(word) >= 0;
}
@Override
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
if (word == null) return -1;
- int[] codePoints = StringUtils.toCodePointArray(word.toString());
+ int[] codePoints = StringUtils.toCodePointArray(word);
return getFrequencyNative(mNativeDict, codePoints);
}
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
// calls when checking for changes in an entire dictionary.
- public boolean isValidBigram(CharSequence word1, CharSequence word2) {
+ public boolean isValidBigram(final String word1, final String word2) {
if (TextUtils.isEmpty(word1) || TextUtils.isEmpty(word2)) return false;
- int[] chars1 = StringUtils.toCodePointArray(word1.toString());
- int[] chars2 = StringUtils.toCodePointArray(word2.toString());
- return isValidBigramNative(mNativeDict, chars1, chars2);
+ final int[] codePoints1 = StringUtils.toCodePointArray(word1);
+ final int[] codePoints2 = StringUtils.toCodePointArray(word2);
+ return isValidBigramNative(mNativeDict, codePoints1, codePoints2);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 9a3f88f52..5eab292fc 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -149,7 +149,13 @@ public final class BinaryDictionaryFileDumper {
final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id);
final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
- final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
+ String tempFileName;
+ try {
+ tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't open the temporary file", e);
+ return null;
+ }
for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
InputStream originalSourceStream = null;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index c747dc673..83dabbede 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
import com.android.inputmethod.latin.makedict.FormatSpec;
@@ -165,14 +166,9 @@ final class BinaryDictionaryGetter {
/**
* Generates a unique temporary file name in the app cache directory.
- *
- * This is unique as long as it doesn't get called twice in the same millisecond by the same
- * thread, which should be more than enough for our purposes.
*/
- public static String getTempFileName(String id, Context context) {
- final String fileName = replaceFileNameDangerousCharacters(id);
- return context.getCacheDir() + File.separator + fileName + "."
- + Thread.currentThread().getId() + "." + System.currentTimeMillis();
+ public static String getTempFileName(String id, Context context) throws IOException {
+ return File.createTempFile(replaceFileNameDangerousCharacters(id), null).getAbsolutePath();
}
/**
@@ -427,8 +423,11 @@ final class BinaryDictionaryGetter {
// cacheWordListsFromContentProvider returns the list of files it copied to local
// storage, but we don't really care about what was copied NOW: what we want is the
// list of everything we ever cached, so we ignore the return value.
- BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context,
- hasDefaultWordList);
+ // TODO: The experimental version is not supported by the Dictionary Pack Service yet
+ if (!ProductionFlag.IS_EXPERIMENTAL) {
+ BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context,
+ hasDefaultWordList);
+ }
final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
final String mainDictId = getMainDictId(locale);
final DictPackSettings dictPackSettings = new DictPackSettings(context);
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 57e12a64f..ba932e590 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -136,11 +136,81 @@ public final class Constants {
public static final int NOT_A_CODE = -1;
- // See {@link KeyboardActionListener.Adapter#isInvalidCoordinate(int)}.
public static final int NOT_A_COORDINATE = -1;
public static final int SUGGESTION_STRIP_COORDINATE = -2;
public static final int SPELL_CHECKER_COORDINATE = -3;
+ public static boolean isValidCoordinate(final int coordinate) {
+ // Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE},
+ // and {@link SPELL_CHECKER_COORDINATE}.
+ return coordinate >= 0;
+ }
+
+ /**
+ * Some common keys code. Must be positive.
+ */
+ public static final int CODE_ENTER = '\n';
+ public static final int CODE_TAB = '\t';
+ public static final int CODE_SPACE = ' ';
+ public static final int CODE_PERIOD = '.';
+ public static final int CODE_DASH = '-';
+ public static final int CODE_SINGLE_QUOTE = '\'';
+ public static final int CODE_DOUBLE_QUOTE = '"';
+ public static final int CODE_QUESTION_MARK = '?';
+ public static final int CODE_EXCLAMATION_MARK = '!';
+ // TODO: Check how this should work for right-to-left languages. It seems to stand
+ // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
+ // managed by the font? Or is it a different char?
+ public static final int CODE_CLOSING_PARENTHESIS = ')';
+ public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
+ public static final int CODE_CLOSING_CURLY_BRACKET = '}';
+ public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
+
+ /**
+ * Special keys code. Must be negative.
+ * 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;
+ public static final int CODE_OUTPUT_TEXT = -3;
+ public static final int CODE_DELETE = -4;
+ public static final int CODE_SETTINGS = -5;
+ public static final int CODE_SHORTCUT = -6;
+ public static final int CODE_ACTION_ENTER = -7;
+ 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 = -12;
+
+ public static boolean isLetterCode(final int code) {
+ return code >= CODE_SPACE;
+ }
+
+ public static String printableCode(final int code) {
+ switch (code) {
+ case CODE_SHIFT: return "shift";
+ case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
+ case CODE_OUTPUT_TEXT: return "text";
+ case CODE_DELETE: return "delete";
+ case CODE_SETTINGS: return "settings";
+ case CODE_SHORTCUT: return "shortcut";
+ case CODE_ACTION_ENTER: return "actionEnter";
+ case CODE_ACTION_NEXT: return "actionNext";
+ case CODE_ACTION_PREVIOUS: return "actionPrevious";
+ case CODE_LANGUAGE_SWITCH: return "languageSwitch";
+ case CODE_UNSPECIFIED: return "unspec";
+ case CODE_TAB: return "tab";
+ case CODE_ENTER: return "enter";
+ default:
+ if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
+ if (code < 0x100) return String.format("'%c'", code);
+ return String.format("'\\u%04x'", code);
+ }
+ }
+
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 5edc4314f..d1b32a2f3 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -24,8 +24,6 @@ import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.util.Log;
-import com.android.inputmethod.keyboard.Keyboard;
-
import java.util.Locale;
public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
@@ -62,7 +60,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private final boolean mUseFirstLastBigrams;
- public ContactsBinaryDictionary(final Context context, Locale locale) {
+ public ContactsBinaryDictionary(final Context context, final Locale locale) {
super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
@@ -120,7 +118,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
}
- private boolean useFirstLastBigramsForLocale(Locale locale) {
+ private boolean useFirstLastBigramsForLocale(final Locale locale) {
// TODO: Add firstname/lastname bigram rules for other languages.
if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
return true;
@@ -128,7 +126,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
return false;
}
- private void addWords(Cursor cursor) {
+ private void addWords(final Cursor cursor) {
clearFusionDictionary();
int count = 0;
while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) {
@@ -160,7 +158,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their
* bigrams depending on locale.
*/
- private void addName(String name) {
+ private void addName(final String name) {
int len = StringUtils.codePointCount(name);
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
@@ -188,12 +186,13 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/**
* Returns the index of the last letter in the word, starting from position startIndex.
*/
- private static int getWordEndPosition(String string, int len, int startIndex) {
+ private static int getWordEndPosition(final String string, final int len,
+ final int startIndex) {
int end;
int cp = 0;
for (end = startIndex + 1; end < len; end += Character.charCount(cp)) {
cp = string.codePointAt(end);
- if (!(cp == Keyboard.CODE_DASH || cp == Keyboard.CODE_SINGLE_QUOTE
+ if (!(cp == Constants.CODE_DASH || cp == Constants.CODE_SINGLE_QUOTE
|| Character.isLetter(cp))) {
break;
}
@@ -249,7 +248,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
return false;
}
- private static boolean isValidName(String name) {
+ private static boolean isValidName(final String name) {
if (name != null && -1 == name.indexOf('@')) {
return true;
}
@@ -259,7 +258,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/**
* Checks if the words in a name are in the current binary dictionary.
*/
- private boolean isNameInDictionary(String name) {
+ private boolean isNameInDictionary(final String name) {
int len = StringUtils.codePointCount(name);
String prevWord = null;
for (int i = 0; i < len; i++) {
diff --git a/java/src/com/android/inputmethod/latin/CoordinateUtils.java b/java/src/com/android/inputmethod/latin/CoordinateUtils.java
new file mode 100644
index 000000000..af270e1e4
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CoordinateUtils.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;
+
+public final class CoordinateUtils {
+ private static final int INDEX_X = 0;
+ private static final int INDEX_Y = 1;
+ private static final int ARRAY_SIZE = INDEX_Y + 1;
+
+ private CoordinateUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static int[] newInstance() {
+ return new int[ARRAY_SIZE];
+ }
+
+ public static int x(final int[] coords) {
+ return coords[INDEX_X];
+ }
+
+ public static int y(final int[] coords) {
+ return coords[INDEX_Y];
+ }
+
+ public static void set(final int[] coords, final int x, final int y) {
+ coords[INDEX_X] = x;
+ coords[INDEX_Y] = y;
+ }
+
+ public static void copy(final int[] destination, final int[] source) {
+ destination[INDEX_X] = source[INDEX_X];
+ destination[INDEX_Y] = source[INDEX_Y];
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 3af3cab2c..f8aeb4e9f 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -37,9 +37,12 @@ public final class DebugSettings extends PreferenceFragment
private static final String DEBUG_MODE_KEY = "debug_mode";
public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch";
public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
+ private static final String PREF_STATISTICS_LOGGING_KEY = "enable_logging";
+ private static final boolean SHOW_STATISTICS_LOGGING = false;
private boolean mServiceNeedsRestart = false;
private CheckBoxPreference mDebugMode;
+ private CheckBoxPreference mStatisticsLoggingPref;
@Override
public void onCreate(Bundle icicle) {
@@ -55,6 +58,13 @@ public final class DebugSettings extends PreferenceFragment
ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
checkbox.setSummary(R.string.settings_warning_researcher_mode);
}
+ final Preference statisticsLoggingPref = findPreference(PREF_STATISTICS_LOGGING_KEY);
+ if (statisticsLoggingPref instanceof CheckBoxPreference) {
+ mStatisticsLoggingPref = (CheckBoxPreference) statisticsLoggingPref;
+ if (!SHOW_STATISTICS_LOGGING) {
+ getPreferenceScreen().removePreference(statisticsLoggingPref);
+ }
+ }
mServiceNeedsRestart = false;
mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
@@ -72,6 +82,14 @@ public final class DebugSettings extends PreferenceFragment
if (key.equals(DEBUG_MODE_KEY)) {
if (mDebugMode != null) {
mDebugMode.setChecked(prefs.getBoolean(DEBUG_MODE_KEY, false));
+ final boolean checked = mDebugMode.isChecked();
+ if (mStatisticsLoggingPref != null) {
+ if (checked) {
+ getPreferenceScreen().addPreference(mStatisticsLoggingPref);
+ } else {
+ getPreferenceScreen().removePreference(mStatisticsLoggingPref);
+ }
+ }
updateDebugMode();
mServiceNeedsRestart = true;
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 88d0c09dd..a218509f3 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -26,11 +26,6 @@ import java.util.ArrayList;
* strokes.
*/
public abstract class Dictionary {
- /**
- * The weight to give to a word if it's length is the same as the number of typed characters.
- */
- protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
-
public static final int NOT_A_PROBABILITY = -1;
public static final String TYPE_USER_TYPED = "user_typed";
@@ -59,12 +54,12 @@ public abstract class Dictionary {
// 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);
+ final String prevWord, final ProximityInfo proximityInfo);
// The default implementation of this method ignores sessionId.
// Subclasses that want to use sessionId need to override this method.
public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ final String prevWord, final ProximityInfo proximityInfo, final int sessionId) {
return getSuggestions(composer, prevWord, proximityInfo);
}
@@ -73,9 +68,9 @@ public abstract class Dictionary {
* @param word the word to search for. The search should be case-insensitive.
* @return true if the word exists, false otherwise
*/
- abstract public boolean isValidWord(CharSequence word);
+ abstract public boolean isValidWord(final String word);
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
return NOT_A_PROBABILITY;
}
@@ -87,7 +82,7 @@ public abstract class Dictionary {
* @param typedWord the word to compare with
* @return true if they are the same, false otherwise.
*/
- protected boolean same(final char[] word, final int length, final CharSequence typedWord) {
+ protected boolean same(final char[] word, final int length, final String typedWord) {
if (typedWord.length() != length) {
return false;
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index d3b120989..7f78ac8a2 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -56,7 +56,7 @@ public final class DictionaryCollection extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String 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
@@ -74,14 +74,14 @@ public final class DictionaryCollection extends Dictionary {
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
for (int i = mDictionaries.size() - 1; i >= 0; --i)
if (mDictionaries.get(i).isValidWord(word)) return true;
return false;
}
@Override
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
int maxFreq = -1;
for (int i = mDictionaries.size() - 1; i >= 0; --i) {
final int tempFreq = mDictionaries.get(i).getFrequency(word);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index b93c17f11..159867ade 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -198,7 +198,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
if (mLocalDictionaryController.tryLock()) {
try {
@@ -213,12 +213,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final CharSequence word) {
+ public boolean isValidWord(final String word) {
asyncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
- protected boolean isValidWordInner(final CharSequence word) {
+ protected boolean isValidWordInner(final String word) {
if (mLocalDictionaryController.tryLock()) {
try {
return isValidWordLocked(word);
@@ -229,17 +229,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidWordLocked(final CharSequence word) {
+ protected boolean isValidWordLocked(final String word) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidWord(word);
}
- protected boolean isValidBigram(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigram(final String word1, final String word2) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidBigram(word1, word2);
}
- protected boolean isValidBigramInner(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigramInner(final String word1, final String word2) {
if (mLocalDictionaryController.tryLock()) {
try {
return isValidBigramLocked(word1, word2);
@@ -250,7 +250,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidBigramLocked(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigramLocked(final String word1, final String word2) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidBigram(word1, word2);
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 8cdc2a0af..fa0d5497b 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
-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;
@@ -32,6 +31,10 @@ import java.util.LinkedList;
* be searched for suggestions and valid words.
*/
public class ExpandableDictionary extends Dictionary {
+ /**
+ * The weight to give to a word if it's length is the same as the number of typed characters.
+ */
+ private static final int FULL_WORD_SCORE_MULTIPLIER = 2;
// Bigram frequency is a fixed point number with 1 meaning 1.2 and 255 meaning 1.8.
protected static final int BIGRAM_MAX_FREQUENCY = 255;
@@ -69,7 +72,7 @@ public class ExpandableDictionary extends Dictionary {
mData = new Node[INCREMENT];
}
- void add(Node n) {
+ void add(final Node n) {
if (mLength + 1 > mData.length) {
Node[] tempData = new Node[mLength + INCREMENT];
if (mLength > 0) {
@@ -172,7 +175,7 @@ public class ExpandableDictionary extends Dictionary {
}
}
- public void setRequiresReload(boolean reload) {
+ public void setRequiresReload(final boolean reload) {
synchronized (mUpdatingLock) {
mRequiresReload = reload;
}
@@ -202,8 +205,8 @@ public class ExpandableDictionary extends Dictionary {
addWordRec(mRoots, word, 0, shortcutTarget, frequency, null);
}
- private void addWordRec(NodeArray children, final String word, final int depth,
- final String shortcutTarget, final int frequency, Node parentNode) {
+ private void addWordRec(final NodeArray children, final String word, final int depth,
+ final String shortcutTarget, final int frequency, final Node parentNode) {
final int wordLength = word.length();
if (wordLength <= depth) return;
final char c = word.charAt(depth);
@@ -248,7 +251,7 @@ public class ExpandableDictionary extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
if (reloadDictionaryIfRequired()) return null;
if (composer.size() > 1) {
if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
@@ -267,8 +270,7 @@ public class ExpandableDictionary extends Dictionary {
// This reloads the dictionary if required, and returns whether it's currently updating its
// contents or not.
- // @VisibleForTesting
- boolean reloadDictionaryIfRequired() {
+ private boolean reloadDictionaryIfRequired() {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
@@ -277,7 +279,7 @@ public class ExpandableDictionary extends Dictionary {
}
protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
@@ -305,7 +307,7 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
@@ -320,7 +322,7 @@ public class ExpandableDictionary extends Dictionary {
return (node == null) ? false : !node.mShortcutOnly;
}
- protected boolean removeBigram(String word1, String word2) {
+ protected boolean removeBigram(final String word1, final String word2) {
// Refer to addOrSetBigram() about word1.toLowerCase()
final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -345,13 +347,13 @@ public class ExpandableDictionary extends Dictionary {
/**
* Returns the word's frequency or -1 if not found
*/
- protected int getWordFrequency(CharSequence word) {
+ protected int getWordFrequency(final String word) {
// Case-sensitive search
final Node node = searchNode(mRoots, word, 0, word.length());
return (node == null) ? -1 : node.mFrequency;
}
- protected NextWord getBigramWord(String word1, String word2) {
+ protected NextWord getBigramWord(final String word1, final String word2) {
// Refer to addOrSetBigram() about word1.toLowerCase()
final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -368,7 +370,8 @@ public class ExpandableDictionary extends Dictionary {
return null;
}
- private static int computeSkippedWordFinalFreq(int freq, int snr, int inputLength) {
+ private static int computeSkippedWordFinalFreq(final int freq, final int snr,
+ final int inputLength) {
// The computation itself makes sense for >= 2, but the == 2 case returns 0
// anyway so we may as well test against 3 instead and return the constant
if (inputLength >= 3) {
@@ -431,9 +434,9 @@ public class ExpandableDictionary extends Dictionary {
* @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,
- final ArrayList<SuggestedWordInfo> suggestions) {
+ protected void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word,
+ final int depth, final boolean completion, final int snr, final int inputIndex,
+ final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) {
final int count = roots.mLength;
final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed.
@@ -472,8 +475,8 @@ public class ExpandableDictionary extends Dictionary {
getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex,
skipPos, suggestions);
}
- } else if ((c == Keyboard.CODE_SINGLE_QUOTE
- && currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) {
+ } else if ((c == Constants.CODE_SINGLE_QUOTE
+ && currentChars[0] != Constants.CODE_SINGLE_QUOTE) || depth == skipPos) {
// Skip the ' and continue deeper
word[depth] = c;
if (children != null) {
@@ -524,11 +527,13 @@ public class ExpandableDictionary extends Dictionary {
}
}
- public int setBigramAndGetFrequency(String word1, String word2, int frequency) {
+ public int setBigramAndGetFrequency(final String word1, final String word2,
+ final int frequency) {
return setBigramAndGetFrequency(word1, word2, frequency, null /* unused */);
}
- public int setBigramAndGetFrequency(String word1, String word2, ForgettingCurveParams fcp) {
+ public int setBigramAndGetFrequency(final String word1, final String word2,
+ final ForgettingCurveParams fcp) {
return setBigramAndGetFrequency(word1, word2, 0 /* unused */, fcp);
}
@@ -540,8 +545,8 @@ public class ExpandableDictionary extends Dictionary {
* @param fcp an instance of ForgettingCurveParams to use for decay policy
* @return returns the final bigram frequency
*/
- private int setBigramAndGetFrequency(
- String word1, String word2, int frequency, ForgettingCurveParams fcp) {
+ private int setBigramAndGetFrequency(final String word1, final String word2,
+ final int frequency, final ForgettingCurveParams fcp) {
// We don't want results to be different according to case of the looked up left hand side
// word. We do want however to return the correct case for the right hand side.
// So we want to squash the case of the left hand side, and preserve that of the right
@@ -572,7 +577,8 @@ public class ExpandableDictionary extends Dictionary {
* Searches for the word and add the word if it does not exist.
* @return Returns the terminal node of the word we are searching for.
*/
- private Node searchWord(NodeArray children, String word, int depth, Node parentNode) {
+ private Node searchWord(final NodeArray children, final String word, final int depth,
+ final Node parentNode) {
final int wordLength = word.length();
final char c = word.charAt(depth);
// Does children have the current character?
@@ -602,36 +608,17 @@ public class ExpandableDictionary extends Dictionary {
return searchWord(childNode.mChildren, word, depth + 1, childNode);
}
- private void runBigramReverseLookUp(final CharSequence previousWord,
+ private void runBigramReverseLookUp(final String previousWord,
final ArrayList<SuggestedWordInfo> suggestions) {
// Search for the lowercase version of the word only, because that's where bigrams
// store their sons.
- Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
+ final Node prevWord = searchNode(mRoots, previousWord.toLowerCase(), 0,
previousWord.length());
if (prevWord != null && prevWord.mNGrams != null) {
reverseLookUp(prevWord.mNGrams, suggestions);
}
}
- /**
- * Used for testing purposes and in the spell checker
- * This function will wait for loading from database to be done
- */
- void waitForDictionaryLoading() {
- while (mUpdatingDictionary) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- //
- }
- }
- }
-
- protected final void blockingReloadDictionaryIfRequired() {
- reloadDictionaryIfRequired();
- waitForDictionaryLoading();
- }
-
// Local to reverseLookUp, but do not allocate each time.
private final char[] mLookedUpString = new char[BinaryDictionary.MAX_WORD_LENGTH];
@@ -641,7 +628,7 @@ public class ExpandableDictionary extends Dictionary {
* @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,
+ private void reverseLookUp(final LinkedList<NextWord> terminalNodes,
final ArrayList<SuggestedWordInfo> suggestions) {
Node node;
int freq;
@@ -714,7 +701,7 @@ public class ExpandableDictionary extends Dictionary {
}
}
- private static char toLowerCase(char c) {
+ private static char toLowerCase(final char c) {
char baseChar = c;
if (c < BASE_CHARS.length) {
baseChar = BASE_CHARS[c];
diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java
deleted file mode 100644
index 2674e4575..000000000
--- a/java/src/com/android/inputmethod/latin/ImfUtils.java
+++ /dev/null
@@ -1,186 +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 static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
-
-import android.content.Context;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.InputMethodSubtype;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Utility class for Input Method Framework
- */
-public final class ImfUtils {
- private ImfUtils() {
- // This utility class is not publicly instantiable.
- }
-
- private static InputMethodManager sInputMethodManager;
-
- public static InputMethodManager getInputMethodManager(Context context) {
- if (sInputMethodManager == null) {
- sInputMethodManager = (InputMethodManager)context.getSystemService(
- Context.INPUT_METHOD_SERVICE);
- }
- return sInputMethodManager;
- }
-
- private static InputMethodInfo sInputMethodInfoOfThisIme;
-
- public static InputMethodInfo getInputMethodInfoOfThisIme(Context context) {
- if (sInputMethodInfoOfThisIme == null) {
- final InputMethodManager imm = getInputMethodManager(context);
- final String packageName = context.getPackageName();
- for (final InputMethodInfo imi : imm.getInputMethodList()) {
- if (imi.getPackageName().equals(packageName))
- return imi;
- }
- throw new RuntimeException("Can not find input method id for " + packageName);
- }
- return sInputMethodInfoOfThisIme;
- }
-
- public static String getInputMethodIdOfThisIme(Context context) {
- return getInputMethodInfoOfThisIme(context).getId();
- }
-
- public static boolean checkIfSubtypeBelongsToThisImeAndEnabled(Context context,
- InputMethodSubtype ims) {
- final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
- final InputMethodManager imm = getInputMethodManager(context);
- // TODO: Cache all subtypes of this IME for optimization
- final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(myImi, true);
- for (final InputMethodSubtype subtype : subtypes) {
- if (subtype.equals(ims)) {
- return true;
- }
- }
- return false;
- }
-
- public static boolean checkIfSubtypeBelongsToThisIme(Context context,
- InputMethodSubtype ims) {
- final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
- final int count = myImi.getSubtypeCount();
- for (int i = 0; i < count; i++) {
- final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
- if (subtype.equals(ims)) {
- return true;
- }
- }
- 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);
- final List<InputMethodInfo> enabledImis = imm.getEnabledInputMethodList();
- return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, enabledImis);
- }
-
- public static boolean hasMultipleEnabledSubtypesInThisIme(Context context,
- final boolean shouldIncludeAuxiliarySubtypes) {
- final InputMethodInfo myImi = getInputMethodInfoOfThisIme(context);
- final List<InputMethodInfo> imiList = Collections.singletonList(myImi);
- return hasMultipleEnabledSubtypes(context, shouldIncludeAuxiliarySubtypes, imiList);
- }
-
- private static boolean hasMultipleEnabledSubtypes(Context context,
- final boolean shouldIncludeAuxiliarySubtypes, List<InputMethodInfo> imiList) {
- final InputMethodManager imm = getInputMethodManager(context);
-
- // Number of the filtered IMEs
- int filteredImisCount = 0;
-
- for (InputMethodInfo imi : imiList) {
- // We can return true immediately after we find two or more filtered IMEs.
- if (filteredImisCount > 1) return true;
- final List<InputMethodSubtype> subtypes =
- imm.getEnabledInputMethodSubtypeList(imi, true);
- // IMEs that have no subtypes should be counted.
- if (subtypes.isEmpty()) {
- ++filteredImisCount;
- continue;
- }
-
- int auxCount = 0;
- for (InputMethodSubtype subtype : subtypes) {
- if (subtype.isAuxiliary()) {
- ++auxCount;
- }
- }
- final int nonAuxCount = subtypes.size() - auxCount;
-
- // IMEs that have one or more non-auxiliary subtypes should be counted.
- // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
- // subtypes should be counted as well.
- if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
- ++filteredImisCount;
- continue;
- }
- }
-
- if (filteredImisCount > 1) {
- return true;
- }
- final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(null, true);
- int keyboardCount = 0;
- // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
- // both explicitly and implicitly enabled input method subtype.
- // (The current IME should be LatinIME.)
- for (InputMethodSubtype subtype : subtypes) {
- if (KEYBOARD_MODE.equals(subtype.getMode())) {
- ++keyboardCount;
- }
- }
- return keyboardCount > 1;
- }
-
- public static InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(
- Context context, String localeString, String keyboardLayoutSetName) {
- final InputMethodInfo imi = getInputMethodInfoOfThisIme(context);
- final int count = imi.getSubtypeCount();
- for (int i = 0; i < count; i++) {
- final InputMethodSubtype subtype = imi.getSubtypeAt(i);
- final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
- if (localeString.equals(subtype.getLocale())
- && keyboardLayoutSetName.equals(layoutName)) {
- return subtype;
- }
- }
- return null;
- }
-
- public static void setAdditionalInputMethodSubtypes(Context context,
- InputMethodSubtype[] subtypes) {
- final InputMethodManager imm = getInputMethodManager(context);
- final String imiId = getInputMethodIdOfThisIme(context);
- imm.setAdditionalInputMethodSubtypes(imiId, subtypes);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index 422fc72d3..1c5bff383 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -117,36 +117,56 @@ public final class InputAttributes {
if (inputClass == InputType.TYPE_CLASS_DATETIME)
Log.i(TAG, " TYPE_CLASS_DATETIME");
Log.i(TAG, "Variation:");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS))
- Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_ADDRESS");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT))
- Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_SUBJECT");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_FILTER))
- Log.i(TAG, " TYPE_TEXT_VARIATION_FILTER");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE))
- Log.i(TAG, " TYPE_TEXT_VARIATION_LONG_MESSAGE");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_NORMAL))
- Log.i(TAG, " TYPE_TEXT_VARIATION_NORMAL");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PASSWORD))
- Log.i(TAG, " TYPE_TEXT_VARIATION_PASSWORD");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PERSON_NAME))
- Log.i(TAG, " TYPE_TEXT_VARIATION_PERSON_NAME");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_PHONETIC))
- Log.i(TAG, " TYPE_TEXT_VARIATION_PHONETIC");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS))
- Log.i(TAG, " TYPE_TEXT_VARIATION_POSTAL_ADDRESS");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE))
- Log.i(TAG, " TYPE_TEXT_VARIATION_SHORT_MESSAGE");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_URI))
- Log.i(TAG, " TYPE_TEXT_VARIATION_URI");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD))
- Log.i(TAG, " TYPE_TEXT_VARIATION_VISIBLE_PASSWORD");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT))
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EDIT_TEXT");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS))
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS");
- if (0 != (inputType & InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD))
- Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_PASSWORD");
+ switch (InputType.TYPE_MASK_VARIATION & inputType) {
+ case InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_ADDRESS");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_EMAIL_SUBJECT");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_FILTER:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_FILTER");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_LONG_MESSAGE");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_NORMAL:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_NORMAL");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_PASSWORD:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_PASSWORD");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_PERSON_NAME:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_PERSON_NAME");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_PHONETIC:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_PHONETIC");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_POSTAL_ADDRESS");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_SHORT_MESSAGE");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_URI:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_URI");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_VISIBLE_PASSWORD");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EDIT_TEXT");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS");
+ break;
+ case InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD:
+ Log.i(TAG, " TYPE_TEXT_VARIATION_WEB_PASSWORD");
+ break;
+ default:
+ Log.i(TAG, " Unknown variation");
+ break;
+ }
Log.i(TAG, "Flags:");
if (0 != (inputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS))
Log.i(TAG, " TYPE_TEXT_FLAG_NO_SUGGESTIONS");
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 6b48aabb3..7dffd96dd 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.annotations.UsedForTesting;
+
// TODO: This class is not thread-safe.
public final class InputPointers {
private final int mDefaultCapacity;
@@ -39,7 +41,8 @@ public final class InputPointers {
mTimes.add(index, time);
}
- public void addPointer(int x, int y, int pointerId, int time) {
+ @UsedForTesting
+ void addPointer(int x, int y, int pointerId, int time) {
mXCoordinates.add(x);
mYCoordinates.add(y);
mPointerIds.add(pointerId);
@@ -66,7 +69,8 @@ public final class InputPointers {
* @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) {
+ @UsedForTesting
+ void append(InputPointers src, int startPos, int length) {
if (length == 0) {
return;
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 94cdc9b85..44ef01204 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -44,7 +44,7 @@ public final class LastComposedWord {
public final String mTypedWord;
public final String mCommittedWord;
public final String mSeparatorString;
- public final CharSequence mPrevWord;
+ public final String mPrevWord;
public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH);
private boolean mActive;
@@ -56,7 +56,7 @@ public final class LastComposedWord {
// immutable. Do not fiddle with their contents after you passed them to this constructor.
public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers,
final String typedWord, final String committedWord,
- final String separatorString, final CharSequence prevWord) {
+ final String separatorString, final String prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
if (inputPointers != null) {
mInputPointers.copy(inputPointers);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index f416396e8..305f38d8c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -60,8 +60,8 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.CompatUtils;
-import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.keyboard.KeyDetector;
@@ -132,14 +132,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private View mKeyPreviewBackingView;
private View mSuggestionsContainer;
private SuggestionStripView mSuggestionStripView;
- /* package for tests */ Suggest mSuggest;
+ @UsedForTesting Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private ApplicationInfo mTargetApplicationInfo;
- private InputMethodManagerCompatWrapper mImm;
+ private RichInputMethodManager mRichImm;
private Resources mResources;
private SharedPreferences mPrefs;
- /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher;
+ @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
private final SubtypeSwitcher mSubtypeSwitcher;
private final SubtypeState mSubtypeState = new SubtypeState();
@@ -163,17 +163,17 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private int mDeleteCount;
private long mLastKeyTime;
- private AudioAndHapticFeedbackManager mFeedbackManager;
-
// Member variables for remembering the current device orientation.
private int mDisplayOrientation;
// Object for reacting to adding/removing a dictionary pack.
+ // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
private BroadcastReceiver mDictionaryPackInstallReceiver =
- new DictionaryPackInstallBroadcastReceiver(this);
+ ProductionFlag.IS_EXPERIMENTAL
+ ? null : new DictionaryPackInstallBroadcastReceiver(this);
// Keeps track of most recently inserted text (multi-character key) for reverting
- private CharSequence mEnteredText;
+ private String mEnteredText;
private boolean mIsAutoCorrectionIndicatorOn;
@@ -193,8 +193,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
- private long mDoubleSpacesTurnIntoPeriodTimeout;
- private long mDoubleSpaceTimerStart;
+ private long mDoubleSpacePeriodTimeout;
+ private long mDoubleSpacePeriodTimerStart;
public UIHandler(final LatinIME outerInstance) {
super(outerInstance);
@@ -206,8 +206,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
res.getInteger(R.integer.config_delay_update_suggestions);
mDelayUpdateShiftState =
res.getInteger(R.integer.config_delay_update_shift_state);
- mDoubleSpacesTurnIntoPeriodTimeout = res.getInteger(
- R.integer.config_double_spaces_turn_into_period_timeout);
+ mDoubleSpacePeriodTimeout =
+ res.getInteger(R.integer.config_double_space_period_timeout);
}
@Override
@@ -258,17 +258,17 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
.sendToTarget();
}
- public void startDoubleSpacesTimer() {
- mDoubleSpaceTimerStart = SystemClock.uptimeMillis();
+ public void startDoubleSpacePeriodTimer() {
+ mDoubleSpacePeriodTimerStart = SystemClock.uptimeMillis();
}
- public void cancelDoubleSpacesTimer() {
- mDoubleSpaceTimerStart = 0;
+ public void cancelDoubleSpacePeriodTimer() {
+ mDoubleSpacePeriodTimerStart = 0;
}
- public boolean isAcceptingDoubleSpaces() {
- return SystemClock.uptimeMillis() - mDoubleSpaceTimerStart
- < mDoubleSpacesTurnIntoPeriodTimeout;
+ public boolean isAcceptingDoubleSpacePeriod() {
+ return SystemClock.uptimeMillis() - mDoubleSpacePeriodTimerStart
+ < mDoubleSpacePeriodTimeout;
}
// Working variables for the following methods.
@@ -373,9 +373,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mCurrentSubtypeUsed = true;
}
- public void switchSubtype(final IBinder token, final InputMethodManagerCompatWrapper imm,
- final Context context) {
- final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+ public void switchSubtype(final IBinder token, final RichInputMethodManager richImm) {
+ final InputMethodSubtype currentSubtype = richImm.getInputMethodManager()
+ .getCurrentInputMethodSubtype();
final InputMethodSubtype lastActiveSubtype = mLastActiveSubtype;
final boolean currentSubtypeUsed = mCurrentSubtypeUsed;
if (currentSubtypeUsed) {
@@ -383,13 +383,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mCurrentSubtypeUsed = false;
}
if (currentSubtypeUsed
- && ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(context, lastActiveSubtype)
+ && richImm.checkIfSubtypeBelongsToThisImeAndEnabled(lastActiveSubtype)
&& !currentSubtype.equals(lastActiveSubtype)) {
- final String id = ImfUtils.getInputMethodIdOfThisIme(context);
- imm.setInputMethodAndSubtype(token, id, lastActiveSubtype);
+ richImm.setInputMethodAndSubtype(token, lastActiveSubtype);
return;
}
- imm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
+ richImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
}
}
@@ -404,33 +403,28 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
@Override
public void onCreate() {
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- mPrefs = prefs;
- LatinImeLogger.init(this, prefs);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mResources = getResources();
+
+ LatinImeLogger.init(this, mPrefs);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.getInstance().init(this, prefs);
+ ResearchLogger.getInstance().init(this, mPrefs);
}
- InputMethodManagerCompatWrapper.init(this);
+ RichInputMethodManager.init(this, mPrefs);
+ mRichImm = RichInputMethodManager.getInstance();
SubtypeSwitcher.init(this);
- KeyboardSwitcher.init(this, prefs);
+ KeyboardSwitcher.init(this, mPrefs);
AccessibilityUtils.init(this);
super.onCreate();
- mImm = InputMethodManagerCompatWrapper.getInstance();
mHandler.onCreate();
DEBUG = LatinImeLogger.sDBG;
- final Resources res = getResources();
- mResources = res;
-
loadSettings();
-
- ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
-
initSuggest();
- mDisplayOrientation = res.getConfiguration().orientation;
+ mDisplayOrientation = mResources.getConfiguration().orientation;
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
@@ -439,20 +433,23 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
registerReceiver(mReceiver, filter);
- final IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme(SCHEME_PACKAGE);
- registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
+ // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
+ if (!ProductionFlag.IS_EXPERIMENTAL) {
+ final IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme(SCHEME_PACKAGE);
+ registerReceiver(mDictionaryPackInstallReceiver, packageFilter);
- final IntentFilter newDictFilter = new IntentFilter();
- newDictFilter.addAction(
- DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
- registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+ final IntentFilter newDictFilter = new IntentFilter();
+ newDictFilter.addAction(
+ DictionaryPackInstallBroadcastReceiver.NEW_DICTIONARY_INTENT_ACTION);
+ registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+ }
}
// Has to be package-visible for unit tests
- /* package for test */
+ @UsedForTesting
void loadSettings() {
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
@@ -466,7 +463,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
};
mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
- mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings);
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
@@ -569,7 +565,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mSuggest = null;
}
unregisterReceiver(mReceiver);
- unregisterReceiver(mDictionaryPackInstallReceiver);
+ // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
+ if (!ProductionFlag.IS_EXPERIMENTAL) {
+ unregisterReceiver(mDictionaryPackInstallReceiver);
+ }
LatinImeLogger.commit();
LatinImeLogger.onDestroy();
super.onDestroy();
@@ -578,7 +577,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
@Override
public void onConfigurationChanged(final Configuration conf) {
// System locale has been changed. Needs to reload keyboard.
- if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) {
+ if (mSubtypeSwitcher.onConfigurationChanged(conf)) {
loadKeyboard();
}
// If orientation changed while predicting, commit the change
@@ -718,8 +717,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
.updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
if (!currentSubtypeEnabled) {
// Current subtype is disabled. Needs to update subtype and keyboard.
- final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype(
- this, mSubtypeSwitcher.getNoLanguageSubtype());
+ final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype(
+ mSubtypeSwitcher.getNoLanguageSubtype());
mSubtypeSwitcher.updateSubtype(newSubtype);
loadKeyboard();
}
@@ -771,7 +770,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mLastSelectionEnd = editorInfo.initialSelEnd;
mHandler.cancelUpdateSuggestionStrip();
- mHandler.cancelDoubleSpacesTimer();
+ mHandler.cancelDoubleSpacePeriodTimer();
mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
@@ -1086,7 +1085,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
public boolean onEvaluateFullscreenMode() {
// Reread resource value here, because this method is called by framework anytime as needed.
final boolean isFullscreenModeAllowed =
- mCurrentSettings.isFullscreenModeAllowed(getResources());
+ SettingsValues.isFullscreenModeAllowed(getResources());
if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
// TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
// implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI
@@ -1129,7 +1128,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private void commitTyped(final String separatorString) {
if (!mWordComposer.isComposingWord()) return;
- final CharSequence typedWord = mWordComposer.getTypedWord();
+ final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
separatorString);
@@ -1166,7 +1165,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
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) {
+ && lastTwo.charAt(0) == Constants.CODE_SPACE) {
mConnection.deleteSurroundingText(2, 0);
mConnection.commitText(lastTwo.charAt(1) + " ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
@@ -1176,15 +1175,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
}
- private boolean maybeDoubleSpace() {
+ private boolean maybeDoubleSpacePeriod() {
if (!mCurrentSettings.mCorrectionEnabled) return false;
- if (!mHandler.isAcceptingDoubleSpaces()) return false;
+ if (!mCurrentSettings.mUseDoubleSpacePeriod) return false;
+ if (!mHandler.isAcceptingDoubleSpacePeriod()) 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.cancelDoubleSpacesTimer();
+ && canBeFollowedByDoubleSpacePeriod(lastThree.charAt(0))
+ && lastThree.charAt(1) == Constants.CODE_SPACE
+ && lastThree.charAt(2) == Constants.CODE_SPACE) {
+ mHandler.cancelDoubleSpacePeriodTimer();
mConnection.deleteSurroundingText(2, 0);
mConnection.commitText(". ", 1);
mKeyboardSwitcher.updateShiftState();
@@ -1193,16 +1193,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return false;
}
- private static boolean canBeFollowedByPeriod(final int codePoint) {
+ private static boolean canBeFollowedByDoubleSpacePeriod(final int codePoint) {
// TODO: Check again whether there really ain't a better way to check this.
// TODO: This should probably be language-dependant...
return Character.isLetterOrDigit(codePoint)
- || codePoint == Keyboard.CODE_SINGLE_QUOTE
- || codePoint == Keyboard.CODE_DOUBLE_QUOTE
- || codePoint == Keyboard.CODE_CLOSING_PARENTHESIS
- || codePoint == Keyboard.CODE_CLOSING_SQUARE_BRACKET
- || codePoint == Keyboard.CODE_CLOSING_CURLY_BRACKET
- || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
+ || codePoint == Constants.CODE_SINGLE_QUOTE
+ || codePoint == Constants.CODE_DOUBLE_QUOTE
+ || codePoint == Constants.CODE_CLOSING_PARENTHESIS
+ || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
+ || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
+ || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET;
}
// Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
@@ -1230,9 +1230,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (isShowingOptionDialog()) return false;
switch (requestCode) {
case CODE_SHOW_INPUT_METHOD_PICKER:
- if (ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
- this, true /* include aux subtypes */)) {
- mImm.showInputMethodPicker();
+ if (mRichImm.hasMultipleEnabledIMEsOrSubtypes(true /* include aux subtypes */)) {
+ mRichImm.getInputMethodManager().showInputMethodPicker();
return true;
}
return false;
@@ -1256,10 +1255,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private void handleLanguageSwitchKey() {
final IBinder token = getWindow().getWindow().getAttributes().token;
if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
- mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
+ mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
return;
}
- mSubtypeState.switchSubtype(token, mImm, this);
+ mSubtypeState.switchSubtype(token, mRichImm);
}
private void sendDownUpKeyEventForBackwardCompatibility(final int code) {
@@ -1285,7 +1284,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// 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
+ if (Constants.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
@@ -1302,7 +1301,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
@Override
public void onCodeInput(final int primaryCode, final int x, final int y) {
final long when = SystemClock.uptimeMillis();
- if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
+ if (primaryCode != Constants.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
mDeleteCount = 0;
}
mLastKeyTime = when;
@@ -1316,43 +1315,43 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
final int spaceState = mSpaceState;
if (!mWordComposer.isComposingWord()) mIsAutoCorrectionIndicatorOn = false;
- // TODO: Consolidate the double space timer, mLastKeyTime, and the space state.
- if (primaryCode != Keyboard.CODE_SPACE) {
- mHandler.cancelDoubleSpacesTimer();
+ // TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
+ if (primaryCode != Constants.CODE_SPACE) {
+ mHandler.cancelDoubleSpacePeriodTimer();
}
boolean didAutoCorrect = false;
switch (primaryCode) {
- case Keyboard.CODE_DELETE:
+ case Constants.CODE_DELETE:
mSpaceState = SPACE_STATE_NONE;
handleBackspace(spaceState);
mDeleteCount++;
mExpectingUpdateSelection = true;
LatinImeLogger.logOnDelete(x, y);
break;
- case Keyboard.CODE_SHIFT:
- case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
+ case Constants.CODE_SHIFT:
+ case Constants.CODE_SWITCH_ALPHA_SYMBOL:
// Shift and symbol key is handled in onPressKey() and onReleaseKey().
break;
- case Keyboard.CODE_SETTINGS:
+ case Constants.CODE_SETTINGS:
onSettingsKeyPressed();
break;
- case Keyboard.CODE_SHORTCUT:
+ case Constants.CODE_SHORTCUT:
mSubtypeSwitcher.switchToShortcutIME(this);
break;
- case Keyboard.CODE_ACTION_ENTER:
+ case Constants.CODE_ACTION_ENTER:
performEditorAction(getActionId(switcher.getKeyboard()));
break;
- case Keyboard.CODE_ACTION_NEXT:
+ case Constants.CODE_ACTION_NEXT:
performEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
- case Keyboard.CODE_ACTION_PREVIOUS:
+ case Constants.CODE_ACTION_PREVIOUS:
performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
break;
- case Keyboard.CODE_LANGUAGE_SWITCH:
+ case Constants.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
- case Keyboard.CODE_RESEARCH:
+ case Constants.CODE_RESEARCH:
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.getInstance().onResearchKeySelected(this);
}
@@ -1387,10 +1386,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
switcher.onCodeInput(primaryCode);
// Reset after any single keystroke, except shift and symbol-shift
- if (!didAutoCorrect && primaryCode != Keyboard.CODE_SHIFT
- && primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
+ if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT
+ && primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
- if (Keyboard.CODE_DELETE != primaryCode) {
+ if (Constants.CODE_DELETE != primaryCode) {
mEnteredText = null;
}
mConnection.endBatchEdit();
@@ -1401,7 +1400,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from PointerTracker through the KeyboardActionListener interface
@Override
- public void onTextInput(final CharSequence rawText) {
+ public void onTextInput(final String rawText) {
mConnection.beginBatchEdit();
if (mWordComposer.isComposingWord()) {
commitCurrentAutoCorrection(rawText.toString());
@@ -1409,7 +1408,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
resetComposingState(true /* alsoResetLastComposedWord */);
}
mHandler.postUpdateSuggestionStrip();
- final CharSequence text = specificTldProcessingOnTextInput(rawText);
+ final String text = specificTldProcessingOnTextInput(rawText);
if (SPACE_STATE_PHANTOM == mSpaceState) {
promotePhantomSpace();
}
@@ -1418,7 +1417,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Space state must be updated before calling updateShiftState
mSpaceState = SPACE_STATE_NONE;
mKeyboardSwitcher.updateShiftState();
- mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
+ mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT);
mEnteredText = text;
}
@@ -1466,7 +1465,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private static final class BatchInputUpdater implements Handler.Callback {
private final Handler mHandler;
private LatinIME mLatinIme;
- private boolean mInBatchInput; // synchornized using "this".
+ private boolean mInBatchInput; // synchronized using "this".
private BatchInputUpdater() {
final HandlerThread handlerThread = new HandlerThread(
@@ -1498,6 +1497,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Run in the UI thread.
public synchronized void onStartBatchInput() {
+ mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
mInBatchInput = true;
}
@@ -1505,7 +1505,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private synchronized void updateBatchInput(final InputPointers batchPointers,
final LatinIME latinIme) {
if (!mInBatchInput) {
- // Batch input has ended while the message was being delivered.
+ // Batch input has ended or canceled while the message was being delivered.
return;
}
final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
@@ -1525,6 +1525,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
.sendToTarget();
}
+ public synchronized void onCancelBatchInput(final LatinIME latinIme) {
+ mInBatchInput = false;
+ latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
+ SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
+ }
+
// Run in the UI thread.
public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers,
final LatinIME latinIme) {
@@ -1547,13 +1553,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
final boolean dismissGestureFloatingPreviewText) {
- final String batchInputText = (suggestedWords.size() > 0)
- ? suggestedWords.getWord(0) : null;
- final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
showSuggestionStrip(suggestedWords, null);
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
if (dismissGestureFloatingPreviewText) {
mainKeyboardView.dismissGestureFloatingPreviewText();
+ } else {
+ final String batchInputText = suggestedWords.isEmpty()
+ ? null : suggestedWords.getWord(0);
+ mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
}
}
@@ -1566,8 +1573,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
public void onEndBatchInput(final InputPointers batchPointers) {
final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
batchPointers, this);
- final String batchInputText = (suggestedWords.size() > 0)
- ? suggestedWords.getWord(0) : null;
+ final String batchInputText = suggestedWords.isEmpty()
+ ? null : suggestedWords.getWord(0);
if (TextUtils.isEmpty(batchInputText)) {
return;
}
@@ -1584,8 +1591,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mKeyboardSwitcher.updateShiftState();
}
- private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
- if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
+ private String specificTldProcessingOnTextInput(final String text) {
+ if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
|| !Character.isLetter(text.charAt(1))) {
// Not a tld: do nothing.
return text;
@@ -1596,8 +1603,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code
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());
+ && lastOne.charAt(0) == Constants.CODE_PERIOD) {
+ return text.substring(1);
} else {
return text;
}
@@ -1610,6 +1617,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mKeyboardSwitcher.onCancelInput();
}
+ @Override
+ public void onCancelBatchInput() {
+ BatchInputUpdater.getInstance().onCancelBatchInput(this);
+ }
+
private void handleBackspace(final int spaceState) {
// In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing
@@ -1619,8 +1631,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (mWordComposer.isComposingWord()) {
final int length = mWordComposer.size();
if (length > 0) {
- // Immediately after a batch input.
- if (SPACE_STATE_PHANTOM == spaceState) {
+ if (mWordComposer.isBatchMode()) {
mWordComposer.reset();
} else {
mWordComposer.deleteLast();
@@ -1651,7 +1662,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return;
}
if (SPACE_STATE_DOUBLE == spaceState) {
- mHandler.cancelDoubleSpacesTimer();
+ mHandler.cancelDoubleSpacePeriodTimer();
if (mConnection.revertDoubleSpace()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
@@ -1701,7 +1712,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private boolean maybeStripSpace(final int code,
final int spaceState, final boolean isFromSuggestionStrip) {
- if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
+ if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
mConnection.removeTrailingSpace();
return false;
} else if ((SPACE_STATE_WEAK == spaceState
@@ -1744,7 +1755,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// 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
// position where it should not start composing a word.
- isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode);
+ isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode);
// Here we don't need to reset the last composed word. It will be reset
// when we commit this one, if we ever do; if on the other hand we backspace
// it entirely and resume suggestions on the previous word, we'd like to still
@@ -1753,15 +1764,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
if (isComposingWord) {
final int keyX, keyY;
- if (KeyboardActionListener.Adapter.isInvalidCoordinate(x)
- || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) {
- keyX = x;
- keyY = y;
- } else {
+ if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) {
final KeyDetector keyDetector =
mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
keyX = keyDetector.getTouchX(x);
keyY = keyDetector.getTouchY(y);
+ } else {
+ keyX = x;
+ keyY = y;
}
mWordComposer.add(primaryCode, keyX, keyY);
// If it's the first letter, make note of auto-caps state
@@ -1812,16 +1822,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
sendKeyCodePoint(primaryCode);
- if (Keyboard.CODE_SPACE == primaryCode) {
+ if (Constants.CODE_SPACE == primaryCode) {
if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
- if (maybeDoubleSpace()) {
+ if (maybeDoubleSpacePeriod()) {
mSpaceState = SPACE_STATE_DOUBLE;
} else if (!isShowingPunctuationList()) {
mSpaceState = SPACE_STATE_WEAK;
}
}
- mHandler.startDoubleSpacesTimer();
+ mHandler.startDoubleSpacePeriodTimer();
if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
mHandler.postUpdateSuggestionStrip();
}
@@ -1857,7 +1867,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return didAutoCorrect;
}
- private CharSequence getTextWithUnderline(final CharSequence text) {
+ private CharSequence getTextWithUnderline(final String text) {
return mIsAutoCorrectionIndicatorOn
? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
: text;
@@ -1874,7 +1884,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// TODO: make this private
// Outside LatinIME, only used by the test suite.
- /* package for tests */
+ @UsedForTesting
boolean isShowingPunctuationList() {
if (mSuggestionStripView == null) return false;
return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
@@ -1944,7 +1954,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private SuggestedWords getSuggestedWords(final int sessionId) {
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
- if (keyboard == null) {
+ if (keyboard == null || mSuggest == null) {
return SuggestedWords.EMPTY;
}
final String typedWord = mWordComposer.getTypedWord();
@@ -1952,7 +1962,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// 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 =
+ final String prevWord =
mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
mWordComposer.isComposingWord() ? 2 : 1);
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
@@ -1961,7 +1971,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
}
- private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
+ private SuggestedWords maybeRetrieveOlderSuggestions(final String 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
@@ -1991,21 +2001,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
}
- private void showSuggestionStrip(final SuggestedWords suggestedWords,
- final CharSequence typedWord) {
- if (null == suggestedWords || suggestedWords.size() <= 0) {
+ private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) {
+ if (suggestedWords.isEmpty()) {
clearSuggestionStrip();
return;
}
- final CharSequence autoCorrection;
- if (suggestedWords.size() > 0) {
- if (suggestedWords.mWillAutoCorrect) {
- autoCorrection = suggestedWords.getWord(1);
- } else {
- autoCorrection = typedWord;
- }
+ final String autoCorrection;
+ if (suggestedWords.mWillAutoCorrect) {
+ autoCorrection = suggestedWords.getWord(1);
} else {
- autoCorrection = null;
+ autoCorrection = typedWord;
}
mWordComposer.setAutoCorrection(autoCorrection);
final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
@@ -2019,9 +2024,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (mHandler.hasPendingUpdateSuggestions()) {
updateSuggestionStrip();
}
- final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
final String typedWord = mWordComposer.getTypedWord();
- final CharSequence autoCorrection = (typedAutoCorrection != null)
+ final String autoCorrection = (typedAutoCorrection != null)
? typedAutoCorrection : typedWord;
if (autoCorrection != null) {
if (TextUtils.isEmpty(typedWord)) {
@@ -2052,7 +2057,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
// interface
@Override
- public void pickSuggestionManually(final int index, final CharSequence suggestion) {
+ public void pickSuggestionManually(final int index, final String suggestion) {
final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
@@ -2122,7 +2127,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
&& !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
if (ProductionFlag.IS_INTERNAL) {
- Stats.onSeparator((char)Keyboard.CODE_SPACE,
+ Stats.onSeparator((char)Constants.CODE_SPACE,
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
@@ -2137,13 +2142,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
/**
* Commits the chosen word to the text field and saves it for later retrieval.
*/
- private void commitChosenWord(final CharSequence chosenWord, final int commitType,
+ private void commitChosenWord(final String chosenWord, final int commitType,
final String separatorString) {
final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
// Add the word to the user history dictionary
- final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
+ final String prevWord = addToUserHistoryDictionary(chosenWord);
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
// LastComposedWord#didCommitTypedWord by string equality of the remembered
@@ -2162,7 +2167,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
setSuggestionStripShown(isSuggestionsStripVisible());
}
- private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
+ private String addToUserHistoryDictionary(final String suggestion) {
if (TextUtils.isEmpty(suggestion)) return null;
if (mSuggest == null) return null;
@@ -2177,19 +2182,18 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
= mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
final String secondWord;
if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
- secondWord = suggestion.toString().toLowerCase(
- mSubtypeSwitcher.getCurrentSubtypeLocale());
+ secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
- secondWord = suggestion.toString();
+ secondWord = suggestion;
}
// We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
// We don't add words with 0-frequency (assuming they would be profanity etc.).
final int maxFreq = AutoCorrection.getMaxFrequency(
mSuggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
- userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
- secondWord, maxFreq > 0);
- return prevWord;
+ final String prevWordString = (null == prevWord) ? null : prevWord.toString();
+ userHistoryDictionary.addToUserHistory(prevWordString, secondWord, maxFreq > 0);
+ return prevWordString;
}
return null;
}
@@ -2214,9 +2218,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
private void revertCommit() {
- final CharSequence previousWord = mLastComposedWord.mPrevWord;
+ final String previousWord = mLastComposedWord.mPrevWord;
final String originallyTypedWord = mLastComposedWord.mTypedWord;
- final CharSequence committedWord = mLastComposedWord.mCommittedWord;
+ final String committedWord = mLastComposedWord.mCommittedWord;
final int cancelLength = committedWord.length();
final int separatorLength = LastComposedWord.getSeparatorLength(
mLastComposedWord.mSeparatorString);
@@ -2226,9 +2230,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (mWordComposer.isComposingWord()) {
throw new RuntimeException("revertCommit, but we are composing a word");
}
- final String wordBeforeCursor =
+ final CharSequence wordBeforeCursor =
mConnection.getTextBeforeCursor(deleteLength, 0)
- .subSequence(0, cancelLength).toString();
+ .subSequence(0, cancelLength);
if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were "
+ "reverting \"" + committedWord
@@ -2258,7 +2262,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// This essentially inserts a space, and that's it.
public void promotePhantomSpace() {
if (mCurrentSettings.shouldInsertSpacesAutomatically()) {
- sendKeyCodePoint(Keyboard.CODE_SPACE);
+ sendKeyCodePoint(Constants.CODE_SPACE);
}
}
@@ -2269,7 +2273,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// TODO: Make this private
// Outside LatinIME, only used by the {@link InputTestsBase} test suite.
- /* package for test */
+ @UsedForTesting
void loadKeyboard() {
// When the device locale is changed in SetupWizard etc., this method may get called via
// onConfigurationChanged before SoftInputWindow is shown.
@@ -2285,13 +2289,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mHandler.postUpdateSuggestionStrip();
}
- // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
- // {@link KeyboardSwitcher}. Called from KeyboardSwitcher
- public void hapticAndAudioFeedback(final int primaryCode) {
- mFeedbackManager.hapticAndAudioFeedback(
- primaryCode, mKeyboardSwitcher.getMainKeyboardView());
- }
-
// Callback called by PointerTracker through the KeyboardActionListener. This is called when a
// key is depressed; release matching call is onReleaseKey below.
@Override
@@ -2308,16 +2305,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// If accessibility is on, ensure the user receives keyboard state updates.
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
switch (primaryCode) {
- case Keyboard.CODE_SHIFT:
+ case Constants.CODE_SHIFT:
AccessibleKeyboardViewProxy.getInstance().notifyShiftState();
break;
- case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
+ case Constants.CODE_SWITCH_ALPHA_SYMBOL:
AccessibleKeyboardViewProxy.getInstance().notifySymbolsState();
break;
}
}
- if (Keyboard.CODE_DELETE == primaryCode) {
+ if (Constants.CODE_DELETE == primaryCode) {
// 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.
@@ -2337,7 +2334,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
mSubtypeSwitcher.onNetworkStateChanged(intent);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- mFeedbackManager.onRingerModeChanged();
+ mKeyboardSwitcher.onRingerModeChanged();
}
}
};
@@ -2374,7 +2371,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
getString(R.string.language_selection_title),
getString(R.string.english_ime_settings),
};
- final Context context = this;
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface di, int position) {
@@ -2382,7 +2378,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
switch (position) {
case 0:
Intent intent = CompatUtils.getInputLanguageSelectionIntent(
- ImfUtils.getInputMethodIdOfThisIme(context),
+ mRichImm.getInputMethodIdOfThisIme(),
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 75b67bfc6..86127466f 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -26,7 +26,6 @@ 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;
@@ -66,12 +65,6 @@ public final class RichInputConnection {
* This contains the currently composing text, as LatinIME thinks the TextView is seeing it.
*/
private StringBuilder mComposingText = new StringBuilder();
- /**
- * This is a one-character string containing the character after the cursor. Since LatinIME
- * never touches it directly, it's never modified by any means other than re-reading from the
- * TextView when the cursor position is changed by the user.
- */
- private CharSequence mCharAfterTheCursor = "";
// A hint on how many characters to cache from the TextView. A good value of this is given by
// how many characters we need to be able to almost always find the caps mode.
private static final int DEFAULT_TEXT_CACHE_SIZE = 100;
@@ -147,7 +140,6 @@ public final class RichInputConnection {
mCommittedTextBeforeComposingText.setLength(0);
final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0);
if (null != textBeforeCursor) mCommittedTextBeforeComposingText.append(textBeforeCursor);
- mCharAfterTheCursor = getTextAfterCursor(1, 0);
if (null != mIC) {
mIC.finishComposingText();
if (ProductionFlag.IS_EXPERIMENTAL) {
@@ -399,7 +391,8 @@ public final class RichInputConnection {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
- public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) {
+ @SuppressWarnings("unused")
+ public String getNthPreviousWord(final String sentenceSeperators, final int n) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) return null;
final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
@@ -467,19 +460,22 @@ public final class RichInputConnection {
// (n = 2) "abc|" -> null
// (n = 2) "abc |" -> null
// (n = 2) "abc. def|" -> null
- public static CharSequence getNthPreviousWord(final CharSequence prev,
+ public static String getNthPreviousWord(final CharSequence prev,
final String sentenceSeperators, final int n) {
if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
+ final 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 (w.length < n) return null;
+ final String nthPrevWord = w[w.length - n];
+ final int length = nthPrevWord.length();
+ if (length <= 0) return null;
// If ends in a separator, return null
- char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1);
+ final char lastChar = nthPrevWord.charAt(length - 1);
if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
- return w[w.length - n];
+ return nthPrevWord;
}
/**
@@ -512,19 +508,20 @@ public final class RichInputConnection {
* be included in the returned range
* @return a range containing the text surrounding the cursor
*/
- public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) {
+ public Range getWordRangeAtCursor(final String sep, final int additionalPrecedingWordsCount) {
mIC = mParent.getCurrentInputConnection();
if (mIC == null || sep == null) {
return null;
}
- CharSequence before = mIC.getTextBeforeCursor(1000, 0);
- CharSequence after = mIC.getTextAfterCursor(1000, 0);
+ final CharSequence before = mIC.getTextBeforeCursor(1000, 0);
+ final 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 count = additionalPrecedingWordsCount;
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
@@ -541,7 +538,7 @@ public final class RichInputConnection {
// 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)) {
+ if (isStoppingAtWhitespace && (--count < 0)) {
break; // outer loop
}
isStoppingAtWhitespace = !isStoppingAtWhitespace;
@@ -559,7 +556,7 @@ public final class RichInputConnection {
}
}
- int cursor = getCursorPosition();
+ final 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);
@@ -570,8 +567,8 @@ public final class RichInputConnection {
}
public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
- CharSequence before = getTextBeforeCursor(1, 0);
- CharSequence after = getTextAfterCursor(1, 0);
+ final CharSequence before = getTextBeforeCursor(1, 0);
+ final CharSequence after = getTextAfterCursor(1, 0);
if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0))
&& !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
return true;
@@ -587,7 +584,7 @@ public final class RichInputConnection {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
final CharSequence lastOne = getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
- && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
+ && lastOne.charAt(0) == Constants.CODE_SPACE) {
deleteSurroundingText(1, 0);
}
}
@@ -613,7 +610,7 @@ public final class RichInputConnection {
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)) {
+ while (!TextUtils.isEmpty(word) && Constants.CODE_SINGLE_QUOTE == word.charAt(0)) {
word = word.subSequence(1, word.length());
}
if (TextUtils.isEmpty(word)) return null;
@@ -665,7 +662,7 @@ public final class RichInputConnection {
// 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))) {
+ || (Constants.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.
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
new file mode 100644
index 000000000..af0d61cc7
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -0,0 +1,248 @@
+/*
+ * 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 static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.IBinder;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Enrichment class for InputMethodManager to simplify interaction and add functionality.
+ */
+public final class RichInputMethodManager {
+ private static final String TAG = RichInputMethodManager.class.getSimpleName();
+
+ private RichInputMethodManager() {
+ // This utility class is not publicly instantiable.
+ }
+
+ private static final RichInputMethodManager sInstance = new RichInputMethodManager();
+
+ private InputMethodManagerCompatWrapper mImmWrapper;
+ private InputMethodInfo mInputMethodInfoOfThisIme;
+
+ public static RichInputMethodManager getInstance() {
+ sInstance.checkInitialized();
+ return sInstance;
+ }
+
+ public static void init(final Context context, final SharedPreferences prefs) {
+ sInstance.initInternal(context, prefs);
+ }
+
+ private boolean isInitialized() {
+ return mImmWrapper != null;
+ }
+
+ private void checkInitialized() {
+ if (!isInitialized()) {
+ throw new RuntimeException(TAG + " is used before initialization");
+ }
+ }
+
+ private void initInternal(final Context context, final SharedPreferences prefs) {
+ if (isInitialized()) {
+ return;
+ }
+ mImmWrapper = new InputMethodManagerCompatWrapper(context);
+ mInputMethodInfoOfThisIme = getInputMethodInfoOfThisIme(context);
+
+ // Initialize additional subtypes.
+ SubtypeLocale.init(context);
+ final String prefAdditionalSubtypes = SettingsValues.getPrefAdditionalSubtypes(
+ prefs, context.getResources());
+ final InputMethodSubtype[] additionalSubtypes =
+ AdditionalSubtype.createAdditionalSubtypesArray(prefAdditionalSubtypes);
+ setAdditionalInputMethodSubtypes(additionalSubtypes);
+ }
+
+ public InputMethodManager getInputMethodManager() {
+ checkInitialized();
+ return mImmWrapper.mImm;
+ }
+
+ private InputMethodInfo getInputMethodInfoOfThisIme(final Context context) {
+ final String packageName = context.getPackageName();
+ for (final InputMethodInfo imi : mImmWrapper.mImm.getInputMethodList()) {
+ if (imi.getPackageName().equals(packageName)) {
+ return imi;
+ }
+ }
+ throw new RuntimeException("Input method id for " + packageName + " not found.");
+ }
+
+ public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
+ final boolean result = mImmWrapper.switchToNextInputMethod(token, onlyCurrentIme);
+ if (!result) {
+ mImmWrapper.mImm.switchToLastInputMethod(token);
+ return false;
+ }
+ return true;
+ }
+
+ public InputMethodInfo getInputMethodInfoOfThisIme() {
+ return mInputMethodInfoOfThisIme;
+ }
+
+ public String getInputMethodIdOfThisIme() {
+ return mInputMethodInfoOfThisIme.getId();
+ }
+
+ public boolean checkIfSubtypeBelongsToThisImeAndEnabled(final InputMethodSubtype subtype) {
+ return checkIfSubtypeBelongsToImeAndEnabled(mInputMethodInfoOfThisIme, subtype);
+ }
+
+ public boolean checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(
+ final InputMethodSubtype subtype) {
+ final boolean subtypeEnabled = checkIfSubtypeBelongsToThisImeAndEnabled(subtype);
+ final boolean subtypeExplicitlyEnabled = checkIfSubtypeBelongsToList(
+ subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList(
+ mInputMethodInfoOfThisIme, false /* allowsImplicitlySelectedSubtypes */));
+ return subtypeEnabled && !subtypeExplicitlyEnabled;
+ }
+
+ public boolean checkIfSubtypeBelongsToImeAndEnabled(final InputMethodInfo imi,
+ final InputMethodSubtype subtype) {
+ return checkIfSubtypeBelongsToList(
+ subtype, mImmWrapper.mImm.getEnabledInputMethodSubtypeList(
+ imi, true /* allowsImplicitlySelectedSubtypes */));
+ }
+
+ private static boolean checkIfSubtypeBelongsToList(final InputMethodSubtype subtype,
+ final List<InputMethodSubtype> subtypes) {
+ for (final InputMethodSubtype ims : subtypes) {
+ if (ims.equals(subtype)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean checkIfSubtypeBelongsToThisIme(final InputMethodSubtype subtype) {
+ final InputMethodInfo myImi = mInputMethodInfoOfThisIme;
+ final int count = myImi.getSubtypeCount();
+ for (int i = 0; i < count; i++) {
+ final InputMethodSubtype ims = myImi.getSubtypeAt(i);
+ if (ims.equals(subtype)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public InputMethodSubtype getCurrentInputMethodSubtype(
+ final InputMethodSubtype defaultSubtype) {
+ final InputMethodSubtype currentSubtype = mImmWrapper.mImm.getCurrentInputMethodSubtype();
+ return (currentSubtype != null) ? currentSubtype : defaultSubtype;
+ }
+
+ public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> enabledImis = mImmWrapper.mImm.getEnabledInputMethodList();
+ return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, enabledImis);
+ }
+
+ public boolean hasMultipleEnabledSubtypesInThisIme(
+ final boolean shouldIncludeAuxiliarySubtypes) {
+ final List<InputMethodInfo> imiList = Collections.singletonList(mInputMethodInfoOfThisIme);
+ return hasMultipleEnabledSubtypes(shouldIncludeAuxiliarySubtypes, imiList);
+ }
+
+ private boolean hasMultipleEnabledSubtypes(final boolean shouldIncludeAuxiliarySubtypes,
+ final List<InputMethodInfo> imiList) {
+ // Number of the filtered IMEs
+ int filteredImisCount = 0;
+
+ for (InputMethodInfo imi : imiList) {
+ // We can return true immediately after we find two or more filtered IMEs.
+ if (filteredImisCount > 1) return true;
+ final List<InputMethodSubtype> subtypes =
+ mImmWrapper.mImm.getEnabledInputMethodSubtypeList(imi, true);
+ // IMEs that have no subtypes should be counted.
+ if (subtypes.isEmpty()) {
+ ++filteredImisCount;
+ continue;
+ }
+
+ int auxCount = 0;
+ for (InputMethodSubtype subtype : subtypes) {
+ if (subtype.isAuxiliary()) {
+ ++auxCount;
+ }
+ }
+ final int nonAuxCount = subtypes.size() - auxCount;
+
+ // IMEs that have one or more non-auxiliary subtypes should be counted.
+ // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary
+ // subtypes should be counted as well.
+ if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) {
+ ++filteredImisCount;
+ continue;
+ }
+ }
+
+ if (filteredImisCount > 1) {
+ return true;
+ }
+ final List<InputMethodSubtype> subtypes =
+ mImmWrapper.mImm.getEnabledInputMethodSubtypeList(null, true);
+ int keyboardCount = 0;
+ // imm.getEnabledInputMethodSubtypeList(null, true) will return the current IME's
+ // both explicitly and implicitly enabled input method subtype.
+ // (The current IME should be LatinIME.)
+ for (InputMethodSubtype subtype : subtypes) {
+ if (KEYBOARD_MODE.equals(subtype.getMode())) {
+ ++keyboardCount;
+ }
+ }
+ return keyboardCount > 1;
+ }
+
+ public InputMethodSubtype findSubtypeByLocaleAndKeyboardLayoutSet(final String localeString,
+ final String keyboardLayoutSetName) {
+ final InputMethodInfo myImi = mInputMethodInfoOfThisIme;
+ final int count = myImi.getSubtypeCount();
+ for (int i = 0; i < count; i++) {
+ final InputMethodSubtype subtype = myImi.getSubtypeAt(i);
+ final String layoutName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
+ if (localeString.equals(subtype.getLocale())
+ && keyboardLayoutSetName.equals(layoutName)) {
+ return subtype;
+ }
+ }
+ return null;
+ }
+
+ public void setInputMethodAndSubtype(final IBinder token, final InputMethodSubtype subtype) {
+ mImmWrapper.mImm.setInputMethodAndSubtype(
+ token, mInputMethodInfoOfThisIme.getId(), subtype);
+ }
+
+ public void setAdditionalInputMethodSubtypes(final InputMethodSubtype[] subtypes) {
+ mImmWrapper.mImm.setAdditionalInputMethodSubtypes(
+ mInputMethodInfoOfThisIme.getId(), subtypes);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 348928df8..7a73cade3 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -60,6 +60,8 @@ public final class Settings extends InputMethodSettingsFragment
"last_user_dictionary_write_time";
public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
+ public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD =
+ "pref_key_use_double_space_period";
public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
"pref_show_language_switch_key";
public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
@@ -203,7 +205,8 @@ public final class Settings extends InputMethodSettingsFragment
final Intent intent = dictionaryLink.getIntent();
final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
- if (0 >= number) {
+ // TODO: The experimental version is not supported by the Dictionary Pack Service yet
+ if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) {
textCorrectionGroup.removePreference(dictionaryLink);
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 2f49fe92e..a23876722 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -22,7 +22,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.keyboard.internal.KeySpecParser;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -76,13 +75,13 @@ public final class SettingsValues {
@SuppressWarnings("unused") // TODO: Use this
private final String mKeyPreviewPopupDismissDelayRawValue;
public final boolean mUseContactsDict;
+ public final boolean mUseDoubleSpacePeriod;
// Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled;
@SuppressWarnings("unused") // TODO: Use this
private final int mVibrationDurationSettingsRawValue;
@SuppressWarnings("unused") // TODO: Use this
private final float mKeypressSoundVolumeRawValue;
- private final InputMethodSubtype[] mAdditionalSubtypes;
public final boolean mGestureInputEnabled;
public final boolean mGesturePreviewTrailEnabled;
public final boolean mGestureFloatingPreviewTextEnabled;
@@ -156,6 +155,7 @@ public final class SettingsValues {
Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout)));
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
+ mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
mBigramPredictionEnabled = isBigramPredictionEnabled(prefs, res);
mVibrationDurationSettingsRawValue =
@@ -170,8 +170,6 @@ public final class SettingsValues {
mAutoCorrectionThresholdRawValue);
mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff);
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
@@ -359,16 +357,15 @@ public final class SettingsValues {
return prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
}
- public boolean isLanguageSwitchKeyEnabled(final Context context) {
+ public boolean isLanguageSwitchKeyEnabled() {
if (!mShowsLanguageSwitchKey) {
return false;
}
+ final RichInputMethodManager imm = RichInputMethodManager.getInstance();
if (mIncludesOtherImesInLanguageSwitchList) {
- return ImfUtils.hasMultipleEnabledIMEsOrSubtypes(
- context, /* include aux subtypes */false);
+ return imm.hasMultipleEnabledIMEsOrSubtypes(false /* include aux subtypes */);
} else {
- return ImfUtils.hasMultipleEnabledSubtypesInThisIme(
- context, /* include aux subtypes */false);
+ return imm.hasMultipleEnabledSubtypesInThisIme(false /* include aux subtypes */);
}
}
@@ -376,10 +373,6 @@ public final class SettingsValues {
return res.getBoolean(R.bool.config_use_fullscreen_mode);
}
- public InputMethodSubtype[] getAdditionalSubtypes() {
- return mAdditionalSubtypes;
- }
-
public static String getPrefAdditionalSubtypes(final SharedPreferences prefs,
final Resources res) {
final String predefinedPrefSubtypes = AdditionalSubtype.createPrefSubtypes(
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index df7709892..043043cef 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -16,10 +16,9 @@
package com.android.inputmethod.latin;
+import android.text.InputType;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.Keyboard; // For character constants
-
import java.util.ArrayList;
import java.util.Locale;
@@ -28,30 +27,30 @@ public final class StringUtils {
// This utility class is not publicly instantiable.
}
- public static int codePointCount(String text) {
+ public static int codePointCount(final String text) {
if (TextUtils.isEmpty(text)) return 0;
return text.codePointCount(0, text.length());
}
- public static boolean containsInArray(String key, String[] array) {
+ public static boolean containsInArray(final String key, final String[] array) {
for (final String element : array) {
if (key.equals(element)) return true;
}
return false;
}
- public static boolean containsInCsv(String key, String csv) {
+ public static boolean containsInCsv(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return false;
return containsInArray(key, csv.split(","));
}
- public static String appendToCsvIfNotExists(String key, String csv) {
+ public static String appendToCsvIfNotExists(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return key;
if (containsInCsv(key, csv)) return csv;
return csv + "," + key;
}
- public static String removeFromCsvIfExists(String key, String csv) {
+ public static String removeFromCsvIfExists(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return "";
final String[] elements = csv.split(",");
if (!containsInArray(key, elements)) return csv;
@@ -63,82 +62,20 @@ public final class StringUtils {
}
/**
- * Returns true if a and b are equal ignoring the case of the character.
- * @param a first character to check
- * @param b second character to check
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- */
- public static boolean equalsIgnoreCase(char a, char b) {
- // Some language, such as Turkish, need testing both cases.
- return a == b
- || Character.toLowerCase(a) == Character.toLowerCase(b)
- || Character.toUpperCase(a) == Character.toUpperCase(b);
- }
-
- /**
- * Returns true if a and b are equal ignoring the case of the characters, including if they are
- * both null.
- * @param a first CharSequence to check
- * @param b second CharSequence to check
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- */
- public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
- if (a == b)
- return true; // including both a and b are null.
- if (a == null || b == null)
- return false;
- final int length = a.length();
- if (length != b.length())
- return false;
- for (int i = 0; i < length; i++) {
- if (!equalsIgnoreCase(a.charAt(i), b.charAt(i)))
- return false;
- }
- return true;
- }
-
- /**
- * Returns true if a and b are equal ignoring the case of the characters, including if a is null
- * and b is zero length.
- * @param a CharSequence to check
- * @param b character array to check
- * @param offset start offset of array b
- * @param length length of characters in array b
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- * @throws IndexOutOfBoundsException
- * if {@code offset < 0 || length < 0 || offset + length > data.length}.
- * @throws NullPointerException if {@code b == null}.
- */
- public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) {
- if (offset < 0 || length < 0 || length > b.length - offset)
- throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset
- + " length=" + length);
- if (a == null)
- return length == 0; // including a is null and b is zero length.
- if (a.length() != length)
- return false;
- for (int i = 0; i < length; i++) {
- if (!equalsIgnoreCase(a.charAt(i), b[offset + i]))
- return false;
- }
- return true;
- }
-
- /**
* Remove duplicates from an array of strings.
*
* This method will always keep the first occurrence of all strings at their position
* in the array, removing the subsequent ones.
*/
- public static void removeDupes(final ArrayList<CharSequence> suggestions) {
+ public static void removeDupes(final ArrayList<String> suggestions) {
if (suggestions.size() < 2) return;
int i = 1;
// Don't cache suggestions.size(), since we may be removing items
while (i < suggestions.size()) {
- final CharSequence cur = suggestions.get(i);
+ final String cur = suggestions.get(i);
// Compare each suggestion with each previous suggestion
for (int j = 0; j < i; j++) {
- CharSequence previous = suggestions.get(j);
+ final String previous = suggestions.get(j);
if (TextUtils.equals(cur, previous)) {
suggestions.remove(i);
i--;
@@ -149,7 +86,7 @@ public final class StringUtils {
}
}
- public static String toTitleCase(String s, Locale locale) {
+ public static String toTitleCase(final String s, final Locale locale) {
if (s.length() <= 1) {
// TODO: is this really correct? Shouldn't this be s.toUpperCase()?
return s;
@@ -165,21 +102,19 @@ public final class StringUtils {
return s.toUpperCase(locale).charAt(0) + s.substring(1);
}
+ private static final int[] EMPTY_CODEPOINTS = {};
+
public static int[] toCodePointArray(final String string) {
- final char[] characters = string.toCharArray();
- final int length = characters.length;
- final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ final int length = string.length();
if (length <= 0) {
- return new int[0];
+ return EMPTY_CODEPOINTS;
}
- int codePoint = Character.codePointAt(characters, 0);
- int dsti = 0;
- for (int srci = Character.charCount(codePoint);
- srci < length; srci += Character.charCount(codePoint), ++dsti) {
- codePoints[dsti] = codePoint;
- codePoint = Character.codePointAt(characters, srci);
+ final int[] codePoints = new int[string.codePointCount(0, length)];
+ int destIndex = 0;
+ for (int index = 0; index < length; index = string.offsetByCodePoints(index, 1)) {
+ codePoints[destIndex] = string.codePointAt(index);
+ destIndex++;
}
- codePoints[dsti] = codePoint;
return codePoints;
}
@@ -237,7 +172,7 @@ public final class StringUtils {
} else {
for (i = cs.length(); i > 0; i--) {
final char c = cs.charAt(i - 1);
- if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
+ if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
&& Character.getType(c) != Character.START_PUNCTUATION) {
break;
}
@@ -253,11 +188,11 @@ public final class StringUtils {
// if the first char that's not a space or tab is a start of line (as in \n, start of text,
// or some other similar characters).
int j = i;
- char prevChar = Keyboard.CODE_SPACE;
+ char prevChar = Constants.CODE_SPACE;
if (hasSpaceBefore) --j;
while (j > 0) {
prevChar = cs.charAt(j - 1);
- if (!Character.isSpaceChar(prevChar) && prevChar != Keyboard.CODE_TAB) break;
+ if (!Character.isSpaceChar(prevChar) && prevChar != Constants.CODE_TAB) break;
j--;
}
if (j <= 0 || Character.isWhitespace(prevChar)) {
@@ -296,7 +231,7 @@ public final class StringUtils {
// variants of English, the final period is placed within double quotes and maybe
// other closing punctuation signs. This is generally not true in other languages.
final char c = cs.charAt(j - 1);
- if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
+ if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
&& Character.getType(c) != Character.END_PUNCTUATION) {
break;
}
@@ -310,10 +245,10 @@ public final class StringUtils {
// end of a sentence. If we have a question mark or an exclamation mark, it's the end of
// a sentence. If it's neither, the only remaining case is the period so we get the opposite
// case out of the way.
- if (c == Keyboard.CODE_QUESTION_MARK || c == Keyboard.CODE_EXCLAMATION_MARK) {
+ if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
}
- if (c != Keyboard.CODE_PERIOD || j <= 0) {
+ if (c != Constants.CODE_PERIOD || j <= 0) {
return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
}
@@ -363,7 +298,7 @@ public final class StringUtils {
case WORD:
if (Character.isLetter(c)) {
state = WORD;
- } else if (c == Keyboard.CODE_PERIOD) {
+ } else if (c == Constants.CODE_PERIOD) {
state = PERIOD;
} else {
return caps;
@@ -379,7 +314,7 @@ public final class StringUtils {
case LETTER:
if (Character.isLetter(c)) {
state = LETTER;
- } else if (c == Keyboard.CODE_PERIOD) {
+ } else if (c == Constants.CODE_PERIOD) {
state = PERIOD;
} else {
return noCaps;
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 579f96bb4..5d8c0b17d 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -151,18 +151,19 @@ public final class SubtypeLocale {
// InputMethodSubtype's display name in its locale.
// isAdditionalSubtype (T=true, F=false)
- // locale layout | display name
- // ------ ------ - ----------------------
- // en_US qwerty F English (US) exception
- // en_GB qwerty F English (UK) exception
- // fr azerty F Français
- // fr_CA qwerty F Français (Canada)
- // de qwertz F Deutsch
- // zz qwerty F No language (QWERTY) in system locale
- // fr qwertz T Français (QWERTZ)
- // de qwerty T Deutsch (QWERTY)
- // en_US azerty T English (US) (AZERTY)
- // zz azerty T No language (AZERTY) in system locale
+ // locale layout | display name
+ // ------ ------- - ----------------------
+ // en_US qwerty F English (US) exception
+ // en_GB qwerty F English (UK) exception
+ // es_US spanish F Español (EE.UU.) exception
+ // fr azerty F Français
+ // fr_CA qwerty F Français (Canada)
+ // de qwertz F Deutsch
+ // zz qwerty F No language (QWERTY) in system locale
+ // fr qwertz T Français (QWERTZ)
+ // de qwerty T Deutsch (QWERTY)
+ // en_US azerty T English (US) (AZERTY)
+ // zz azerty T No language (AZERTY) in system locale
public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) {
final String replacementString = (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 8e51a372b..8f2e27549 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -43,7 +43,7 @@ public final class SubtypeSwitcher {
private static final String TAG = SubtypeSwitcher.class.getSimpleName();
private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
- private /* final */ InputMethodManager mImm;
+ private /* final */ RichInputMethodManager mRichImm;
private /* final */ Resources mResources;
private /* final */ ConnectivityManager mConnectivityManager;
@@ -84,7 +84,7 @@ public final class SubtypeSwitcher {
public static void init(final Context context) {
SubtypeLocale.init(context);
sInstance.initialize(context);
- sInstance.updateAllParameters(context);
+ sInstance.updateAllParameters();
}
private SubtypeSwitcher() {
@@ -93,13 +93,13 @@ public final class SubtypeSwitcher {
private void initialize(final Context service) {
mResources = service.getResources();
- mImm = ImfUtils.getInputMethodManager(service);
+ mRichImm = RichInputMethodManager.getInstance();
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
mCurrentSystemLocale = mResources.getConfiguration().locale;
- mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
- service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
- mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype);
+ mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
+ mCurrentSubtype = mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype);
if (mNoLanguageSubtype == null) {
throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
}
@@ -110,9 +110,9 @@ public final class SubtypeSwitcher {
// Update all parameters stored in SubtypeSwitcher.
// Only configuration changed event is allowed to call this because this is heavy.
- private void updateAllParameters(final Context context) {
+ private void updateAllParameters() {
mCurrentSystemLocale = mResources.getConfiguration().locale;
- updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype));
+ updateSubtype(mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype));
updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
}
@@ -137,7 +137,7 @@ public final class SubtypeSwitcher {
*/
private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) {
final List<InputMethodSubtype> enabledSubtypesOfThisIme =
- mImm.getEnabledInputMethodSubtypeList(null, true);
+ mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true);
mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) {
@@ -162,7 +162,7 @@ public final class SubtypeSwitcher {
}
// TODO: Update an icon for shortcut IME
final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
- mImm.getShortcutInputMethodsAndSubtypes();
+ mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes();
mShortcutInputMethodInfo = null;
mShortcutSubtype = null;
for (final InputMethodInfo imi : shortcuts.keySet()) {
@@ -193,8 +193,13 @@ public final class SubtypeSwitcher {
}
final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
+ final boolean sameLocale = mCurrentSystemLocale.equals(newLocale);
+ final boolean sameLanguage = mCurrentSystemLocale.getLanguage().equals(
+ newLocale.getLanguage());
+ final boolean implicitlyEnabled =
+ mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(
- mCurrentSystemLocale.equals(newLocale));
+ sameLocale || (sameLanguage && implicitlyEnabled));
if (newSubtype.equals(mCurrentSubtype)) return;
@@ -221,7 +226,7 @@ public final class SubtypeSwitcher {
if (token == null) {
return;
}
- final InputMethodManager imm = mImm;
+ final InputMethodManager imm = mRichImm.getInputMethodManager();
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
@@ -238,14 +243,8 @@ public final class SubtypeSwitcher {
if (mShortcutSubtype == null) {
return true;
}
- final boolean allowsImplicitlySelectedSubtypes = true;
- for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList(
- mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
- if (enabledSubtype.equals(mShortcutSubtype)) {
- return true;
- }
- }
- return false;
+ return mRichImm.checkIfSubtypeBelongsToImeAndEnabled(
+ mShortcutInputMethodInfo, mShortcutSubtype);
}
public boolean isShortcutImeReady() {
@@ -285,12 +284,12 @@ public final class SubtypeSwitcher {
return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
}
- public boolean onConfigurationChanged(final Configuration conf, final Context context) {
+ public boolean onConfigurationChanged(final Configuration conf) {
final Locale systemLocale = conf.locale;
final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale);
// If system configuration was changed, update all parameters.
if (systemLocaleChanged) {
- updateAllParameters(context);
+ updateAllParameters();
}
return systemLocaleChanged;
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index f0e3b4ebd..3dc2ba95b 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -38,7 +38,7 @@ public final class Suggest {
public static final String TAG = Suggest.class.getSimpleName();
// Session id for
- // {@link #getSuggestedWords(WordComposer,CharSequence,ProximityInfo,boolean,int)}.
+ // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}.
public static final int SESSION_TYPING = 0;
public static final int SESSION_GESTURE = 1;
@@ -71,7 +71,8 @@ public final class Suggest {
mLocale = locale;
}
- /* package for test */ Suggest(final Context context, final File dictionary,
+ @UsedForTesting
+ 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);
@@ -138,7 +139,7 @@ public final class Suggest {
* 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(UserBinaryDictionary userDictionary) {
+ public void setUserDictionary(final UserBinaryDictionary userDictionary) {
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
}
@@ -147,12 +148,12 @@ public final class Suggest {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
- public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
+ public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
}
- public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
+ public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) {
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
@@ -160,9 +161,9 @@ public final class Suggest {
mAutoCorrectionThreshold = threshold;
}
- public SuggestedWords getSuggestedWords(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) {
+ public SuggestedWords getSuggestedWords(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final boolean isCorrectionEnabled, final int sessionId) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
if (wordComposer.isBatchMode()) {
return getSuggestedWordsForBatchInput(
@@ -174,9 +175,9 @@ public final class Suggest {
}
// Retrieves suggestions for the typing input.
- private SuggestedWords getSuggestedWordsForTypingInput(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final boolean isCorrectionEnabled) {
final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS);
@@ -203,7 +204,7 @@ public final class Suggest {
wordComposerForLookup, prevWordForBigram, proximityInfo));
}
- final CharSequence whitelistedWord;
+ final String whitelistedWord;
if (suggestionsSet.isEmpty()) {
whitelistedWord = null;
} else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
@@ -287,9 +288,9 @@ public final class Suggest {
}
// Retrieves suggestions for the batch input.
- private SuggestedWords getSuggestedWordsForBatchInput(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, int sessionId) {
+ private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final int sessionId) {
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS);
@@ -307,7 +308,7 @@ public final class Suggest {
}
for (SuggestedWordInfo wordInfo : suggestionsSet) {
- LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict);
}
final ArrayList<SuggestedWordInfo> suggestionsContainer =
@@ -372,7 +373,7 @@ public final class Suggest {
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());
+ return o1.mWord.compareTo(o2.mWord);
}
}
private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
@@ -383,16 +384,17 @@ public final class Suggest {
final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
if (isAllUpperCase) {
- sb.append(wordInfo.mWord.toString().toUpperCase(locale));
+ sb.append(wordInfo.mWord.toUpperCase(locale));
} else if (isFirstCharCapitalized) {
- sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale));
+ sb.append(StringUtils.toTitleCase(wordInfo.mWord, locale));
} else {
sb.append(wordInfo.mWord);
}
for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
- sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
+ sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
}
- return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
+ return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
+ wordInfo.mSourceDict);
}
public void close() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 52e292a86..572f2906e 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -53,6 +53,10 @@ public final class SuggestedWords {
mIsPrediction = isPrediction;
}
+ public boolean isEmpty() {
+ return mSuggestedWordInfoList.isEmpty();
+ }
+
public int size() {
return mSuggestedWordInfoList.size();
}
@@ -86,11 +90,14 @@ public final class SuggestedWords {
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) {
final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList();
- for (CompletionInfo info : infos) {
- if (null != info && info.getText() != null) {
- result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED));
- }
+ for (final CompletionInfo info : infos) {
+ if (info == null) continue;
+ final CharSequence text = info.getText();
+ if (null == text) continue;
+ final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(),
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED,
+ Dictionary.TYPE_APPLICATION_DEFINED);
+ result.add(suggestedWordInfo);
}
return result;
}
@@ -98,7 +105,7 @@ public final class SuggestedWords {
// Should get rid of the first one (what the user typed previously) from suggestions
// and replace it with what the user currently typed.
public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
- final CharSequence typedWord, final SuggestedWords previousSuggestions) {
+ final String typedWord, final SuggestedWords previousSuggestions) {
final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
@@ -107,7 +114,7 @@ public final class SuggestedWords {
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos);
- final String prevWord = prevWordInfo.mWord.toString();
+ final String prevWord = prevWordInfo.mWord;
// Filter out duplicate suggestion.
if (!alreadySeen.contains(prevWord)) {
suggestionsList.add(prevWordInfo);
@@ -135,9 +142,9 @@ public final class SuggestedWords {
public final String mSourceDict;
private String mDebugString = "";
- public SuggestedWordInfo(final CharSequence word, final int score, final int kind,
+ public SuggestedWordInfo(final String word, final int score, final int kind,
final String sourceDict) {
- mWord = word.toString();
+ mWord = word;
mScore = score;
mKind = kind;
mSourceDict = sourceDict;
@@ -145,7 +152,7 @@ public final class SuggestedWords {
}
- public void setDebugString(String str) {
+ public void setDebugString(final String str) {
if (null == str) throw new NullPointerException("Debug info is null");
mDebugString = str;
}
@@ -167,7 +174,7 @@ public final class SuggestedWords {
if (TextUtils.isEmpty(mDebugString)) {
return mWord;
} else {
- return mWord + " (" + mDebugString.toString() + ")";
+ return mWord + " (" + mDebugString + ")";
}
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 8f21b7b4a..ec4dc1436 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -33,13 +33,13 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB
@Override
public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
syncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 612f54d73..4bdaf2039 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -36,13 +36,13 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic
@Override
public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
syncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 60e6fa127..00c3cbe0a 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -200,7 +200,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
mContext.startActivity(intent);
}
- private void addWords(Cursor cursor) {
+ private void addWords(final Cursor cursor) {
// 16 is JellyBean, but we want this to compile against ICS.
final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16;
clearFusionDictionary();
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index e39011145..100e377f6 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.util.Log;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
@@ -62,7 +63,7 @@ public final class UserHistoryDictIOUtils {
@Override
public int readUnsignedByte() {
- return ((int)mBuffer[mPosition++]) & 0xFF;
+ return mBuffer[mPosition++] & 0xFF;
}
@Override
@@ -129,7 +130,8 @@ public final class UserHistoryDictIOUtils {
/**
* Constructs a new FusionDictionary from BigramDictionaryInterface.
*/
- /* packages for test */ static FusionDictionary constructFusionDictionary(
+ @UsedForTesting
+ static FusionDictionary constructFusionDictionary(
final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
final FusionDictionary fusionDict = new FusionDictionary(new Node(),
new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
@@ -193,7 +195,8 @@ public final class UserHistoryDictIOUtils {
/**
* Adds all unigrams and bigrams in maps to OnAddWordListener.
*/
- /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
+ @UsedForTesting
+ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
final Map<Integer, Integer> frequencies,
final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 3615fa1fb..f30a60af2 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -21,6 +21,7 @@ import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.util.Log;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface;
@@ -75,7 +76,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
private final SharedPreferences mPrefs;
// Should always be false except when we use this class for test
- /* package for test */ boolean isTest = false;
+ @UsedForTesting boolean isTest = false;
private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
sLangDictCache = CollectionUtils.newConcurrentHashMap();
@@ -122,7 +123,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
@Override
protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String 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;
@@ -132,7 +133,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
* Return whether the passed charsequence is in the dictionary.
*/
@Override
- public synchronized boolean isValidWord(final CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
// TODO: figure out what is the correct thing to do here.
return false;
}
@@ -145,7 +146,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
* context, as in beginning of a sentence for example.
* The second word may not be null (a NullPointerException would be thrown).
*/
- public int addToUserHistory(final String word1, String word2, boolean isValid) {
+ public int addToUserHistory(final String word1, final String word2, final boolean isValid) {
if (word2.length() >= BinaryDictionary.MAX_WORD_LENGTH ||
(word1 != null && word1.length() >= BinaryDictionary.MAX_WORD_LENGTH)) {
return -1;
@@ -175,7 +176,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
return -1;
}
- public boolean cancelAddingUserHistory(String word1, String word2) {
+ public boolean cancelAddingUserHistory(final String word1, final String word2) {
if (mBigramListLock.tryLock()) {
try {
if (mBigramList.removeBigram(word1, word2)) {
@@ -226,7 +227,8 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
final ExpandableDictionary dictionary = this;
final OnAddWordListener listener = new OnAddWordListener() {
@Override
- public void setUnigram(String word, String shortcutTarget, int frequency) {
+ public void setUnigram(final String word, final String shortcutTarget,
+ final int frequency) {
profTotal++;
if (DBG_SAVE_RESTORE) {
Log.d(TAG, "load unigram: " + word + "," + frequency);
@@ -236,7 +238,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- public void setBigram(String word1, String word2, int frequency) {
+ public void setBigram(final String word1, final String word2, final int frequency) {
if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
&& word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
profTotal++;
@@ -250,7 +252,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
mBigramList.addBigram(word1, word2, (byte)frequency);
}
};
-
+
// Load the dictionary from binary file
FileInputStream inStream = null;
try {
@@ -292,8 +294,9 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
private final SharedPreferences mPrefs;
private final Context mContext;
- public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale,
- UserHistoryDictionary dict, SharedPreferences prefs, Context context) {
+ public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites,
+ final String locale, final UserHistoryDictionary dict,
+ final SharedPreferences prefs, final Context context) {
mBigramList = pendingWrites;
mLocale = locale;
mUserHistoryDictionary = dict;
@@ -303,7 +306,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- protected Void doInBackground(Void... v) {
+ protected Void doInBackground(final Void... v) {
if (mUserHistoryDictionary.isTest) {
// If isTest == true, wait until the lock is released.
mUserHistoryDictionary.mBigramListLock.lock();
@@ -360,7 +363,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- public int getFrequency(String word1, String word2) {
+ public int getFrequency(final String word1, final String word2) {
final int freq;
if (word1 == null) { // unigram
freq = FREQUENCY_FOR_TYPED;
@@ -373,10 +376,10 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
final byte fc = fcp.getFc();
final boolean isValid = fcp.isValid();
if (prevFc > 0 && prevFc == fc) {
- freq = ((int)fc) & 0xFF;
+ freq = fc & 0xFF;
} else if (UserHistoryForgettingCurveUtils.
needsToSave(fc, isValid, mAddLevel0Bigrams)) {
- freq = ((int)fc) & 0xFF;
+ freq = fc & 0xFF;
} else {
// Delete this entry
freq = -1;
@@ -390,6 +393,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
}
+ @UsedForTesting
void forceAddWordForTest(final String word1, final String word2, final boolean isValid) {
mBigramListLock.lock();
try {
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 876bc8e79..3eac6a237 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -41,6 +41,7 @@ import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
+import java.util.Locale;
public final class Utils {
private Utils() {
@@ -193,7 +194,7 @@ public final class Utils {
private UsabilityStudyLogUtils() {
mDate = new Date();
- mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ");
+ mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US);
HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task",
Process.THREAD_PRIORITY_BACKGROUND);
@@ -255,7 +256,7 @@ public final class Utils {
final long currentTime = System.currentTimeMillis();
mDate.setTime(currentTime);
- final String printString = String.format("%s\t%d\t%s\n",
+ final String printString = String.format(Locale.US, "%s\t%d\t%s\n",
mDateFormat.format(mDate), currentTime, log);
if (LatinImeLogger.sDBG) {
Log.d(USABILITY_TAG, "Write: " + log);
@@ -297,7 +298,7 @@ public final class Utils {
final Date date = new Date();
date.setTime(System.currentTimeMillis());
final String currentDateTimeString =
- new SimpleDateFormat("yyyyMMdd-HHmmssZ").format(date);
+ new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date);
if (mFile == null) {
Log.w(USABILITY_TAG, "No internal log file found.");
return;
@@ -313,11 +314,15 @@ public final class Utils {
+ "/research-" + currentDateTimeString + ".log";
final File destFile = new File(destPath);
try {
- final FileChannel src = (new FileInputStream(mFile)).getChannel();
- final FileChannel dest = (new FileOutputStream(destFile)).getChannel();
+ final FileInputStream srcStream = new FileInputStream(mFile);
+ final FileOutputStream destStream = new FileOutputStream(destFile);
+ final FileChannel src = srcStream.getChannel();
+ final FileChannel dest = destStream.getChannel();
src.transferTo(0, src.size(), dest);
src.close();
+ srcStream.close();
dest.close();
+ destStream.close();
} catch (FileNotFoundException e1) {
Log.w(USABILITY_TAG, e1);
return;
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index da0071adc..daff442f3 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -38,7 +38,7 @@ public final class WordComposer {
private int[] mPrimaryKeyCodes;
private final InputPointers mInputPointers = new InputPointers(N);
private final StringBuilder mTypedWord;
- private CharSequence mAutoCorrection;
+ private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@@ -64,7 +64,7 @@ public final class WordComposer {
refreshSize();
}
- public WordComposer(WordComposer source) {
+ public WordComposer(final WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
mInputPointers.copy(source.mInputPointers);
@@ -121,7 +121,8 @@ public final class WordComposer {
return mInputPointers;
}
- private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
+ private static boolean isFirstCharCapitalized(final int index, final int codePoint,
+ final boolean previous) {
if (index == 0) return Character.isUpperCase(codePoint);
return previous && !Character.isUpperCase(codePoint);
}
@@ -129,12 +130,12 @@ public final class WordComposer {
/**
* Add a new keystroke, with the pressed key's code point with the touch point coordinates.
*/
- public void add(int primaryCode, int keyX, int keyY) {
+ public void add(final int primaryCode, final int keyX, final int keyY) {
final int newIndex = size();
mTypedWord.appendCodePoint(primaryCode);
refreshSize();
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
- mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
+ mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
? Character.toLowerCase(primaryCode) : primaryCode;
// In the batch input mode, the {@code mInputPointers} holds batch input points and
// shouldn't be overridden by the "typed key" coordinates
@@ -148,7 +149,7 @@ public final class WordComposer {
newIndex, primaryCode, mIsFirstCharCapitalized);
if (Character.isUpperCase(primaryCode)) mCapsCount++;
if (Character.isDigit(primaryCode)) mDigitsCount++;
- if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) {
+ if (Constants.CODE_SINGLE_QUOTE == primaryCode) {
++mTrailingSingleQuotesCount;
} else {
mTrailingSingleQuotesCount = 0;
@@ -156,12 +157,12 @@ public final class WordComposer {
mAutoCorrection = null;
}
- public void setBatchInputPointers(InputPointers batchPointers) {
+ public void setBatchInputPointers(final InputPointers batchPointers) {
mInputPointers.set(batchPointers);
mIsBatchMode = true;
}
- public void setBatchInputWord(CharSequence word) {
+ public void setBatchInputWord(final String word) {
reset();
mIsBatchMode = true;
final int length = word.length();
@@ -235,7 +236,7 @@ public final class WordComposer {
int i = mTypedWord.length();
while (i > 0) {
i = mTypedWord.offsetByCodePoints(i, -1);
- if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break;
+ if (Constants.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break;
++mTrailingSingleQuotesCount;
}
}
@@ -321,14 +322,14 @@ public final class WordComposer {
/**
* Sets the auto-correction for this word.
*/
- public void setAutoCorrection(final CharSequence correction) {
+ public void setAutoCorrection(final String correction) {
mAutoCorrection = correction;
}
/**
* @return the auto-correction for this word, or null if none.
*/
- public CharSequence getAutoCorrectionOrNull() {
+ public String getAutoCorrectionOrNull() {
return mAutoCorrection;
}
@@ -341,7 +342,7 @@ public final class WordComposer {
// `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
public LastComposedWord commitWord(final int type, final String committedWord,
- final String separatorString, final CharSequence prevWord) {
+ final String separatorString, final String prevWord) {
// Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 7b0231a6b..ee0e9cd7e 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -16,19 +16,32 @@
package com.android.inputmethod.latin.makedict;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
public final class BinaryDictIOUtils {
private static final boolean DBG = false;
+ private static final int MSB24 = 0x800000;
+ private static final int SINT24_MAX = 0x7FFFFF;
+ private static final int MAX_JUMPS = 10000;
+
+ private BinaryDictIOUtils() {
+ // This utility class is not publicly instantiable.
+ }
private static final class Position {
public static final int NOT_READ_GROUPCOUNT = -1;
@@ -90,7 +103,9 @@ public final class BinaryDictIOUtils {
final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
formatOptions);
- if (!isMovedGroup
+ final boolean isDeletedGroup = BinaryDictInputOutput.isDeletedGroup(info.mFlags,
+ formatOptions);
+ if (!isMovedGroup && !isDeletedGroup
&& info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
frequencies.put(info.mOriginalAddress, info.mFrequency);
@@ -153,6 +168,7 @@ public final class BinaryDictIOUtils {
* @throws IOException
* @throws UnsupportedFormatException
*/
+ @UsedForTesting
public static int getTerminalPosition(final FusionDictionaryBufferInterface buffer,
final String word) throws IOException, UnsupportedFormatException {
if (word == null) return FormatSpec.NOT_VALID_WORD;
@@ -165,19 +181,19 @@ public final class BinaryDictIOUtils {
if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
do {
- int groupOffset = buffer.position() - header.mHeaderSize;
final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
- groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount);
-
boolean foundNextCharGroup = false;
for (int i = 0; i < charGroupCount; ++i) {
final int charGroupPos = buffer.position();
final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
buffer.position(), header.mFormatOptions);
- if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
- header.mFormatOptions)) {
- continue;
- }
+ final boolean isMovedGroup =
+ BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ final boolean isDeletedGroup =
+ BinaryDictInputOutput.isDeletedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ if (isMovedGroup) continue;
boolean same = true;
for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
p < currentInfo.mCharacters.length;
@@ -192,7 +208,8 @@ public final class BinaryDictIOUtils {
if (same) {
// found the group matches the word.
if (wordPos + currentInfo.mCharacters.length == wordLen) {
- if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) {
+ if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL
+ || isDeletedGroup) {
return FormatSpec.NOT_VALID_WORD;
} else {
return charGroupPos;
@@ -206,7 +223,6 @@ public final class BinaryDictIOUtils {
buffer.position(currentInfo.mChildrenAddress);
break;
}
- groupOffset = currentInfo.mEndAddress;
}
// If we found the next char group, it is under the file pointer.
@@ -228,6 +244,10 @@ public final class BinaryDictIOUtils {
return FormatSpec.NOT_VALID_WORD;
}
+ private static int markAsDeleted(final int flags) {
+ return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
+ }
+
/**
* Delete the word from the binary file.
*
@@ -236,6 +256,7 @@ public final class BinaryDictIOUtils {
* @throws IOException
* @throws UnsupportedFormatException
*/
+ @UsedForTesting
public static void deleteWord(final FusionDictionaryBufferInterface buffer,
final String word) throws IOException, UnsupportedFormatException {
buffer.position(0);
@@ -245,21 +266,58 @@ public final class BinaryDictIOUtils {
buffer.position(wordPosition);
final int flags = buffer.readUnsignedByte();
- final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL;
buffer.position(wordPosition);
- buffer.put((byte)newFlags);
+ buffer.put((byte)markAsDeleted(flags));
}
- private static void putSInt24(final FusionDictionaryBufferInterface buffer,
+ /**
+ * @return the size written, in bytes. Always 3 bytes.
+ */
+ private static int writeSInt24ToBuffer(final FusionDictionaryBufferInterface buffer,
final int value) {
final int absValue = Math.abs(value);
buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
buffer.put((byte)((absValue >> 8) & 0xFF));
buffer.put((byte)(absValue & 0xFF));
+ return 3;
+ }
+
+ /**
+ * @return the size written, in bytes. Always 3 bytes.
+ */
+ private static int writeSInt24ToStream(final OutputStream destination, final int value)
+ throws IOException {
+ final int absValue = Math.abs(value);
+ destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+ destination.write((byte)((absValue >> 8) & 0xFF));
+ destination.write((byte)(absValue & 0xFF));
+ return 3;
}
/**
- * Update a parent address in a CharGroup that is addressed by groupOriginAddress.
+ * @return the size written, in bytes. 1, 2, or 3 bytes.
+ */
+ private static int writeVariableAddress(final OutputStream destination, final int value)
+ throws IOException {
+ switch (BinaryDictInputOutput.getByteSize(value)) {
+ case 1:
+ destination.write((byte)value);
+ break;
+ case 2:
+ destination.write((byte)(0xFF & (value >> 8)));
+ destination.write((byte)(0xFF & value));
+ break;
+ case 3:
+ destination.write((byte)(0xFF & (value >> 16)));
+ destination.write((byte)(0xFF & (value >> 8)));
+ destination.write((byte)(0xFF & value));
+ break;
+ }
+ return BinaryDictInputOutput.getByteSize(value);
+ }
+
+ /**
+ * Update a parent address in a CharGroup that is referred to by groupOriginAddress.
*
* @param buffer the buffer to write.
* @param groupOriginAddress the address of the group.
@@ -275,8 +333,648 @@ public final class BinaryDictIOUtils {
throw new RuntimeException("this file format does not support parent addresses");
}
final int flags = buffer.readUnsignedByte();
+ if (BinaryDictInputOutput.isMovedGroup(flags, formatOptions)) {
+ // if the group is moved, the parent address is stored in the destination group.
+ // We are guaranteed to process the destination group later, so there is no need to
+ // update anything here.
+ buffer.position(originalPosition);
+ return;
+ }
+ if (DBG) {
+ MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress);
+ }
final int parentOffset = newParentAddress - groupOriginAddress;
- putSInt24(buffer, parentOffset);
+ writeSInt24ToBuffer(buffer, parentOffset);
+ buffer.position(originalPosition);
+ }
+
+ private static void skipCharGroup(final FusionDictionaryBufferInterface buffer,
+ final FormatOptions formatOptions) {
+ final int flags = buffer.readUnsignedByte();
+ BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+ skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ BinaryDictInputOutput.readChildrenAddress(buffer, flags, formatOptions);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+ if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
+ final int shortcutsSize = buffer.readUnsignedShort();
+ buffer.position(buffer.position() + shortcutsSize
+ - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE);
+ }
+ if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ final int bigramFlags = buffer.readUnsignedByte();
+ switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+ buffer.readUnsignedByte();
+ break;
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+ buffer.readUnsignedShort();
+ break;
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+ buffer.readUnsignedInt24();
+ break;
+ }
+ if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break;
+ }
+ if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ throw new RuntimeException("Too many bigrams in a group.");
+ }
+ }
+ }
+
+ /**
+ * Update parent addresses in a Node that is referred to by nodeOriginAddress.
+ *
+ * @param buffer the buffer to be modified.
+ * @param nodeOriginAddress the address of a modified Node.
+ * @param newParentAddress the address to be written.
+ * @param formatOptions file format options.
+ */
+ public static void updateParentAddresses(final FusionDictionaryBufferInterface buffer,
+ final int nodeOriginAddress, final int newParentAddress,
+ final FormatOptions formatOptions) {
+ final int originalPosition = buffer.position();
+ buffer.position(nodeOriginAddress);
+ do {
+ final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+ for (int i = 0; i < count; ++i) {
+ updateParentAddress(buffer, buffer.position(), newParentAddress, formatOptions);
+ skipCharGroup(buffer, formatOptions);
+ }
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ buffer.position(forwardLinkAddress);
+ } while (formatOptions.mSupportsDynamicUpdate
+ && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
+ buffer.position(originalPosition);
+ }
+
+ private static void skipString(final FusionDictionaryBufferInterface buffer,
+ final boolean hasMultipleChars) {
+ if (hasMultipleChars) {
+ int character = CharEncoding.readChar(buffer);
+ while (character != FormatSpec.INVALID_CHARACTER) {
+ character = CharEncoding.readChar(buffer);
+ }
+ } else {
+ CharEncoding.readChar(buffer);
+ }
+ }
+
+ /**
+ * Write a string to a stream.
+ *
+ * @param destination the stream to write.
+ * @param word the string to be written.
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int writeString(final OutputStream destination, final String word)
+ throws IOException {
+ int size = 0;
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (CharEncoding.getCharSize(codePoint) == 1) {
+ destination.write((byte)codePoint);
+ size++;
+ } else {
+ destination.write((byte)(0xFF & (codePoint >> 16)));
+ destination.write((byte)(0xFF & (codePoint >> 8)));
+ destination.write((byte)(0xFF & codePoint));
+ size += 3;
+ }
+ }
+ destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+ size += FormatSpec.GROUP_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
+ * Update a children address in a CharGroup that is addressed by groupOriginAddress.
+ *
+ * @param buffer the buffer to write.
+ * @param groupOriginAddress the address of the group.
+ * @param newChildrenAddress the absolute address of the child.
+ * @param formatOptions file format options.
+ */
+ public static void updateChildrenAddress(final FusionDictionaryBufferInterface buffer,
+ final int groupOriginAddress, final int newChildrenAddress,
+ final FormatOptions formatOptions) {
+ final int originalPosition = buffer.position();
+ buffer.position(groupOriginAddress);
+ final int flags = buffer.readUnsignedByte();
+ final int parentAddress = BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+ skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+ final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
+ ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - buffer.position();
+ writeSInt24ToBuffer(buffer, childrenOffset);
buffer.position(originalPosition);
}
+
+ /**
+ * Write a char group to an output stream.
+ * A char group is an in-memory representation of a node in trie.
+ * A char group info is an on-disk representation of a node.
+ *
+ * @param destination the stream to write.
+ * @param info the char group info to be written.
+ * @return the size written, in bytes.
+ */
+ public static int writeCharGroup(final OutputStream destination, final CharGroupInfo info)
+ throws IOException {
+ int size = FormatSpec.GROUP_FLAGS_SIZE;
+ destination.write((byte)info.mFlags);
+ final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ?
+ FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress;
+ size += writeSInt24ToStream(destination, parentOffset);
+
+ for (int i = 0; i < info.mCharacters.length; ++i) {
+ if (CharEncoding.getCharSize(info.mCharacters[i]) == 1) {
+ destination.write((byte)info.mCharacters[i]);
+ size++;
+ } else {
+ size += writeSInt24ToStream(destination, info.mCharacters[i]);
+ }
+ }
+ if (info.mCharacters.length > 1) {
+ destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+ size++;
+ }
+
+ if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
+ destination.write((byte)info.mFrequency);
+ size++;
+ }
+
+ if (DBG) {
+ MakedictLog.d("writeCharGroup origin=" + info.mOriginalAddress + ", size=" + size
+ + ", child=" + info.mChildrenAddress + ", characters ="
+ + new String(info.mCharacters, 0, info.mCharacters.length));
+ }
+ final int childrenOffset = info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS ?
+ 0 : info.mChildrenAddress - (info.mOriginalAddress + size);
+ writeSInt24ToStream(destination, childrenOffset);
+ size += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+
+ if (info.mShortcutTargets != null && info.mShortcutTargets.size() > 0) {
+ final int shortcutListSize =
+ BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+ destination.write((byte)(shortcutListSize >> 8));
+ destination.write((byte)(shortcutListSize & 0xFF));
+ size += 2;
+ final Iterator<WeightedString> shortcutIterator = info.mShortcutTargets.iterator();
+ while (shortcutIterator.hasNext()) {
+ final WeightedString target = shortcutIterator.next();
+ destination.write((byte)BinaryDictInputOutput.makeShortcutFlags(
+ shortcutIterator.hasNext(), target.mFrequency));
+ size++;
+ size += writeString(destination, target.mWord);
+ }
+ }
+
+ if (info.mBigrams != null) {
+ // TODO: Consolidate this code with the code that computes the size of the bigram list
+ // in BinaryDictionaryInputOutput#computeActualNodeSize
+ for (int i = 0; i < info.mBigrams.size(); ++i) {
+
+ final int bigramFrequency = info.mBigrams.get(i).mFrequency;
+ int bigramFlags = (i < info.mBigrams.size() - 1)
+ ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0;
+ size++;
+ final int bigramOffset = info.mBigrams.get(i).mAddress - (info.mOriginalAddress
+ + size);
+ bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0;
+ switch (BinaryDictInputOutput.getByteSize(bigramOffset)) {
+ case 1:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+ break;
+ }
+ bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+ destination.write((byte)bigramFlags);
+ size += writeVariableAddress(destination, Math.abs(bigramOffset));
+ }
+ }
+ return size;
+ }
+
+ @SuppressWarnings("unused")
+ private static void updateForwardLink(final FusionDictionaryBufferInterface buffer,
+ final int nodeOriginAddress, final int newNodeAddress,
+ final FormatOptions formatOptions) {
+ buffer.position(nodeOriginAddress);
+ int jumpCount = 0;
+ while (jumpCount++ < MAX_JUMPS) {
+ final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+ for (int i = 0; i < count; ++i) skipCharGroup(buffer, formatOptions);
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ buffer.position(buffer.position() - FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+ writeSInt24ToBuffer(buffer, newNodeAddress);
+ return;
+ }
+ buffer.position(forwardLinkAddress);
+ }
+ if (DBG && jumpCount >= MAX_JUMPS) {
+ throw new RuntimeException("too many jumps, probably a bug.");
+ }
+ }
+
+ /**
+ * Helper method to move a char group to the tail of the file.
+ */
+ private static int moveCharGroup(final OutputStream destination,
+ final FusionDictionaryBufferInterface buffer, final CharGroupInfo info,
+ final int nodeOriginAddress, final int oldGroupAddress,
+ final FormatOptions formatOptions) throws IOException {
+ updateParentAddress(buffer, oldGroupAddress, buffer.limit() + 1, formatOptions);
+ buffer.position(oldGroupAddress);
+ final int currentFlags = buffer.readUnsignedByte();
+ buffer.position(oldGroupAddress);
+ buffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
+ & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
+ int size = FormatSpec.GROUP_FLAGS_SIZE;
+ updateForwardLink(buffer, nodeOriginAddress, buffer.limit(), formatOptions);
+ size += writeNode(destination, new CharGroupInfo[] { info });
+ return size;
+ }
+
+ /**
+ * Compute the size of the char group.
+ */
+ private static int computeGroupSize(final CharGroupInfo info,
+ final FormatOptions formatOptions) {
+ int size = FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+ + BinaryDictInputOutput.getGroupCharactersSize(info.mCharacters)
+ + BinaryDictInputOutput.getChildrenAddressSize(info.mFlags, formatOptions);
+ if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
+ size += FormatSpec.GROUP_FREQUENCY_SIZE;
+ }
+ if (info.mShortcutTargets != null && !info.mShortcutTargets.isEmpty()) {
+ size += BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+ }
+ if (info.mBigrams != null) {
+ for (final PendingAttribute attr : info.mBigrams) {
+ size += FormatSpec.GROUP_FLAGS_SIZE;
+ size += BinaryDictInputOutput.getByteSize(attr.mAddress);
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Write a node to the stream.
+ *
+ * @param destination the stream to write.
+ * @param infos groups to be written.
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int writeNode(final OutputStream destination, final CharGroupInfo[] infos)
+ throws IOException {
+ int size = BinaryDictInputOutput.getGroupCountSize(infos.length);
+ switch (BinaryDictInputOutput.getGroupCountSize(infos.length)) {
+ case 1:
+ destination.write((byte)infos.length);
+ break;
+ case 2:
+ destination.write((byte)(infos.length >> 8));
+ destination.write((byte)(infos.length & 0xFF));
+ break;
+ default:
+ throw new RuntimeException("Invalid group count size.");
+ }
+ for (final CharGroupInfo info : infos) size += writeCharGroup(destination, info);
+ writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS);
+ return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+ }
+
+ /**
+ * Move a group that is referred to by oldGroupOrigin to the tail of the file.
+ * And set the children address to the byte after the group.
+ *
+ * @param nodeOrigin the address of the tail of the file.
+ * @param characters
+ * @param length
+ * @param flags
+ * @param frequency
+ * @param parentAddress
+ * @param shortcutTargets
+ * @param bigrams
+ * @param destination the stream representing the tail of the file.
+ * @param buffer the buffer representing the (constant-size) body of the file.
+ * @param oldNodeOrigin
+ * @param oldGroupOrigin
+ * @param formatOptions
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int moveGroup(final int nodeOrigin, final int[] characters, final int length,
+ final int flags, final int frequency, final int parentAddress,
+ final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
+ final FusionDictionaryBufferInterface buffer, final int oldNodeOrigin,
+ final int oldGroupOrigin, final FormatOptions formatOptions) throws IOException {
+ int size = 0;
+ final int newGroupOrigin = nodeOrigin + 1;
+ final int[] writtenCharacters = Arrays.copyOfRange(characters, 0, length);
+ final CharGroupInfo tmpInfo = new CharGroupInfo(newGroupOrigin, -1 /* endAddress */,
+ flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS,
+ shortcutTargets, bigrams);
+ size = computeGroupSize(tmpInfo, formatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newGroupOrigin, newGroupOrigin + size,
+ flags, writtenCharacters, frequency, parentAddress,
+ nodeOrigin + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
+ bigrams);
+ moveCharGroup(destination, buffer, newInfo, oldNodeOrigin, oldGroupOrigin, formatOptions);
+ return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+ }
+
+ /**
+ * Insert a word into a binary dictionary.
+ *
+ * @param buffer
+ * @param destination
+ * @param word
+ * @param frequency
+ * @param bigramStrings
+ * @param shortcuts
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ // TODO: Support batch insertion.
+ // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
+ @UsedForTesting
+ public static void insertWord(final FusionDictionaryBufferInterface buffer,
+ final OutputStream destination, final String word, final int frequency,
+ final ArrayList<WeightedString> bigramStrings,
+ final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
+ final boolean isBlackListEntry)
+ throws IOException, UnsupportedFormatException {
+ final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>();
+ if (bigramStrings != null) {
+ for (final WeightedString bigram : bigramStrings) {
+ int position = getTerminalPosition(buffer, bigram.mWord);
+ if (position == FormatSpec.NOT_VALID_WORD) {
+ // TODO: figure out what is the correct thing to do here.
+ } else {
+ bigrams.add(new PendingAttribute(bigram.mFrequency, position));
+ }
+ }
+ }
+
+ final boolean isTerminal = true;
+ final boolean hasBigrams = !bigrams.isEmpty();
+ final boolean hasShortcuts = shortcuts != null && !shortcuts.isEmpty();
+
+ // find the insert position of the word.
+ if (buffer.position() != 0) buffer.position(0);
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+
+ int wordPos = 0, address = buffer.position(), nodeOriginAddress = buffer.position();
+ final int[] codePoints = FusionDictionary.getCodePoints(word);
+ final int wordLen = codePoints.length;
+
+ for (int depth = 0; depth < Constants.Dictionary.MAX_WORD_LENGTH; ++depth) {
+ if (wordPos >= wordLen) break;
+ nodeOriginAddress = buffer.position();
+ int nodeParentAddress = -1;
+ final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
+ boolean foundNextGroup = false;
+
+ for (int i = 0; i < charGroupCount; ++i) {
+ address = buffer.position();
+ final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
+ buffer.position(), header.mFormatOptions);
+ final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ if (isMovedGroup) continue;
+ nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS)
+ ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address;
+ boolean matched = true;
+ for (int p = 0; p < currentInfo.mCharacters.length; ++p) {
+ if (wordPos + p >= wordLen) {
+ /*
+ * splitting
+ * before
+ * abcd - ef
+ *
+ * insert "abc"
+ *
+ * after
+ * abc - d - ef
+ */
+ final int newNodeAddress = buffer.limit();
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
+ isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */,
+ false /* isBlackListEntry */, header.mFormatOptions);
+ int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, flags,
+ frequency, nodeParentAddress, shortcuts, bigrams, destination,
+ buffer, nodeOriginAddress, address, header.mFormatOptions);
+
+ final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p,
+ currentInfo.mCharacters.length);
+ if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ updateParentAddresses(buffer, currentInfo.mChildrenAddress,
+ newNodeAddress + written + 1, header.mFormatOptions);
+ }
+ final CharGroupInfo newInfo2 = new CharGroupInfo(
+ newNodeAddress + written + 1, -1 /* endAddress */,
+ currentInfo.mFlags, characters2, currentInfo.mFrequency,
+ newNodeAddress + 1, currentInfo.mChildrenAddress,
+ currentInfo.mShortcutTargets, currentInfo.mBigrams);
+ writeNode(destination, new CharGroupInfo[] { newInfo2 });
+ return;
+ } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) {
+ if (p > 0) {
+ /*
+ * splitting
+ * before
+ * ab - cd
+ *
+ * insert "ac"
+ *
+ * after
+ * a - b - cd
+ * |
+ * - c
+ */
+
+ final int newNodeAddress = buffer.limit();
+ final int childrenAddress = currentInfo.mChildrenAddress;
+
+ // move prefix
+ final int prefixFlags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
+ false /* isTerminal */, 0 /* childrenAddressSize*/,
+ false /* hasShortcut */, false /* hasBigrams */,
+ false /* isNotAWord */, false /* isBlackListEntry */,
+ header.mFormatOptions);
+ int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p,
+ prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
+ destination, buffer, nodeOriginAddress, address,
+ header.mFormatOptions);
+
+ final int[] suffixCharacters = Arrays.copyOfRange(
+ currentInfo.mCharacters, p, currentInfo.mCharacters.length);
+ if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ updateParentAddresses(buffer, currentInfo.mChildrenAddress,
+ newNodeAddress + written + 1, header.mFormatOptions);
+ }
+ final int suffixFlags = BinaryDictInputOutput.makeCharGroupFlags(
+ suffixCharacters.length > 1,
+ (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0,
+ 0 /* childrenAddressSize */,
+ (currentInfo.mFlags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)
+ != 0,
+ (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo suffixInfo = new CharGroupInfo(
+ newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags,
+ suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1,
+ currentInfo.mChildrenAddress, currentInfo.mShortcutTargets,
+ currentInfo.mBigrams);
+ written += computeGroupSize(suffixInfo, header.mFormatOptions) + 1;
+
+ final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p,
+ codePoints.length);
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(
+ newCharacters.length > 1, isTerminal,
+ 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(
+ newNodeAddress + written, -1 /* endAddress */, flags,
+ newCharacters, frequency, newNodeAddress + 1,
+ FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[] { suffixInfo, newInfo });
+ return;
+ }
+ matched = false;
+ break;
+ }
+ }
+
+ if (matched) {
+ if (wordPos + currentInfo.mCharacters.length == wordLen) {
+ // the word exists in the dictionary.
+ // only update group.
+ final int newNodeAddress = buffer.limit();
+ final boolean hasMultipleChars = currentInfo.mCharacters.length > 1;
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+ -1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
+ nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
+ bigrams);
+ moveCharGroup(destination, buffer, newInfo, nodeOriginAddress, address,
+ header.mFormatOptions);
+ return;
+ }
+ wordPos += currentInfo.mCharacters.length;
+ if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
+ /*
+ * found the prefix of the word.
+ * make new node and link to the node from this group.
+ *
+ * before
+ * ab - cd
+ *
+ * insert "abcde"
+ *
+ * after
+ * ab - cd - e
+ */
+ final int newNodeAddress = buffer.limit();
+ updateChildrenAddress(buffer, address, newNodeAddress,
+ header.mFormatOptions);
+ final int newGroupAddress = newNodeAddress + 1;
+ final boolean hasMultipleChars = (wordLen - wordPos) > 1;
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+ final CharGroupInfo newInfo = new CharGroupInfo(newGroupAddress, -1, flags,
+ characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS,
+ shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[] { newInfo });
+ return;
+ }
+ buffer.position(currentInfo.mChildrenAddress);
+ foundNextGroup = true;
+ break;
+ }
+ }
+
+ if (foundNextGroup) continue;
+
+ // reached the end of the array.
+ final int linkAddressPosition = buffer.position();
+ int nextLink = buffer.readUnsignedInt24();
+ if ((nextLink & MSB24) != 0) {
+ nextLink = -(nextLink & SINT24_MAX);
+ }
+ if (nextLink == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ /*
+ * expand this node.
+ *
+ * before
+ * ab - cd
+ *
+ * insert "abef"
+ *
+ * after
+ * ab - cd
+ * |
+ * - ef
+ */
+
+ // change the forward link address.
+ final int newNodeAddress = buffer.limit();
+ buffer.position(linkAddressPosition);
+ writeSInt24ToBuffer(buffer, newNodeAddress);
+
+ final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(characters.length > 1,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+ -1 /* endAddress */, flags, characters, frequency, nodeParentAddress,
+ FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[]{ newInfo });
+ return;
+ } else {
+ depth--;
+ buffer.position(nextLink);
+ }
+ }
+ }
+
+ /**
+ * Find a word from the buffer.
+ *
+ * @param buffer the buffer representing the body of the dictionary file.
+ * @param word the word searched
+ * @return the found group
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ @UsedForTesting
+ public static CharGroupInfo findWordFromBuffer(final FusionDictionaryBufferInterface buffer,
+ final String word) throws IOException, UnsupportedFormatException {
+ int position = getTerminalPosition(buffer, word);
+ if (position != FormatSpec.NOT_VALID_WORD) {
+ buffer.position(0);
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+ buffer.position(position);
+ return BinaryDictInputOutput.readCharGroup(buffer, position, header.mFormatOptions);
+ }
+ return null;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index b431a4da9..fb1eb2701 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.makedict;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
@@ -76,12 +77,12 @@ public final class BinaryDictInputOutput {
@Override
public int readUnsignedByte() {
- return ((int)mBuffer.get()) & 0xFF;
+ return mBuffer.get() & 0xFF;
}
@Override
public int readUnsignedShort() {
- return ((int)mBuffer.getShort()) & 0xFFFF;
+ return mBuffer.getShort() & 0xFFFF;
}
@Override
@@ -124,8 +125,7 @@ public final class BinaryDictInputOutput {
/**
* A class grouping utility function for our specific character encoding.
*/
- private static final class CharEncoding {
-
+ static final class CharEncoding {
private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
@@ -154,7 +154,7 @@ public final class BinaryDictInputOutput {
* @param character the character code.
* @return the size in binary encoded-form, either 1 or 3 bytes.
*/
- private static int getCharSize(final int character) {
+ static int getCharSize(final int character) {
// See char encoding in FusionDictionary.java
if (fitsOnOneByte(character)) return 1;
if (FormatSpec.INVALID_CHARACTER == character) return 1;
@@ -263,7 +263,7 @@ public final class BinaryDictInputOutput {
* @param buffer the buffer, positioned over an encoded character.
* @return the character code.
*/
- private static int readChar(final FusionDictionaryBufferInterface buffer) {
+ static int readChar(final FusionDictionaryBufferInterface buffer) {
int character = buffer.readUnsignedByte();
if (!fitsOnOneByte(character)) {
if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
@@ -277,6 +277,21 @@ public final class BinaryDictInputOutput {
}
/**
+ * Compute the binary size of the character array.
+ *
+ * If only one character, this is the size of this character. If many, it's the sum of their
+ * sizes + 1 byte for the terminator.
+ *
+ * @param characters the character array
+ * @return the size of the char array, including the terminator if any
+ */
+ static int getGroupCharactersSize(final int[] characters) {
+ int size = CharEncoding.getCharArraySize(characters);
+ if (characters.length > 1) size += FormatSpec.GROUP_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
* Compute the binary size of the character array in a group
*
* If only one character, this is the size of this character. If many, it's the sum of their
@@ -286,9 +301,7 @@ public final class BinaryDictInputOutput {
* @return the size of the char array, including the terminator if any
*/
private static int getGroupCharactersSize(final CharGroup group) {
- int size = CharEncoding.getCharArraySize(group.mChars);
- if (group.hasSeveralChars()) size += FormatSpec.GROUP_TERMINATOR_SIZE;
- return size;
+ return getGroupCharactersSize(group.mChars);
}
/**
@@ -338,7 +351,7 @@ public final class BinaryDictInputOutput {
* This is known in advance and does not change according to position in the file
* like address lists do.
*/
- private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+ static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
if (null == shortcutList) return 0;
int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
for (final WeightedString shortcut : shortcutList) {
@@ -399,7 +412,16 @@ public final class BinaryDictInputOutput {
* Helper method to check whether the group is moved.
*/
public static boolean isMovedGroup(final int flags, final FormatOptions options) {
- return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1);
+ return options.mSupportsDynamicUpdate
+ && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
+ }
+
+ /**
+ * Helper method to check whether the group is deleted.
+ */
+ public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) {
+ return formatOptions.mSupportsDynamicUpdate
+ && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
}
/**
@@ -439,7 +461,7 @@ public final class BinaryDictInputOutput {
* @param address the address
* @return the byte size.
*/
- private static int getByteSize(final int address) {
+ static int getByteSize(final int address) {
assert(address <= UINT24_MAX);
if (!hasChildrenAddress(address)) {
return 0;
@@ -452,11 +474,8 @@ public final class BinaryDictInputOutput {
}
}
- private static final int SINT8_MAX = 0x7F;
- private static final int SINT16_MAX = 0x7FFF;
private static final int SINT24_MAX = 0x7FFFFF;
private static final int MSB8 = 0x80;
- private static final int MSB16 = 0x8000;
private static final int MSB24 = 0x800000;
// End utility methods.
@@ -721,53 +740,60 @@ public final class BinaryDictInputOutput {
return 3;
}
- private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
- final int childrenOffset, final FormatOptions formatOptions) {
+ /**
+ * Makes the flag value for a char group.
+ *
+ * @param hasMultipleChars whether the group has multiple chars.
+ * @param isTerminal whether the group is terminal.
+ * @param childrenAddressSize the size of a children address.
+ * @param hasShortcuts whether the group has shortcuts.
+ * @param hasBigrams whether the group has bigrams.
+ * @param isNotAWord whether the group is not a word.
+ * @param isBlackListEntry whether the group is a blacklist entry.
+ * @param formatOptions file format options.
+ * @return the flags
+ */
+ static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal,
+ final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
+ final boolean isNotAWord, final boolean isBlackListEntry,
+ final FormatOptions formatOptions) {
byte flags = 0;
- if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
- if (group.mFrequency >= 0) {
- flags |= FormatSpec.FLAG_IS_TERMINAL;
- }
- if (null != group.mChildren) {
- final int byteSize = formatOptions.mSupportsDynamicUpdate
- ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset);
- switch (byteSize) {
- case 1:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
- break;
- case 2:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
- break;
- case 3:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
- break;
- default:
- throw new RuntimeException("Node with a strange address");
- }
- } else if (formatOptions.mSupportsDynamicUpdate) {
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
- }
- if (null != group.mShortcutTargets) {
- if (DBG && 0 == group.mShortcutTargets.size()) {
- throw new RuntimeException("0-sized shortcut list must be null");
- }
- flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
- }
- if (null != group.mBigrams) {
- if (DBG && 0 == group.mBigrams.size()) {
- throw new RuntimeException("0-sized bigram list must be null");
+ if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
+ if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
+ if (formatOptions.mSupportsDynamicUpdate) {
+ flags |= FormatSpec.FLAG_IS_NOT_MOVED;
+ } else if (true) {
+ switch (childrenAddressSize) {
+ case 1:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+ break;
+ case 0:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
+ break;
+ default:
+ throw new RuntimeException("Node with a strange address");
}
- flags |= FormatSpec.FLAG_HAS_BIGRAMS;
- }
- if (group.mIsNotAWord) {
- flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
- }
- if (group.mIsBlacklistEntry) {
- flags |= FormatSpec.FLAG_IS_BLACKLISTED;
}
+ if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
+ if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+ if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+ if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
return flags;
}
+ private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
+ final int childrenOffset, final FormatOptions formatOptions) {
+ return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0,
+ getByteSize(childrenOffset), group.mShortcutTargets != null, group.mBigrams != null,
+ group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions);
+ }
+
/**
* Makes the flag value for a bigram.
*
@@ -859,7 +885,7 @@ public final class BinaryDictInputOutput {
* @param frequency the frequency of the attribute, 0..15
* @return the flags
*/
- private static final int makeShortcutFlags(final boolean more, final int frequency) {
+ static final int makeShortcutFlags(final boolean more, final int frequency) {
return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
+ (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
}
@@ -895,8 +921,10 @@ public final class BinaryDictInputOutput {
* @param formatOptions file format options.
* @return the address of the END of the node.
*/
+ @SuppressWarnings("unused")
private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
final Node node, final FormatOptions formatOptions) {
+ // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
int index = node.mCachedAddress;
final int groupCount = node.mData.size();
@@ -1178,7 +1206,7 @@ public final class BinaryDictInputOutput {
// Input methods: Read a binary dictionary to memory.
// readDictionaryBinary is the public entry point for them.
- private static int getChildrenAddressSize(final int optionFlags,
+ static int getChildrenAddressSize(final int optionFlags,
final FormatOptions formatOptions) {
if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
@@ -1194,7 +1222,7 @@ public final class BinaryDictInputOutput {
}
}
- private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
+ static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
final int optionFlags, final FormatOptions options) {
if (options.mSupportsDynamicUpdate) {
final int address = buffer.readUnsignedInt24();
@@ -1219,7 +1247,7 @@ public final class BinaryDictInputOutput {
}
}
- private static int readParentAddress(final FusionDictionaryBufferInterface buffer,
+ static int readParentAddress(final FusionDictionaryBufferInterface buffer,
final FormatOptions formatOptions) {
if (supportsDynamicUpdate(formatOptions)) {
final int parentAddress = buffer.readUnsignedInt24();
@@ -1290,7 +1318,8 @@ public final class BinaryDictInputOutput {
ArrayList<PendingAttribute> bigrams = null;
if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
bigrams = new ArrayList<PendingAttribute>();
- while (true) {
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
final int bigramFlags = buffer.readUnsignedByte();
++addressPointer;
final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
@@ -1318,6 +1347,9 @@ public final class BinaryDictInputOutput {
bigramAddress));
if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
}
+ if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ MakedictLog.d("too many bigrams in a group.");
+ }
}
return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency,
parentAddress, childrenAddress, shortcutTargets, bigrams);
@@ -1340,7 +1372,8 @@ public final class BinaryDictInputOutput {
// of this method. Since it performs direct, unbuffered random access to the file and
// may be called hundreds of thousands of times, the resulting performance is not
// reasonable without some kind of cache. Thus:
- private static TreeMap<Integer, String> wordCache = new TreeMap<Integer, String>();
+ private static TreeMap<Integer, WeightedString> wordCache =
+ new TreeMap<Integer, WeightedString>();
/**
* Finds, as a string, the word at the address passed as an argument.
*
@@ -1348,15 +1381,15 @@ public final class BinaryDictInputOutput {
* @param headerSize the size of the header.
* @param address the address to seek.
* @param formatOptions file format options.
- * @return the word, as a string.
+ * @return the word with its frequency, as a weighted string.
*/
- /* packages for tests */ static String getWordAtAddress(
+ /* package for tests */ static WeightedString getWordAtAddress(
final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
final FormatOptions formatOptions) {
- final String cachedString = wordCache.get(address);
+ final WeightedString cachedString = wordCache.get(address);
if (null != cachedString) return cachedString;
- final String result;
+ final WeightedString result;
final int originalPointer = buffer.position();
buffer.position(address);
@@ -1372,14 +1405,18 @@ public final class BinaryDictInputOutput {
return result;
}
+ // TODO: static!? This will behave erratically when used in multi-threaded code.
+ // We need to fix this
private static int[] sGetWordBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
- private static String getWordAtAddressWithParentAddress(
+ @SuppressWarnings("unused")
+ private static WeightedString getWordAtAddressWithParentAddress(
final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
final FormatOptions options) {
final StringBuilder builder = new StringBuilder();
int currentAddress = address;
int index = FormatSpec.MAX_WORD_LENGTH - 1;
+ int frequency = Integer.MIN_VALUE;
// the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
CharGroupInfo currentInfo;
@@ -1394,6 +1431,7 @@ public final class BinaryDictInputOutput {
MakedictLog.d("Too many jumps - probably a bug");
}
} while (isMovedGroup(currentInfo.mFlags, options));
+ if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency;
for (int i = 0; i < currentInfo.mCharacters.length; ++i) {
sGetWordBuffer[index--] =
currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1];
@@ -1402,17 +1440,19 @@ public final class BinaryDictInputOutput {
currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
}
- return new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1);
+ return new WeightedString(
+ new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1),
+ frequency);
}
- private static String getWordAtAddressWithoutParentAddress(
+ private static WeightedString getWordAtAddressWithoutParentAddress(
final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
final FormatOptions options) {
buffer.position(headerSize);
final int count = readCharGroupCount(buffer);
int groupOffset = getGroupCountSize(count);
final StringBuilder builder = new StringBuilder();
- String result = null;
+ WeightedString result = null;
CharGroupInfo last = null;
for (int i = count - 1; i >= 0; --i) {
@@ -1420,7 +1460,7 @@ public final class BinaryDictInputOutput {
groupOffset = info.mEndAddress;
if (info.mOriginalAddress == address) {
builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
- result = builder.toString();
+ result = new WeightedString(builder.toString(), info.mFrequency);
break; // and return
}
if (hasChildrenAddress(info.mChildrenAddress)) {
@@ -1481,9 +1521,11 @@ public final class BinaryDictInputOutput {
if (null != info.mBigrams) {
bigrams = new ArrayList<WeightedString>();
for (PendingAttribute bigram : info.mBigrams) {
- final String word = getWordAtAddress(
+ final WeightedString word = getWordAtAddress(
buffer, headerSize, bigram.mAddress, options);
- bigrams.add(new WeightedString(word, bigram.mFrequency));
+ final int reconstructedFrequency =
+ reconstructBigramFrequency(word.mFrequency, bigram.mFrequency);
+ bigrams.add(new WeightedString(word.mWord, reconstructedFrequency));
}
}
if (hasChildrenAddress(info.mChildrenAddress)) {
@@ -1618,6 +1660,7 @@ public final class BinaryDictInputOutput {
* @param dict an optional dictionary to add words to, or null.
* @return the created (or merged) dictionary.
*/
+ @UsedForTesting
public static FusionDictionary readDictionaryBinary(
final FusionDictionaryBufferInterface buffer, final FusionDictionary dict)
throws IOException, UnsupportedFormatException {
@@ -1655,17 +1698,24 @@ public final class BinaryDictInputOutput {
}
/**
+ * Helper method to pass a file name instead of a File object to isBinaryDictionary.
+ */
+ public static boolean isBinaryDictionary(final String filename) {
+ final File file = new File(filename);
+ return isBinaryDictionary(file);
+ }
+
+ /**
* Basic test to find out whether the file is a binary dictionary or not.
*
* Concretely this only tests the magic number.
*
- * @param filename The name of the file to test.
+ * @param file The file to test.
* @return true if it's a binary dictionary, false otherwise
*/
- public static boolean isBinaryDictionary(final String filename) {
+ public static boolean isBinaryDictionary(final File file) {
FileInputStream inStream = null;
try {
- final File file = new File(filename);
inStream = new FileInputStream(file);
final ByteBuffer buffer = inStream.getChannel().map(
FileChannel.MapMode.READ_ONLY, 0, file.length());
@@ -1700,8 +1750,7 @@ public final class BinaryDictInputOutput {
final int bigramFrequency) {
final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
/ (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
- final float resultFreqFloat = (float)unigramFrequency
- + stepSize * (bigramFrequency + 1.0f);
+ final float resultFreqFloat = unigramFrequency + stepSize * (bigramFrequency + 1.0f);
return (int)resultFreqFloat;
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index b3fbb9fb5..705f66414 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -59,9 +59,11 @@ public final class FormatSpec {
* l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
* a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
* g | ELSE
- * s | is moved ? 2 bits, 11 = no
- * | 01 = yes
+ * s | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED
+ * | This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+ * | 01 = yes : FLAG_IS_MOVED
* | the new address is stored in the same place as the parent address
+ * | is deleted? 10 = yes : FLAG_IS_DELETED
* | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
* | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
* | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
@@ -170,6 +172,7 @@ public final class FormatSpec {
static final int PARENT_ADDRESS_SIZE = 3;
static final int FORWARD_LINK_ADDRESS_SIZE = 3;
+ // These flags are used only in the static dictionary.
static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
@@ -183,7 +186,13 @@ public final class FormatSpec {
static final int FLAG_HAS_BIGRAMS = 0x04;
static final int FLAG_IS_NOT_A_WORD = 0x02;
static final int FLAG_IS_BLACKLISTED = 0x01;
- static final int FLAG_IS_MOVED = 0x40;
+
+ // These flags are used only in the dynamic dictionary.
+ static final int MASK_MOVE_AND_DELETE_FLAG = 0xC0;
+ static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40;
+ static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+ static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+ static final int FLAG_IS_DELETED = 0x80;
static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
@@ -210,10 +219,13 @@ public final class FormatSpec {
static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
+ static final int MAX_BIGRAMS_IN_A_GROUP = 10000;
static final int MAX_TERMINAL_FREQUENCY = 255;
static final int MAX_BIGRAM_FREQUENCY = 15;
+ public static final int SHORTCUT_WHITELIST_FREQUENCY = 15;
+
// This option needs to be the same numeric value as the one in binary_format.h.
static final int NOT_VALID_WORD = -99;
static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 3193ef457..ee647abed 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -21,6 +21,7 @@ import com.android.inputmethod.latin.Constants;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -141,6 +142,33 @@ public final class FusionDictionary implements Iterable<Word> {
return NOT_A_TERMINAL != mFrequency;
}
+ public int getFrequency() {
+ return mFrequency;
+ }
+
+ public boolean getIsNotAWord() {
+ return mIsNotAWord;
+ }
+
+ public boolean getIsBlacklistEntry() {
+ return mIsBlacklistEntry;
+ }
+
+ public ArrayList<WeightedString> getShortcutTargets() {
+ // We don't want write permission to escape outside the package, so we return a copy
+ if (null == mShortcutTargets) return null;
+ final ArrayList<WeightedString> copyOfShortcutTargets =
+ new ArrayList<WeightedString>(mShortcutTargets);
+ return copyOfShortcutTargets;
+ }
+
+ public ArrayList<WeightedString> getBigrams() {
+ // We don't want write permission to escape outside the package, so we return a copy
+ if (null == mBigrams) return null;
+ final ArrayList<WeightedString> copyOfBigrams = new ArrayList<WeightedString>(mBigrams);
+ return copyOfBigrams;
+ }
+
public boolean hasSeveralChars() {
assert(mChars.length > 0);
return 1 < mChars.length;
@@ -249,8 +277,6 @@ public final class FusionDictionary implements Iterable<Word> {
/**
* Options global to the dictionary.
- *
- * There are no options at the moment, so this class is empty.
*/
public static final class DictionaryOptions {
public final boolean mGermanUmlautProcessing;
@@ -262,6 +288,43 @@ public final class FusionDictionary implements Iterable<Word> {
mGermanUmlautProcessing = germanUmlautProcessing;
mFrenchLigatureProcessing = frenchLigatureProcessing;
}
+ @Override
+ public String toString() { // Convenience method
+ return toString(0, false);
+ }
+ public String toString(final int indentCount, final boolean plumbing) {
+ final StringBuilder indent = new StringBuilder();
+ if (plumbing) {
+ indent.append("H:");
+ } else {
+ for (int i = 0; i < indentCount; ++i) {
+ indent.append(" ");
+ }
+ }
+ final StringBuilder s = new StringBuilder();
+ for (final String optionKey : mAttributes.keySet()) {
+ s.append(indent);
+ s.append(optionKey);
+ s.append(" = ");
+ if ("date".equals(optionKey) && !plumbing) {
+ // Date needs a number of milliseconds, but the dictionary contains seconds
+ s.append(new Date(
+ 1000 * Long.parseLong(mAttributes.get(optionKey))).toString());
+ } else {
+ s.append(mAttributes.get(optionKey));
+ }
+ s.append("\n");
+ }
+ if (mGermanUmlautProcessing) {
+ s.append(indent);
+ s.append("Needs German umlaut processing\n");
+ }
+ if (mFrenchLigatureProcessing) {
+ s.append(indent);
+ s.append("Needs French ligature processing\n");
+ }
+ return s.toString();
+ }
}
public final DictionaryOptions mOptions;
@@ -279,7 +342,7 @@ public final class FusionDictionary implements Iterable<Word> {
/**
* Helper method to convert a String to an int array.
*/
- static private int[] getCodePoints(final String word) {
+ static int[] getCodePoints(final String word) {
// TODO: this is a copy-paste of the contents of StringUtils.toCodePointArray,
// which is not visible from the makedict package. Factor this code.
final char[] characters = word.toCharArray();
@@ -358,6 +421,10 @@ public final class FusionDictionary implements Iterable<Word> {
if (charGroup2 == null) {
add(getCodePoints(word2), 0, null, false /* isNotAWord */,
false /* isBlacklistEntry */);
+ // The chargroup for the first word may have moved by the above insertion,
+ // if word1 and word2 share a common stem that happens not to have been
+ // a cutting point until now. In this case, we need to refresh charGroup.
+ charGroup = findWordInTree(mRoot, word1);
}
charGroup.addBigram(word2, frequency);
} else {
@@ -550,6 +617,7 @@ public final class FusionDictionary implements Iterable<Word> {
/**
* Helper method to find a word in a given branch.
*/
+ @SuppressWarnings("unused")
public static CharGroup findWordInTree(Node node, final String s) {
int index = 0;
final StringBuilder checker = DBG ? new StringBuilder() : null;
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 5a11ae534..2f146f86c 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -212,7 +212,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
}
- private final ArrayList<CharSequence> mSuggestions;
+ private final ArrayList<String> mSuggestions;
private final int[] mScores;
private final String mOriginalText;
private final float mSuggestionThreshold;
@@ -335,7 +335,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY);
final int bestScore = mScores[mLength - 1];
- final CharSequence bestSuggestion = mSuggestions.get(0);
+ final String bestSuggestion = mSuggestions.get(0);
final float normalizedScore =
BinaryDictionary.calcNormalizedScore(
mOriginalText, bestSuggestion.toString(), bestScore);
@@ -438,15 +438,23 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
final int len = text.length();
int capsCount = 1;
+ int letterCount = 1;
for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) {
- if (1 != capsCount && i != capsCount) break;
- if (Character.isUpperCase(text.codePointAt(i))) ++capsCount;
+ if (1 != capsCount && letterCount != capsCount) break;
+ final int codePoint = text.codePointAt(i);
+ if (Character.isUpperCase(codePoint)) {
+ ++capsCount;
+ ++letterCount;
+ } else if (Character.isLetter(codePoint)) {
+ // We need to discount non-letters since they may not be upper-case, but may
+ // still be part of a word (e.g. single quote or dash, as in "IT'S" or "FULL-TIME")
+ ++letterCount;
+ }
}
- // We know the first char is upper case. So we want to test if either everything
- // else is lower case, or if everything else is upper case. If the string is
- // exactly one char long, then we will arrive here with capsCount 1, and this is
- // correct, too.
+ // We know the first char is upper case. So we want to test if either every letter other
+ // than the first is lower case, or if they are all upper case. If the string is exactly
+ // one char long, then we will arrive here with letterCount 1, and this is correct, too.
if (1 == capsCount) return CAPITALIZE_FIRST;
- return (len == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
+ return (letterCount == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 53ed4d3c3..470943be1 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -28,9 +28,11 @@ import android.view.textservice.TextInfo;
import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.LocaleUtils;
-import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer;
import java.util.ArrayList;
@@ -188,6 +190,35 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
return (letterCount * 4 < length * 3);
}
+ /**
+ * Helper method to test valid capitalizations of a word.
+ *
+ * If the "text" is lower-case, we test only the exact string.
+ * If the "Text" is capitalized, we test the exact string "Text" and the lower-cased
+ * version of it "text".
+ * If the "TEXT" is fully upper case, we test the exact string "TEXT", the lower-cased
+ * version of it "text" and the capitalized version of it "Text".
+ */
+ private boolean isInDictForAnyCapitalization(final Dictionary dict, final String text,
+ final int capitalizeType) {
+ // If the word is in there as is, then it's in the dictionary. If not, we'll test lower
+ // case versions, but only if the word is not already all-lower case or mixed case.
+ if (dict.isValidWord(text)) return true;
+ if (AndroidSpellCheckerService.CAPITALIZE_NONE == capitalizeType) return false;
+
+ // If we come here, we have a capitalized word (either First- or All-).
+ // Downcase the word and look it up again. If the word is only capitalized, we
+ // tested all possibilities, so if it's still negative we can return false.
+ final String lowerCaseText = text.toLowerCase(mLocale);
+ if (dict.isValidWord(lowerCaseText)) return true;
+ if (AndroidSpellCheckerService.CAPITALIZE_FIRST == capitalizeType) return false;
+
+ // If the lower case version is not in the dictionary, it's still possible
+ // that we have an all-caps version of a word that needs to be capitalized
+ // according to the dictionary. E.g. "GERMANS" only exists in the dictionary as "Germans".
+ return dict.isValidWord(StringUtils.toTitleCase(lowerCaseText, mLocale));
+ }
+
// Note : this must be reentrant
/**
* Gets a list of suggestions for a specific string. This returns a list of possible
@@ -268,17 +299,11 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
dictInfo.mDictionary.getSuggestions(composer, prevWord,
dictInfo.mProximityInfo);
for (final SuggestedWordInfo suggestion : suggestions) {
- final String suggestionStr = suggestion.mWord.toString();
+ final String suggestionStr = suggestion.mWord;
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));
- }
+ isInDict = isInDictForAnyCapitalization(dictInfo.mDictionary, text, capitalizeType);
} finally {
if (null != dictInfo) {
if (!mDictionaryPool.offer(dictInfo)) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 1fb2bbb6a..eae5d2e60 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -51,11 +51,11 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity>
new Dictionary(Dictionary.TYPE_MAIN) {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
return noSuggestions;
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
// This is never called. However if for some strange reason it ever gets
// called, returning true is less destructive (it will not underline the
// word in red).
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 11bb97031..6c0d79c2b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.Constants;
@@ -23,7 +24,7 @@ import com.android.inputmethod.latin.Constants;
import java.util.TreeMap;
public final class SpellCheckerProximityInfo {
- /* public for test */
+ @UsedForTesting
final public static int NUL = Constants.NOT_A_CODE;
// This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 4e9fd1968..35d5a0067 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -22,7 +22,6 @@ import android.graphics.drawable.Drawable;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -66,7 +65,7 @@ public final class MoreSuggestions extends Keyboard {
int pos = fromPos, rowStartPos = fromPos;
final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
while (pos < size) {
- final String word = suggestions.getWord(pos).toString();
+ final String word = suggestions.getWord(pos);
// TODO: Should take care of text x-scaling.
mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
final int numColumn = pos - rowStartPos + 1;
@@ -176,11 +175,11 @@ public final class MoreSuggestions extends Keyboard {
}
public Builder layout(final SuggestedWords suggestions, final int fromPos,
- final int maxWidth, final int minWidth, final int maxRow) {
- final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
+ final int maxWidth, final int minWidth, final int maxRow,
+ final Keyboard parentKeyboard) {
final int xmlId = R.xml.kbd_suggestions_pane_template;
- load(xmlId, keyboard.mId);
- mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
+ load(xmlId, parentKeyboard.mId);
+ mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight);
final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 03a2e73d1..8cc09f39b 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -34,6 +34,7 @@ import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.CoordinateUtils;
import com.android.inputmethod.latin.R;
/**
@@ -41,7 +42,7 @@ import com.android.inputmethod.latin.R;
* key presses and touch movements.
*/
public final class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
- private final int[] mCoordinates = new int[2];
+ private final int[] mCoordinates = CoordinateUtils.newInstance();
final KeyDetector mModalPanelKeyDetector;
private final KeyDetector mSlidingPanelKeyDetector;
@@ -56,17 +57,17 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
final KeyboardActionListener mSuggestionsPaneListener =
new KeyboardActionListener.Adapter() {
@Override
- public void onPressKey(int primaryCode) {
+ public void onPressKey(final int primaryCode) {
mListener.onPressKey(primaryCode);
}
@Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {
+ public void onReleaseKey(final int primaryCode, final boolean withSliding) {
mListener.onReleaseKey(primaryCode, withSliding);
}
@Override
- public void onCodeInput(int primaryCode, int x, int y) {
+ public void onCodeInput(final int primaryCode, final int x, final int y) {
final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
@@ -79,11 +80,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
};
- public MoreSuggestionsView(Context context, AttributeSet attrs) {
+ public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreSuggestionsViewStyle);
}
- public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ public MoreSuggestionsView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
@@ -94,7 +96,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard != null) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
@@ -110,7 +112,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public void setKeyboard(Keyboard keyboard) {
+ public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mModalPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop());
mSlidingPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@@ -138,15 +140,16 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
// Suggestions pane needs no pop-up key preview displayed, so we pass always false with a
// delay of 0. The delay does not matter actually since the popup is not shown anyway.
super.setKeyPreviewPopupEnabled(false, 0);
}
@Override
- public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
- PopupWindow window, KeyboardActionListener listener) {
+ public void showMoreKeysPanel(final View parentView, final Controller controller,
+ final int pointX, final int pointY, final PopupWindow window,
+ final KeyboardActionListener listener) {
mController = controller;
mListener = listener;
final View container = (View)getParent();
@@ -161,7 +164,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
window.setHeight(container.getMeasuredHeight());
parentView.getLocationInWindow(mCoordinates);
window.showAtLocation(parentView, Gravity.NO_GRAVITY,
- x + mCoordinates[0], y + mCoordinates[1]);
+ x + CoordinateUtils.x(mCoordinates), y + CoordinateUtils.y(mCoordinates));
mOriginX = x + container.getPaddingLeft();
mOriginY = y + container.getPaddingTop();
@@ -179,12 +182,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public int translateX(int x) {
+ public int translateX(final int x) {
return x - mOriginX;
}
@Override
- public int translateY(int y) {
+ public int translateY(final int y) {
return y - mOriginY;
}
@@ -211,7 +214,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
};
@Override
- public boolean onTouchEvent(MotionEvent me) {
+ public boolean onTouchEvent(final MotionEvent me) {
final int action = me.getAction();
final long eventTime = me.getEventTime();
final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e926fa209..e7cb97fc2 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -52,13 +52,16 @@ import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.ViewLayoutUtils;
import com.android.inputmethod.latin.AutoCorrection;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResourceUtils;
@@ -74,7 +77,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
OnLongClickListener {
public interface Listener {
public boolean addWordToUserDictionary(String word);
- public void pickSuggestionManually(int index, CharSequence word);
+ public void pickSuggestionManually(int index, String word);
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
@@ -83,7 +86,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
static final boolean DBG = LatinImeLogger.sDBG;
private final ViewGroup mSuggestionsStrip;
- private KeyboardView mKeyboardView;
+ KeyboardView mKeyboardView;
private final View mMoreSuggestionsContainer;
private final MoreSuggestionsView mMoreSuggestionsView;
@@ -97,8 +100,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
- private Listener mListener;
- private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+ Listener mListener;
+ SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
private final SuggestionStripViewParams mParams;
private static final float MIN_TEXT_XSCALE = 0.70f;
@@ -108,12 +111,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
private static final int MSG_HIDE_PREVIEW = 0;
- public UiHandler(SuggestionStripView outerInstance) {
+ public UiHandler(final SuggestionStripView outerInstance) {
super(outerInstance);
}
@Override
- public void dispatchMessage(Message msg) {
+ public void dispatchMessage(final Message msg) {
final SuggestionStripView suggestionStripView = getOuterInstance();
switch (msg.what) {
case MSG_HIDE_PREVIEW:
@@ -177,8 +180,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final TextView mLeftwardsArrowView;
private final TextView mHintToSaveView;
- public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
- ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
+ public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
+ final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
+ final ArrayList<TextView> infos) {
mWords = words;
mDividers = dividers;
mInfos = infos;
@@ -250,7 +254,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
}
- public int setMoreSuggestionsHeight(int remainingHeight) {
+ public int setMoreSuggestionsHeight(final int remainingHeight) {
final int currentHeight = getMoreSuggestionsHeight();
if (currentHeight <= remainingHeight) {
return currentHeight;
@@ -262,7 +266,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return newHeight;
}
- private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) {
+ private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
+ final int color) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextAlign(Align.CENTER);
@@ -279,8 +284,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return new BitmapDrawable(res, buffer);
}
- private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) {
- final CharSequence word = suggestedWords.getWord(pos);
+ private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
+ final int pos) {
+ final String word = suggestedWords.getWord(pos);
final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
if (!isAutoCorrect && !isTypedWordValid)
@@ -299,7 +305,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return spannedWord;
}
- private int getWordPosition(int index, SuggestedWords suggestedWords) {
+ private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
// TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
// suggestions.
final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
@@ -312,7 +318,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private int getSuggestionTextColor(int index, SuggestedWords suggestedWords, int pos) {
+ private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
+ final int pos) {
// TODO: Need to revisit this logic with bigram suggestions
final boolean isSuggested = (pos != 0);
@@ -331,7 +338,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
// is in slot 1.
if (index == mCenterSuggestionIndex
&& AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
- suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
+ suggestedWords.getWord(1), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}
}
@@ -355,8 +362,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
params.gravity = Gravity.CENTER;
}
- public void layout(SuggestedWords suggestedWords, ViewGroup stripView, ViewGroup placer,
- int stripWidth) {
+ public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
+ final ViewGroup placer, final int stripWidth) {
if (suggestedWords.mIsPunctuationSuggestions) {
layoutPunctuationSuggestions(suggestedWords, stripView);
return;
@@ -402,7 +409,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
x += word.getMeasuredWidth();
if (DBG && pos < suggestedWords.size()) {
- final CharSequence debugInfo = Utils.getDebugInfo(suggestedWords, pos);
+ final String debugInfo = Utils.getDebugInfo(suggestedWords, pos);
if (debugInfo != null) {
final TextView info = mInfos.get(pos);
info.setText(debugInfo);
@@ -418,14 +425,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private int getSuggestionWidth(int index, int maxWidth) {
+ private int getSuggestionWidth(final int index, final int maxWidth) {
final int paddings = mPadding * mSuggestionsCountInStrip;
final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
final int availableWidth = maxWidth - paddings - dividers;
return (int)(availableWidth * getSuggestionWeight(index));
}
- private float getSuggestionWeight(int index) {
+ private float getSuggestionWeight(final int index) {
if (index == mCenterSuggestionIndex) {
return mCenterSuggestionWeight;
} else {
@@ -434,7 +441,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private void setupTexts(SuggestedWords suggestedWords, int countInStrip) {
+ private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
mTexts.clear();
final int count = Math.min(suggestedWords.size(), countInStrip);
for (int pos = 0; pos < count; pos++) {
@@ -447,8 +454,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private void layoutPunctuationSuggestions(SuggestedWords suggestedWords,
- ViewGroup stripView) {
+ private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
+ final ViewGroup stripView) {
final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
for (int index = 0; index < countInStrip; index++) {
if (index != 0) {
@@ -459,7 +466,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
final TextView word = mWords.get(index);
word.setEnabled(true);
word.setTextColor(mColorAutoCorrect);
- final CharSequence text = suggestedWords.getWord(index);
+ final String text = suggestedWords.getWord(index);
word.setText(text);
word.setTextScaleX(1.0f);
word.setCompoundDrawables(null, null, null, null);
@@ -469,8 +476,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
mMoreSuggestionsAvailable = false;
}
- public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView,
- int stripWidth, CharSequence hintText, OnClickListener listener) {
+ public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView,
+ final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
final int width = stripWidth - mDividerWidth - mPadding * 2;
final TextView wordView = mWordToSaveView;
@@ -511,11 +518,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return (CharSequence)mWordToSaveView.getTag();
}
- public boolean isAddToDictionaryShowing(View v) {
+ public boolean isAddToDictionaryShowing(final View v) {
return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
}
- private static void setLayoutWeight(View v, float weight, int height) {
+ private static void setLayoutWeight(final View v, final float weight, final int height) {
final ViewGroup.LayoutParams lp = v.getLayoutParams();
if (lp instanceof LinearLayout.LayoutParams) {
final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
@@ -525,7 +532,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) {
+ private static float getTextScaleX(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
if (width <= maxWidth) {
@@ -534,8 +542,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return maxWidth / (float)width;
}
- private static CharSequence getEllipsizedText(CharSequence text, int maxWidth,
- TextPaint paint) {
+ private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
if (text == null) return null;
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
@@ -556,7 +564,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return ellipsized;
}
- private static int getTextWidth(CharSequence text, TextPaint paint) {
+ private static int getTextWidth(final CharSequence text, final TextPaint paint) {
if (TextUtils.isEmpty(text)) return 0;
final Typeface savedTypeface = paint.getTypeface();
paint.setTypeface(getTextTypeface(text));
@@ -571,7 +579,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return width;
}
- private static Typeface getTextTypeface(CharSequence text) {
+ private static Typeface getTextTypeface(final CharSequence text) {
if (!(text instanceof SpannableString))
return Typeface.DEFAULT;
@@ -593,11 +601,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
* @param context
* @param attrs
*/
- public SuggestionStripView(Context context, AttributeSet attrs) {
+ public SuggestionStripView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.suggestionStripViewStyle);
}
- public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
+ public SuggestionStripView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -658,15 +667,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
* A connection back to the input method.
* @param listener
*/
- public void setListener(Listener listener, View inputView) {
+ public void setListener(final Listener listener, final View inputView) {
mListener = listener;
mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view);
}
- public void setSuggestions(SuggestedWords suggestedWords) {
- if (suggestedWords == null)
- return;
-
+ public void setSuggestions(final SuggestedWords suggestedWords) {
clear();
mSuggestedWords = suggestedWords;
mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
@@ -675,7 +681,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- public int setMoreSuggestionsHeight(int remainingHeight) {
+ public int setMoreSuggestionsHeight(final int remainingHeight) {
return mParams.setMoreSuggestionsHeight(remainingHeight);
}
@@ -684,7 +690,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
&& mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
}
- public void showAddToDictionaryHint(CharSequence word, CharSequence hintText) {
+ public void showAddToDictionaryHint(final String word, final CharSequence hintText) {
clear();
mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
}
@@ -708,16 +714,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
dismissMoreSuggestions();
}
- private void hidePreview() {
+ void hidePreview() {
mPreviewPopup.dismiss();
}
private final KeyboardActionListener mMoreSuggestionsListener =
new KeyboardActionListener.Adapter() {
@Override
- public boolean onCustomRequest(int requestCode) {
+ public boolean onCustomRequest(final int requestCode) {
final int index = requestCode;
- final CharSequence word = mSuggestedWords.getWord(index);
+ final String word = mSuggestedWords.getWord(index);
mListener.pickSuggestionManually(index, word);
dismissMoreSuggestions();
return true;
@@ -737,7 +743,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
};
- private boolean dismissMoreSuggestions() {
+ boolean dismissMoreSuggestions() {
if (mMoreSuggestionsWindow.isShowing()) {
mMoreSuggestionsWindow.dismiss();
return true;
@@ -746,41 +752,43 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
@Override
- public boolean onLongClick(View view) {
+ public boolean onLongClick(final View view) {
+ KeyboardSwitcher.getInstance().hapticAndAudioFeedback(Constants.NOT_A_CODE);
return showMoreSuggestions();
}
- private boolean showMoreSuggestions() {
+ boolean showMoreSuggestions() {
+ final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard();
+ if (parentKeyboard == null) {
+ return false;
+ }
final SuggestionStripViewParams params = mParams;
- if (params.mMoreSuggestionsAvailable) {
- final int stripWidth = getWidth();
- final View container = mMoreSuggestionsContainer;
- final int maxWidth = stripWidth - container.getPaddingLeft()
- - container.getPaddingRight();
- final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
- builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
- (int)(maxWidth * params.mMinMoreSuggestionsWidth),
- params.getMaxMoreSuggestionsRow());
- mMoreSuggestionsView.setKeyboard(builder.build());
- container.measure(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
- final int pointX = stripWidth / 2;
- final int pointY = -params.mMoreSuggestionsBottomGap;
- moreKeysPanel.showMoreKeysPanel(
- this, mMoreSuggestionsController, pointX, pointY,
- mMoreSuggestionsWindow, mMoreSuggestionsListener);
- mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
- mOriginX = mLastX;
- mOriginY = mLastY;
- mKeyboardView.dimEntireKeyboard(true);
- for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
- mWords.get(i).setPressed(false);
- }
- return true;
+ if (!params.mMoreSuggestionsAvailable) {
+ return false;
}
- return false;
+ final int stripWidth = getWidth();
+ final View container = mMoreSuggestionsContainer;
+ final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
+ final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
+ builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
+ (int)(maxWidth * params.mMinMoreSuggestionsWidth),
+ params.getMaxMoreSuggestionsRow(), parentKeyboard);
+ mMoreSuggestionsView.setKeyboard(builder.build());
+ container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
+ final int pointX = stripWidth / 2;
+ final int pointY = -params.mMoreSuggestionsBottomGap;
+ moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
+ mMoreSuggestionsWindow, mMoreSuggestionsListener);
+ mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
+ mOriginX = mLastX;
+ mOriginY = mLastY;
+ mKeyboardView.dimEntireKeyboard(true);
+ for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
+ mWords.get(i).setPressed(false);
+ }
+ return true;
}
// Working variables for onLongClick and dispatchTouchEvent.
@@ -807,7 +815,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
};
@Override
- public boolean dispatchTouchEvent(MotionEvent me) {
+ public boolean dispatchTouchEvent(final MotionEvent me) {
if (!mMoreSuggestionsWindow.isShowing()
|| mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
mLastX = (int)me.getX();
@@ -849,7 +857,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
@Override
- public void onClick(View view) {
+ public void onClick(final View view) {
if (mParams.isAddToDictionaryShowing(view)) {
mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
clear();
@@ -863,7 +871,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (index >= mSuggestedWords.size())
return;
- final CharSequence word = mSuggestedWords.getWord(index);
+ final String word = mSuggestedWords.getWord(index);
mListener.pickSuggestionManually(index, word);
}
diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java
index 745768d35..94dbf3960 100644
--- a/java/src/com/android/inputmethod/research/MainLogBuffer.java
+++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java
@@ -16,16 +16,22 @@
package com.android.inputmethod.research;
+import android.util.Log;
+
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.Suggest;
import java.util.Random;
public class MainLogBuffer extends LogBuffer {
+ private static final String TAG = MainLogBuffer.class.getSimpleName();
+ // For privacy reasons, be sure to set to "false" for production code.
+ private static final boolean DEBUG = false;
+
// The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams.
private static final int N_GRAM_SIZE = 2;
// The number of words between n-grams to omit from the log.
- private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18;
+ private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = DEBUG ? 2 : 18;
private final ResearchLog mResearchLog;
private Suggest mSuggest;
@@ -61,6 +67,9 @@ public class MainLogBuffer extends LogBuffer {
mWordsUntilSafeToSample--;
}
}
+ if (DEBUG) {
+ Log.d(TAG, "shiftedIn " + (newLogUnit.hasWord() ? newLogUnit.getWord() : ""));
+ }
}
public void resetWordCounter() {
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 763fd6e00..982d10458 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -57,9 +57,9 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.InputTypeUtils;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputConnection;
@@ -143,7 +143,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private final Statistics mStatistics;
private Intent mUploadIntent;
- private PendingIntent mUploadPendingIntent;
private LogUnit mCurrentLogUnit = new LogUnit();
@@ -188,7 +187,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
mInputMethodService = ims;
mPrefs = prefs;
mUploadIntent = new Intent(mInputMethodService, UploaderService.class);
- mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0);
if (ProductionFlag.IS_EXPERIMENTAL) {
scheduleUploadingService(mInputMethodService);
@@ -427,21 +425,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
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) {
@@ -687,7 +670,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
/* package for test */ void commitCurrentLogUnit() {
if (DEBUG) {
- Log.d(TAG, "commitCurrentLogUnit");
+ Log.d(TAG, "commitCurrentLogUnit" + (mCurrentLogUnit.hasWord() ?
+ ": " + mCurrentLogUnit.getWord() : ""));
}
if (!mCurrentLogUnit.isEmpty()) {
if (mMainLogBuffer != null) {
@@ -706,28 +690,45 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
}
}
+ private static final String[] EVENTKEYS_LOG_SEGMENT_START = {
+ "logSegmentStart", "isIncludingPrivateData"
+ };
+ private static final String[] EVENTKEYS_LOG_SEGMENT_END = {
+ "logSegmentEnd"
+ };
/* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ final LogUnit openingLogUnit = new LogUnit();
+ final Object[] values = {
+ isIncludingPrivateData
+ };
+ openingLogUnit.addLogStatement(EVENTKEYS_LOG_SEGMENT_START, values,
+ false /* isPotentiallyPrivate */);
+ researchLog.publish(openingLogUnit, true /* isIncludingPrivateData */);
LogUnit logUnit;
while ((logUnit = logBuffer.shiftOut()) != null) {
researchLog.publish(logUnit, isIncludingPrivateData);
}
+ final LogUnit closingLogUnit = new LogUnit();
+ closingLogUnit.addLogStatement(EVENTKEYS_LOG_SEGMENT_END, EVENTKEYS_NULLVALUES,
+ false /* isPotentiallyPrivate */);
+ researchLog.publish(closingLogUnit, true /* isIncludingPrivateData */);
}
- private boolean hasOnlyLetters(final String word) {
+ private boolean hasLetters(final String word) {
final int length = word.length();
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i);
- if (!Character.isLetter(codePoint)) {
- return false;
+ if (Character.isLetter(codePoint)) {
+ return true;
}
}
- return true;
+ return false;
}
private void onWordComplete(final String word) {
Log.d(TAG, "onWordComplete: " + word);
- if (word != null && word.length() > 0 && hasOnlyLetters(word)) {
+ if (word != null && word.length() > 0 && hasLetters(word)) {
mCurrentLogUnit.setWord(word);
mStatistics.recordWordEntered();
}
@@ -791,8 +792,11 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
final SharedPreferences prefs) {
final ResearchLogger researchLogger = getInstance();
- researchLogger.start();
if (editorInfo != null) {
+ final boolean isPassword = InputTypeUtils.isPasswordInputType(editorInfo.inputType)
+ || InputTypeUtils.isVisiblePasswordInputType(editorInfo.inputType);
+ getInstance().setIsPasswordView(isPassword);
+ researchLogger.start();
final Context context = researchLogger.mInputMethodService;
try {
final PackageInfo packageInfo;
@@ -818,10 +822,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
stop();
}
- private static final String[] EVENTKEYS_USER_FEEDBACK = {
- "UserFeedback", "FeedbackContents"
- };
-
private static final String[] EVENTKEYS_PREFS_CHANGED = {
"PrefsChanged", "prefs"
};
@@ -870,7 +870,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final long time = SystemClock.uptimeMillis();
final ResearchLogger researchLogger = getInstance();
final Object[] values = {
- Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y
+ Constants.printableCode(scrubDigitFromCodePoint(code)), x, y
};
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
if (Character.isDigit(code)) {
@@ -903,18 +903,20 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
public static void latinIME_onWindowHidden(final int savedSelectionStart,
final int savedSelectionEnd, final InputConnection ic) {
if (ic != null) {
- // Capture the TextView contents. This will trigger onUpdateSelection(), so we
- // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
- // it can tell that it was generated by the logging code, and not by the user, and
- // therefore keep user-visible state as is.
- ic.beginBatchEdit();
- ic.performContextMenuAction(android.R.id.selectAll);
- CharSequence charSequence = ic.getSelectedText(0);
- ic.setSelection(savedSelectionStart, savedSelectionEnd);
- ic.endBatchEdit();
- sLatinIMEExpectingUpdateSelection = true;
final Object[] values = new Object[2];
if (OUTPUT_ENTIRE_BUFFER) {
+ // Capture the TextView contents. This will trigger onUpdateSelection(), so we
+ // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
+ // it can tell that it was generated by the logging code, and not by the user, and
+ // therefore keep user-visible state as is.
+ ic.beginBatchEdit();
+ ic.performContextMenuAction(android.R.id.selectAll);
+ CharSequence charSequence = ic.getSelectedText(0);
+ if (savedSelectionStart != -1 && savedSelectionEnd != -1) {
+ ic.setSelection(savedSelectionStart, savedSelectionEnd);
+ }
+ ic.endBatchEdit();
+ sLatinIMEExpectingUpdateSelection = true;
if (TextUtils.isEmpty(charSequence)) {
values[0] = false;
values[1] = "";
@@ -1006,7 +1008,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
};
public static void latinIME_sendKeyCodePoint(final int code) {
final Object[] values = {
- Keyboard.printableCode(scrubDigitFromCodePoint(code))
+ Constants.printableCode(scrubDigitFromCodePoint(code))
};
final ResearchLogger researchLogger = getInstance();
researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values);
@@ -1059,7 +1061,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
keyboard.mOccupiedHeight,
keyboard.mKeys
};
- getInstance().setIsPasswordView(isPasswordView);
getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD, values);
}
}
@@ -1092,7 +1093,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
if (key != null) {
String outputText = key.getOutputText();
final Object[] values = {
- Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null
+ Constants.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null
: scrubDigitsFromString(outputText.toString()),
x, y, ignoreModifierKey, altersCode, key.isEnabled()
};
@@ -1109,7 +1110,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
final boolean withSliding, final boolean ignoreModifierKey) {
if (key != null) {
final Object[] values = {
- Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
+ Constants.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
ignoreModifierKey, key.isEnabled()
};
getInstance().enqueuePotentiallyPrivateEvent(
diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java
index eab465aa2..2065ab15e 100644
--- a/java/src/com/android/inputmethod/research/Statistics.java
+++ b/java/src/com/android/inputmethod/research/Statistics.java
@@ -16,9 +16,14 @@
package com.android.inputmethod.research;
-import com.android.inputmethod.keyboard.Keyboard;
+import android.util.Log;
+
+import com.android.inputmethod.latin.Constants;
public class Statistics {
+ private static final String TAG = Statistics.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
// Number of characters entered during a typing session
int mCharCount;
// Number of letter characters entered during a typing session
@@ -103,8 +108,11 @@ public class Statistics {
}
public void recordChar(int codePoint, long time) {
+ if (DEBUG) {
+ Log.d(TAG, "recordChar() called");
+ }
final long delta = time - mLastTapTime;
- if (codePoint == Keyboard.CODE_DELETE) {
+ if (codePoint == Constants.CODE_DELETE) {
mDeleteKeyCount++;
if (delta < MIN_DELETION_INTERMISSION) {
if (mIsLastKeyDeleteKey) {