aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod')
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java68
-rw-r--r--java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java70
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java11
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java17
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java3
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java55
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java17
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java4
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java17
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java41
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java35
-rw-r--r--java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java16
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java64
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java54
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java6
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java6
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java60
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java185
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java14
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java14
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java3
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java (renamed from java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java)25
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java59
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java18
32 files changed, 588 insertions, 302 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 3a6453128..7a3510ee1 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -28,6 +28,7 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.StringUtils;
import java.util.Locale;
@@ -79,14 +80,6 @@ final class KeyCodeDescriptionMapper {
/**
* Returns the localized description of the action performed by a specified
* key based on the current keyboard state.
- * <p>
- * The order of precedence for key descriptions is:
- * <ol>
- * <li>Manually-defined based on the key label</li>
- * <li>Automatic or manually-defined based on the key code</li>
- * <li>Automatically based on the key label</li>
- * <li>{code null} for keys with no label or key code defined</li>
- * </p>
*
* @param context The package's context.
* @param keyboard The keyboard on which the key resides.
@@ -121,7 +114,20 @@ final class KeyCodeDescriptionMapper {
// Just attempt to speak the description.
if (code != Constants.CODE_UNSPECIFIED) {
- return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
+ // If the key description should be obscured, now is the time to do it.
+ final boolean isDefinedNonCtrl = Character.isDefined(code)
+ && !Character.isISOControl(code);
+ if (shouldObscure && isDefinedNonCtrl) {
+ return context.getString(OBSCURED_KEY_RES_ID);
+ }
+ final String description = getDescriptionForCodePoint(context, code);
+ if (description != null) {
+ return description;
+ }
+ if (!TextUtils.isEmpty(key.getLabel())) {
+ return key.getLabel();
+ }
+ return context.getString(R.string.spoken_description_unknown);
}
return null;
}
@@ -247,57 +253,35 @@ final class KeyCodeDescriptionMapper {
/**
* Returns a localized character sequence describing what will happen when
- * the specified key is pressed based on its key code.
- * <p>
- * The order of precedence for key code descriptions is:
- * <ol>
- * <li>Manually-defined shift-locked description</li>
- * <li>Manually-defined shifted description</li>
- * <li>Manually-defined normal description</li>
- * <li>Automatic based on the character represented by the key code</li>
- * <li>Fall-back for undefined or control characters</li>
- * </ol>
- * </p>
+ * the specified key is pressed based on its key code point.
*
* @param context The package's context.
- * @param keyboard The keyboard on which the key resides.
- * @param key The key from which to obtain a description.
- * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
- * @return a character sequence describing the action performed by pressing the key
+ * @param codePoint The code point from which to obtain a description.
+ * @return a character sequence describing the code point.
*/
- private String getDescriptionForKeyCode(final Context context, final Keyboard keyboard,
- final Key key, final boolean shouldObscure) {
- final int code = key.getCode();
-
+ public String getDescriptionForCodePoint(final Context context, final int codePoint) {
// If the key description should be obscured, now is the time to do it.
- final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code);
- if (shouldObscure && isDefinedNonCtrl) {
- return context.getString(OBSCURED_KEY_RES_ID);
- }
- final int index = mKeyCodeMap.indexOfKey(code);
+ final int index = mKeyCodeMap.indexOfKey(codePoint);
if (index >= 0) {
return context.getString(mKeyCodeMap.valueAt(index));
}
- final String accentedLetter = getSpokenAccentedLetterDescription(context, code);
+ final String accentedLetter = getSpokenAccentedLetterDescription(context, codePoint);
if (accentedLetter != null) {
return accentedLetter;
}
// Here, <code>code</code> may be a base (non-accented) letter.
- final String unsupportedSymbol = getSpokenSymbolDescription(context, code);
+ final String unsupportedSymbol = getSpokenSymbolDescription(context, codePoint);
if (unsupportedSymbol != null) {
return unsupportedSymbol;
}
- final String emojiDescription = getSpokenEmojiDescription(context, code);
+ final String emojiDescription = getSpokenEmojiDescription(context, codePoint);
if (emojiDescription != null) {
return emojiDescription;
}
- if (isDefinedNonCtrl) {
- return Character.toString((char) code);
- }
- if (!TextUtils.isEmpty(key.getLabel())) {
- return key.getLabel();
+ if (Character.isDefined(codePoint) && !Character.isISOControl(codePoint)) {
+ return StringUtils.newSingleCodePointString(codePoint);
}
- return context.getString(R.string.spoken_description_unknown, code);
+ return null;
}
// TODO: Remove this method once TTS supports those accented letters' verbalization.
diff --git a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
index 4fdf5b8fa..96f84dde9 100644
--- a/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
+++ b/java/src/com/android/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.accessibility;
import android.content.Context;
+import android.graphics.Rect;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseIntArray;
@@ -58,7 +59,8 @@ public final class MainKeyboardAccessibilityDelegate
/** The most recently set keyboard mode. */
private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
private static final int KEYBOARD_IS_HIDDEN = -1;
- private boolean mShouldIgnoreOnRegisterHoverKey;
+ // The rectangle region to ignore hover events.
+ private final Rect mBoundsToIgnoreHoverEvent = new Rect();
private final AccessibilityLongPressTimer mAccessibilityLongPressTimer;
@@ -154,14 +156,28 @@ public final class MainKeyboardAccessibilityDelegate
case KeyboardId.ELEMENT_ALPHABET:
if (lastElementId == KeyboardId.ELEMENT_ALPHABET
|| lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ // Transition between alphabet mode and automatic shifted mode should be silently
+ // ignored because it can be determined by each key's talk back announce.
return;
}
resId = R.string.spoken_description_mode_alpha;
break;
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ // Resetting automatic shifted mode by pressing the shift key causes the transition
+ // from automatic shifted to manual shifted that should be silently ignored.
+ return;
+ }
resId = R.string.spoken_description_shiftmode_on;
break;
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+ // Resetting caps locked mode by pressing the shift key causes the transition
+ // from shift locked to shift lock shifted that should be silently ignored.
+ return;
+ }
+ resId = R.string.spoken_description_shiftmode_locked;
+ break;
case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
resId = R.string.spoken_description_shiftmode_locked;
break;
@@ -192,31 +208,49 @@ public final class MainKeyboardAccessibilityDelegate
@Override
protected void onRegisterHoverKey(final Key key, final MotionEvent event) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
if (DEBUG_HOVER) {
- Log.d(TAG, "onRegisterHoverKey: key=" + key + " ignore="
- + mShouldIgnoreOnRegisterHoverKey);
+ Log.d(TAG, "onRegisterHoverKey: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
}
- if (!mShouldIgnoreOnRegisterHoverKey) {
- super.onRegisterHoverKey(key, event);
+ if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
+ // This hover exit event points to the key that should be ignored.
+ // Clear the ignoring region to handle further hover events.
+ mBoundsToIgnoreHoverEvent.setEmpty();
+ return;
}
- mShouldIgnoreOnRegisterHoverKey = false;
+ super.onRegisterHoverKey(key, event);
}
@Override
protected void onHoverEnterTo(final Key key) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverEnterTo: key=" + key);
+ Log.d(TAG, "onHoverEnterTo: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
}
mAccessibilityLongPressTimer.cancelLongPress();
+ if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
+ return;
+ }
+ // This hover enter event points to the key that isn't in the ignoring region.
+ // Further hover events should be handled.
+ mBoundsToIgnoreHoverEvent.setEmpty();
super.onHoverEnterTo(key);
if (key.isLongPressEnabled()) {
mAccessibilityLongPressTimer.startLongPress(key);
}
}
+ @Override
protected void onHoverExitFrom(final Key key) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
if (DEBUG_HOVER) {
- Log.d(TAG, "onHoverExitFrom: key=" + key);
+ Log.d(TAG, "onHoverExitFrom: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
}
mAccessibilityLongPressTimer.cancelLongPress();
super.onHoverExitFrom(key);
@@ -246,6 +280,24 @@ public final class MainKeyboardAccessibilityDelegate
// or a key invokes IME switcher dialog, we should just ignore the next
// {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether
// {@link PointerTracker} is in operation or not.
- mShouldIgnoreOnRegisterHoverKey = !tracker.isInOperation();
+ if (tracker.isInOperation()) {
+ // This long press shows a more keys keyboard and further hover events should be
+ // handled.
+ mBoundsToIgnoreHoverEvent.setEmpty();
+ return;
+ }
+ // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}.
+ // We should ignore further hover events on this key.
+ mBoundsToIgnoreHoverEvent.set(key.getHitBox());
+ if (key.hasNoPanelAutoMoreKey()) {
+ // This long press has registered a code point without showing a more keys keyboard.
+ // We should talk back the code point if possible.
+ final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys()[0].mCode;
+ final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint(
+ mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey);
+ if (text != null) {
+ sendWindowStateChanged(text);
+ }
+ }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 77877143b..bcd0cd848 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -382,7 +382,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
mLanguageOnSpacebarTextSize = keyHeight * mLanguageOnSpacebarTextRatio;
if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector);
+ if (mAccessibilityDelegate == null) {
+ mAccessibilityDelegate = new MainKeyboardAccessibilityDelegate(this, mKeyDetector);
+ }
mAccessibilityDelegate.setKeyboard(keyboard);
} else {
mAccessibilityDelegate = null;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 4ca4abec6..0f575d30c 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -77,10 +77,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
// discarded at {@link InputView#dispatchHoverEvent(MotionEvent)}. Because only a hover
// event that is on this view is dispatched by the platform, we should use a
// {@link KeyDetector} that has no sliding allowance and no hysteresis.
- mKeyDetector = new KeyDetector();
- mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(this, mKeyDetector);
- mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
- mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
+ if (mAccessibilityDelegate == null) {
+ mKeyDetector = new KeyDetector();
+ mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate(
+ this, mKeyDetector);
+ mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_keys_keyboard);
+ mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard);
+ }
mAccessibilityDelegate.setKeyboard(keyboard);
} else {
mKeyDetector = new MoreKeysDetector(getResources().getDimension(
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
index 0166802a4..80ba60c82 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
@@ -55,7 +55,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements
private OnKeyEventListener mListener = EMPTY_LISTENER;
private final KeyDetector mKeyDetector = new KeyDetector();
private final GestureDetector mGestureDetector;
- private final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
+ private KeyboardAccessibilityDelegate<EmojiPageKeyboardView> mAccessibilityDelegate;
public EmojiPageKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.keyboardViewStyle);
@@ -67,7 +67,6 @@ final class EmojiPageKeyboardView extends KeyboardView implements
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
mHandler = new Handler();
- mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector);
}
public void setOnKeyEventListener(final OnKeyEventListener listener) {
@@ -81,6 +80,14 @@ final class EmojiPageKeyboardView extends KeyboardView implements
public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
+ if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
+ if (mAccessibilityDelegate == null) {
+ mAccessibilityDelegate = new KeyboardAccessibilityDelegate<>(this, mKeyDetector);
+ }
+ mAccessibilityDelegate.setKeyboard(keyboard);
+ } else {
+ mAccessibilityDelegate = null;
+ }
}
/**
@@ -88,8 +95,10 @@ final class EmojiPageKeyboardView extends KeyboardView implements
*/
@Override
public boolean onHoverEvent(final MotionEvent event) {
- if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
- return mAccessibilityDelegate.onHoverEvent(event);
+ final KeyboardAccessibilityDelegate<EmojiPageKeyboardView> accessibilityDelegate =
+ mAccessibilityDelegate;
+ if (accessibilityDelegate != null) {
+ return accessibilityDelegate.onHoverEvent(event);
}
return super.onHoverEvent(event);
}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
index abed5208b..e37cd2369 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
@@ -46,6 +46,7 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SubtypeSwitcher;
@@ -240,6 +241,8 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
@Override
public void onTabChanged(final String tabId) {
+ AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
+ Constants.CODE_UNSPECIFIED, this);
final int categoryId = mEmojiCategory.getCategoryId(tabId);
setCurrentCategoryId(categoryId, false /* force */);
updateEmojiCategoryPageIdView();
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 54bc29559..eb8b34ccd 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -16,14 +16,14 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
import android.content.Context;
import android.media.AudioManager;
import android.os.Vibrator;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import com.android.inputmethod.latin.settings.SettingsValues;
+
/**
* This class gathers audio feedback and haptic feedback functions.
*
@@ -86,40 +86,41 @@ public final class AudioAndHapticFeedbackManager {
if (mAudioManager == null) {
return;
}
- if (mSoundOn) {
- final int sound;
- switch (code) {
- case Constants.CODE_DELETE:
- sound = AudioManager.FX_KEYPRESS_DELETE;
- break;
- case Constants.CODE_ENTER:
- sound = AudioManager.FX_KEYPRESS_RETURN;
- break;
- case Constants.CODE_SPACE:
- sound = AudioManager.FX_KEYPRESS_SPACEBAR;
- break;
- default:
- sound = AudioManager.FX_KEYPRESS_STANDARD;
- break;
- }
- mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
+ if (!mSoundOn) {
+ return;
+ }
+ final int sound;
+ switch (code) {
+ case Constants.CODE_DELETE:
+ sound = AudioManager.FX_KEYPRESS_DELETE;
+ break;
+ case Constants.CODE_ENTER:
+ sound = AudioManager.FX_KEYPRESS_RETURN;
+ break;
+ case Constants.CODE_SPACE:
+ sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+ break;
+ default:
+ sound = AudioManager.FX_KEYPRESS_STANDARD;
+ break;
}
+ mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
}
public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
if (!mSettingsValues.mVibrateOn) {
return;
}
- if (mSettingsValues.mKeypressVibrationDuration < 0) {
- // Go ahead with the system default
- if (viewToPerformHapticFeedbackOn != null) {
- viewToPerformHapticFeedbackOn.performHapticFeedback(
- HapticFeedbackConstants.KEYBOARD_TAP,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
+ if (mSettingsValues.mKeypressVibrationDuration >= 0) {
+ vibrate(mSettingsValues.mKeypressVibrationDuration);
return;
}
- vibrate(mSettingsValues.mKeypressVibrationDuration);
+ // Go ahead with the system default
+ if (viewToPerformHapticFeedbackOn != null) {
+ viewToPerformHapticFeedbackOn.performHapticFeedback(
+ HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
}
public void onSettingsChanged(final SettingsValues settingsValues) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 7247a1f20..1d087439d 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -345,7 +345,7 @@ public final class BinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
return getFrequency(word) != NOT_A_PROBABILITY;
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index bc7276b9a..b55ed125f 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -85,11 +86,19 @@ public abstract class Dictionary {
final int sessionId, final float[] inOutLanguageWeight);
/**
- * Checks if the given word occurs in the dictionary
+ * Checks if the given word has to be treated as a valid word. Please note that some
+ * dictionaries have entries that should be treated as invalid words.
* @param word the word to search for. The search should be case-insensitive.
- * @return true if the word exists, false otherwise
+ * @return true if the word is valid, false otherwise
*/
- abstract public boolean isValidWord(final String word);
+ public boolean isValidWord(final String word) {
+ return isInDictionary(word);
+ }
+
+ /**
+ * Checks if the given word is in the dictionary regardless of it being valid or not.
+ */
+ abstract public boolean isInDictionary(final String word);
public int getFrequency(final String word) {
return NOT_A_PROBABILITY;
@@ -165,7 +174,7 @@ public abstract class Dictionary {
}
@Override
- public boolean isValidWord(String word) {
+ public boolean isInDictionary(String word) {
return false;
}
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 53c78fd00..89d61ce2a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -78,9 +78,9 @@ public final class DictionaryCollection extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
for (int i = mDictionaries.size() - 1; i >= 0; --i)
- if (mDictionaries.get(i).isValidWord(word)) return true;
+ if (mDictionaries.get(i).isInDictionary(word)) return true;
return false;
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index e8b0be069..bdf39238a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -30,6 +30,7 @@ import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.SuggestionResults;
@@ -571,16 +572,22 @@ public class DictionaryFacilitator {
final PersonalizationDataChunk personalizationDataChunk,
final SpacingAndPunctuations spacingAndPunctuations,
final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
+ final ExpandableBinaryDictionary personalizationDict =
+ mDictionaries.getSubDict(Dictionary.TYPE_PERSONALIZATION);
+ if (personalizationDict == null) {
+ if (callback != null) {
+ callback.onFinished();
+ }
+ return;
+ }
final ArrayList<LanguageModelParam> languageModelParams =
LanguageModelParam.createLanguageModelParamsFrom(
personalizationDataChunk.mTokens,
personalizationDataChunk.mTimestampInSeconds,
this /* dictionaryFacilitator */, spacingAndPunctuations,
- mDistracterFilter);
- final ExpandableBinaryDictionary personalizationDict =
- mDictionaries.getSubDict(Dictionary.TYPE_PERSONALIZATION);
- if (personalizationDict == null || languageModelParams == null
- || languageModelParams.isEmpty()) {
+ new DistracterFilterCheckingIsInDictionary(
+ mDistracterFilter, personalizationDict));
+ if (languageModelParams == null || languageModelParams.isEmpty()) {
if (callback != null) {
callback.onFinished();
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 0a5478c95..4dbfa0bac 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -49,6 +49,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* queries in native code. This binary dictionary is written to internal storage.
*/
abstract public class ExpandableBinaryDictionary extends Dictionary {
+ private static final boolean DEBUG = false;
/** Used for Log actions from this class */
private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName();
@@ -324,16 +325,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void addNgramEntryLocked(final PrevWordsInfo prevWordsInfo, final String word,
final int frequency, final int timestamp) {
if (!mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp)) {
- Log.e(TAG, "Cannot add n-gram entry.");
- Log.e(TAG, " PrevWordsInfo: " + prevWordsInfo);
- Log.e(TAG, " word: " + word);
+ if (DEBUG) {
+ Log.i(TAG, "Cannot add n-gram entry.");
+ Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word);
+ }
}
}
/**
* Dynamically remove the n-gram entry in the dictionary.
*/
- public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word1) {
+ public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word) {
reloadDictionaryIfRequired();
asyncExecuteTaskWithWriteLock(new Runnable() {
@Override
@@ -342,7 +344,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return;
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.removeNgramEntry(prevWordsInfo, word1);
+ if (!mBinaryDictionary.removeNgramEntry(prevWordsInfo, word)) {
+ if (DEBUG) {
+ Log.i(TAG, "Cannot remove n-gram entry.");
+ Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word);
+ }
+ }
}
});
}
@@ -413,7 +420,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
reloadDictionaryIfRequired();
boolean lockAcquired = false;
try {
@@ -423,10 +430,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (mBinaryDictionary == null) {
return false;
}
- return isValidWordLocked(word);
+ return isInDictionaryLocked(word);
}
} catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted tryLock() in isValidWord().", e);
+ Log.e(TAG, "Interrupted tryLock() in isInDictionary().", e);
} finally {
if (lockAcquired) {
mLock.readLock().unlock();
@@ -435,9 +442,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidWordLocked(final String word) {
+ protected boolean isInDictionaryLocked(final String word) {
if (mBinaryDictionary == null) return false;
- return mBinaryDictionary.isValidWord(word);
+ return mBinaryDictionary.isInDictionary(word);
}
@Override
@@ -589,20 +596,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
});
}
- // TODO: Implement BinaryDictionary.isInDictionary().
- @UsedForTesting
- public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
- mLock.readLock().lock();
- try {
- if (mBinaryDictionary != null && mDictType == Dictionary.TYPE_USER_HISTORY) {
- return mBinaryDictionary.isValidWord(word);
- }
- return false;
- } finally {
- mLock.readLock().unlock();
- }
- }
-
@UsedForTesting
public void waitAllTasksForTests() {
final CountDownLatch countDownLatch = new CountDownLatch(1);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 709f1334f..35966bb71 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -81,7 +81,7 @@ import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.DistracterFilterUsingSuggestion;
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
@@ -121,7 +121,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private final Settings mSettings;
private final DictionaryFacilitator mDictionaryFacilitator =
- new DictionaryFacilitator(new DistracterFilterUsingSuggestion(this /* context */));
+ new DictionaryFacilitator(new DistracterFilterCheckingExactMatches(this /* context */));
private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */,
this /* SuggestionStripViewAccessor */, mDictionaryFacilitator);
// We expect to have only one decoder in almost all cases, hence the default capacity of 1.
@@ -1374,34 +1374,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
callback.onGetSuggestedWords(SuggestedWords.EMPTY);
return;
}
- // Get the word on which we should search the bigrams. If we are composing a word, it's
- // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
- // should just skip whitespace if any, so 1.
final SettingsValues currentSettings = mSettings.getCurrent();
final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
-
- if (DEBUG) {
- if (mInputLogic.mWordComposer.isComposingWord()
- || mInputLogic.mWordComposer.isBatchMode()) {
- final PrevWordsInfo prevWordsInfo
- = mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion();
- // TODO: this is for checking consistency with older versions. Remove this when
- // we are confident this is stable.
- // We're checking the previous word in the text field against the memorized previous
- // word. If we are composing a word we should have the second word before the cursor
- // memorized, otherwise we should have the first.
- final PrevWordsInfo rereadPrevWordsInfo =
- mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
- currentSettings.mSpacingAndPunctuations,
- mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
- if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWordsInfo.mPrevWord)) {
- throw new RuntimeException("Unexpected previous word: "
- + prevWordsInfo.mPrevWord + " <> " + rereadPrevWordsInfo.mPrevWord);
- }
- }
- }
mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer,
- mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion(),
+ mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ currentSettings.mSpacingAndPunctuations,
+ // Get the word on which we should search the bigrams. If we are composing
+ // a word, it's whatever is *before* the half-committed word in the buffer,
+ // hence 2; if we aren't, we should just skip whitespace if any, so 1.
+ mInputLogic.mWordComposer.isComposingWord() ? 2 : 1),
keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive,
currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId,
sequenceNumber, callback);
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
index 7989346f4..e59ef7563 100644
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
@@ -66,10 +66,10 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
if (mLock.readLock().tryLock()) {
try {
- return mBinaryDictionary.isValidWord(word);
+ return mBinaryDictionary.isInDictionary(word);
} finally {
mLock.readLock().unlock();
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9da0f8451..1ba5d5ea6 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -110,7 +110,7 @@ public final class Suggest {
wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords,
additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);
- final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+ final boolean isOnlyFirstCharCapitalized = wordComposer.isOnlyFirstCharCapitalized();
// If resumed, then we don't want to upcase everything: resuming on a fully-capitalized
// words is rarely done to switch to another fully-capitalized word, but usually to a
// normal, non-capitalized suggestion.
@@ -122,7 +122,7 @@ public final class Suggest {
} else {
final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase,
- isFirstCharCapitalized, trailingSingleQuotesCount);
+ isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
firstSuggestion = firstSuggestedWordInfo.mWord;
if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
whitelistedWord = null;
@@ -142,7 +142,7 @@ public final class Suggest {
final boolean allowsToBeAutoCorrected = (null != whitelistedWord
&& !whitelistedWord.equals(typedWord))
|| (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord(
- consideredWord, wordComposer.isFirstCharCapitalized())
+ consideredWord, isOnlyFirstCharCapitalized)
&& !typedWord.equals(firstSuggestion));
final boolean hasAutoCorrection;
@@ -173,12 +173,12 @@ public final class Suggest {
final ArrayList<SuggestedWordInfo> suggestionsContainer =
new ArrayList<>(suggestionResults);
final int suggestionsCount = suggestionsContainer.size();
- if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
+ if (isOnlyFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
for (int i = 0; i < suggestionsCount; ++i) {
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
- wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
- trailingSingleQuotesCount);
+ wordInfo, suggestionResults.mLocale, isAllUpperCase,
+ isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
suggestionsContainer.set(i, transformedWordInfo);
}
}
@@ -292,11 +292,11 @@ public final class Suggest {
/* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
- final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
+ final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
if (isAllUpperCase) {
sb.append(wordInfo.mWord.toUpperCase(locale));
- } else if (isFirstCharCapitalized) {
+ } else if (isOnlyFirstCharCapitalized) {
sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale));
} else {
sb.append(wordInfo.mWord);
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 7a50d1a9d..6ce1f85c5 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -45,9 +45,6 @@ public final class WordComposer {
// The list of events that served to compose this string.
private final ArrayList<Event> mEvents;
private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
- // The information of previous words (before the composing word). Must not be null. Used as
- // context for suggestions.
- private PrevWordsInfo mPrevWordsInfo;
private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@@ -72,9 +69,9 @@ public final class WordComposer {
private int mCursorPositionWithinWord;
/**
- * Whether the user chose to capitalize the first char of the word.
+ * Whether the composing word has the only first char capitalized.
*/
- private boolean mIsFirstCharCapitalized;
+ private boolean mIsOnlyFirstCharCapitalized;
public WordComposer() {
mCombinerChain = new CombinerChain("");
@@ -84,7 +81,6 @@ public final class WordComposer {
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
refreshTypedWordCache();
}
@@ -111,12 +107,11 @@ public final class WordComposer {
mAutoCorrection = null;
mCapsCount = 0;
mDigitsCount = 0;
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
mIsResumed = false;
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
refreshTypedWordCache();
}
@@ -178,12 +173,6 @@ public final class WordComposer {
return mInputPointers;
}
- 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);
- }
-
/**
* Process an input event.
*
@@ -203,7 +192,7 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
// We may have deleted the last one.
if (0 == mCodePointSize) {
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
}
if (Constants.CODE_DELETE != event.mKeyCode) {
if (newIndex < MAX_WORD_LENGTH) {
@@ -215,8 +204,12 @@ public final class WordComposer {
mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
}
}
- mIsFirstCharCapitalized = isFirstCharCapitalized(
- newIndex, primaryCode, mIsFirstCharCapitalized);
+ if (0 == newIndex) {
+ mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode);
+ } else {
+ mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized
+ && !Character.isUpperCase(primaryCode);
+ }
if (Character.isUpperCase(primaryCode)) mCapsCount++;
if (Character.isDigit(primaryCode)) mDigitsCount++;
}
@@ -296,10 +289,8 @@ public final class WordComposer {
* This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
* @param codePoints the code points to set as the composing word.
* @param coordinates the x, y coordinates of the key in the CoordinateUtils format
- * @param prevWordsInfo the information of previous words, to use as context for suggestions
*/
- public void setComposingWord(final int[] codePoints, final int[] coordinates,
- final PrevWordsInfo prevWordsInfo) {
+ public void setComposingWord(final int[] codePoints, final int[] coordinates) {
reset();
final int length = codePoints.length;
for (int i = 0; i < length; ++i) {
@@ -308,7 +299,6 @@ public final class WordComposer {
CoordinateUtils.yFromArray(coordinates, i)));
}
mIsResumed = true;
- mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -319,16 +309,13 @@ public final class WordComposer {
return mTypedWordCache.toString();
}
- public PrevWordsInfo getPrevWordsInfoForSuggestion() {
- return mPrevWordsInfo;
- }
-
/**
- * Whether or not the user typed a capital letter as the first letter in the word
+ * Whether or not the user typed a capital letter as the first letter in the word, and no
+ * other letter is capitalized
* @return capitalization preference
*/
- public boolean isFirstCharCapitalized() {
- return mIsFirstCharCapitalized;
+ public boolean isOnlyFirstCharCapitalized() {
+ return mIsOnlyFirstCharCapitalized;
}
/**
@@ -364,7 +351,7 @@ public final class WordComposer {
}
/**
- * Saves the caps mode and the previous word at the start of composing.
+ * Saves the caps mode at the start of composing.
*
* WordComposer needs to know about the caps mode for several reasons. The first is, we need
* to know after the fact what the reason was, to register the correct form into the user
@@ -373,12 +360,9 @@ public final class WordComposer {
* Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions.
* @param mode the mode at the time of start
- * @param prevWordsInfo the information of previous words
*/
- public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
- final PrevWordsInfo prevWordsInfo) {
+ public void setCapitalizedModeAtStartComposingTime(final int mode) {
mCapitalizedMode = mode;
- mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -429,11 +413,10 @@ public final class WordComposer {
mCapsCount = 0;
mDigitsCount = 0;
mIsBatchMode = false;
- mPrevWordsInfo = new PrevWordsInfo(committedWord.toString());
mCombinerChain.reset();
mEvents.clear();
mCodePointSize = 0;
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
mCapitalizedMode = CAPS_MODE_OFF;
refreshTypedWordCache();
mAutoCorrection = null;
@@ -443,15 +426,7 @@ public final class WordComposer {
return lastComposedWord;
}
- // Call this when the recorded previous word should be discarded. This is typically called
- // when the user inputs a separator that's not whitespace (including the case of the
- // double-space-to-period feature).
- public void discardPreviousWordForSuggestion() {
- mPrevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
- }
-
- public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
- final PrevWordsInfo prevWordsInfo) {
+ public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mEvents.clear();
Collections.copy(mEvents, lastComposedWord.mEvents);
mInputPointers.set(lastComposedWord.mInputPointers);
@@ -462,7 +437,6 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
mRejectedBatchModeSuggestion = null;
mIsResumed = true;
- mPrevWordsInfo = prevWordsInfo;
}
public boolean isBatchMode() {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index c90dc90ce..24cc1ef0d 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -544,11 +544,8 @@ public final class InputLogic {
}
}
mConnection.endBatchEdit();
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
- // Prev word is 1st word before cursor
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(
+ getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
}
/* The sequence number member is only used in onUpdateBatchInput. It is increased each time
@@ -584,10 +581,8 @@ public final class InputLogic {
mSpaceState = SpaceState.PHANTOM;
keyboardSwitcher.requestUpdatingShiftState(
getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(settingsValues,
- keyboardSwitcher.getKeyboardShiftMode()),
- new PrevWordsInfo(commitParts[0]));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode(
+ settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
++mAutoCommitSequenceNumber;
}
}
@@ -724,15 +719,7 @@ public final class InputLogic {
mWordComposer.processEvent(inputTransaction.mEvent);
// If it's the first letter, make note of auto-caps state
if (mWordComposer.isSingleLetter()) {
- // We pass 2 to getPreviousWordForSuggestion when the previous code point is a word
- // connector. Otherwise, we pass 1 because we were not composing a word yet, so the
- // word we want is the 1st word before the cursor.
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- inputTransaction.mShiftState,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations,
- settingsValues.mSpacingAndPunctuations.isWordConnector(
- mConnection.getCodePointBeforeCursor()) ? 2 : 1));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
}
mConnection.setComposingText(getTextWithUnderline(
mWordComposer.getTypedWord()), 1);
@@ -924,10 +911,8 @@ public final class InputLogic {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
inputTransaction.setRequiresUpdateSuggestions();
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- WordComposer.CAPS_MODE_OFF,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- inputTransaction.mSettingsValues.mSpacingAndPunctuations, 1));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(
+ WordComposer.CAPS_MODE_OFF);
return;
}
} else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
@@ -1107,7 +1092,6 @@ public final class InputLogic {
final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations
.mSentenceSeparatorAndSpace;
mConnection.commitText(textToInsert, 1);
- mWordComposer.discardPreviousWordForSuggestion();
return true;
}
return false;
@@ -1267,10 +1251,7 @@ public final class InputLogic {
final int expectedCursorPosition = mConnection.getExpectedSelectionStart();
if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) {
// Show predictions.
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- WordComposer.CAPS_MODE_OFF,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations, 1));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF);
mLatinIME.mHandler.postUpdateSuggestionStrip();
return;
}
@@ -1322,7 +1303,7 @@ public final class InputLogic {
settingsValues.mSpacingAndPunctuations,
0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
mWordComposer.setCursorPositionWithinWord(
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
@@ -1450,7 +1431,7 @@ public final class InputLogic {
// with the typed word, so we need to resume suggestions right away.
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
mConnection.setComposingText(textToCommit, 1);
}
// Don't restart suggestion yet. We'll restart if the user deletes the separator.
@@ -1897,21 +1878,6 @@ public final class InputLogic {
// strings.
mLastComposedWord = mWordComposer.commitWord(commitType,
chosenWordWithSuggestions, separatorString, prevWordsInfo);
- final boolean shouldDiscardPreviousWordForSuggestion;
- if (0 == StringUtils.codePointCount(separatorString)) {
- // Separator is 0-length, we can keep the previous word for suggestion. Either this
- // was a manual pick or the language has no spaces in which case we want to keep the
- // previous word, or it was the keyboard closing or the cursor moving in which case it
- // will be reset anyway.
- shouldDiscardPreviousWordForSuggestion = false;
- } else {
- // Otherwise, we discard if the separator contains any non-whitespace.
- shouldDiscardPreviousWordForSuggestion =
- !StringUtils.containsOnlyWhitespace(separatorString);
- }
- if (shouldDiscardPreviousWordForSuggestion) {
- mWordComposer.discardPreviousWordForSuggestion();
- }
}
/**
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index be658ceff..1ba7b366f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -80,4 +80,10 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
/* package */ void runGCIfRequired() {
runGCIfRequired(false /* mindsBlockByGC */);
}
+
+ @Override
+ public boolean isValidWord(final String word) {
+ // Strings out of this dictionary should not be considered existing words.
+ return false;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 1423fceff..19fa29e5f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -38,10 +38,4 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar
final Locale locale, final File dictFile) {
return PersonalizationHelper.getPersonalizationDictionary(context, locale);
}
-
- @Override
- public boolean isValidWord(final String word) {
- // Strings out of this dictionary should not be considered existing words.
- return false;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 67ad54fb7..ea1035612 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -47,12 +47,6 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
return PersonalizationHelper.getUserHistoryDictionary(context, locale);
}
- @Override
- public boolean isValidWord(final String word) {
- // Strings out of this dictionary should not be considered existing words.
- return false;
- }
-
/**
* Add a word to the user history dictionary.
*
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
index cc80e6f21..55274cfe2 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
+import android.content.res.Resources;
import android.os.Binder;
import android.text.TextUtils;
import android.util.Log;
@@ -26,14 +27,18 @@ import android.view.textservice.TextInfo;
import com.android.inputmethod.latin.PrevWordsInfo;
import java.util.ArrayList;
+import java.util.Locale;
public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
private static final boolean DBG = false;
private final static String[] EMPTY_STRING_ARRAY = new String[0];
+ private final Resources mResources;
+ private SentenceLevelAdapter mSentenceLevelAdapter;
public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
super(service);
+ mResources = service.getResources();
}
private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
@@ -115,8 +120,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
@Override
public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit) {
- final SentenceSuggestionsInfo[] retval =
- super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
+ final SentenceSuggestionsInfo[] retval = splitAndSuggest(textInfos, suggestionsLimit);
if (retval == null || retval.length != textInfos.length) {
return retval;
}
@@ -130,6 +134,58 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
return retval;
}
+ /**
+ * Get sentence suggestions for specified texts in an array of TextInfo. This is taken from
+ * SpellCheckerService#onGetSentenceSuggestionsMultiple that we can't use because it's
+ * using private variables.
+ * The default implementation splits the input text to words and returns
+ * {@link SentenceSuggestionsInfo} which contains suggestions for each word.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfos an array of the text metadata
+ * @param suggestionsLimit the maximum number of suggestions to be returned
+ * @return an array of {@link SentenceSuggestionsInfo} returned by
+ * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
+ */
+ private SentenceSuggestionsInfo[] splitAndSuggest(TextInfo[] textInfos, int suggestionsLimit) {
+ if (textInfos == null || textInfos.length == 0) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ SentenceLevelAdapter sentenceLevelAdapter;
+ synchronized(this) {
+ sentenceLevelAdapter = mSentenceLevelAdapter;
+ if (sentenceLevelAdapter == null) {
+ final String localeStr = getLocale();
+ if (!TextUtils.isEmpty(localeStr)) {
+ sentenceLevelAdapter = new SentenceLevelAdapter(mResources,
+ new Locale(localeStr));
+ mSentenceLevelAdapter = sentenceLevelAdapter;
+ }
+ }
+ }
+ if (sentenceLevelAdapter == null) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ final int infosSize = textInfos.length;
+ final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
+ for (int i = 0; i < infosSize; ++i) {
+ final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
+ sentenceLevelAdapter.getSplitWords(textInfos[i]);
+ final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
+ textInfoParams.mItems;
+ final int itemsSize = mItems.size();
+ final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
+ for (int j = 0; j < itemsSize; ++j) {
+ splitTextInfos[j] = mItems.get(j).mTextInfo;
+ }
+ retval[i] = SentenceLevelAdapter.reconstructSuggestions(
+ textInfoParams, onGetSuggestionsMultiple(
+ splitTextInfos, suggestionsLimit, true));
+ }
+ return retval;
+ }
+
@Override
public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit, boolean sequentialWords) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index d7953e6e7..0032fcb88 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -323,7 +323,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
} else {
coordinates = dictInfo.mKeyboard.getCoordinates(codePoints);
}
- composer.setComposingWord(codePoints, coordinates, null /* previousWord */);
+ composer.setComposingWord(codePoints, coordinates);
// TODO: make a spell checker option to block offensive words or not
final ArrayList<SuggestedWordInfo> suggestions =
dictInfo.mDictionary.getSuggestions(composer, prevWordsInfo,
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 1331d52d5..cc52a3e0f 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -58,7 +58,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> {
return noSuggestions;
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(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/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
new file mode 100644
index 000000000..13352f39e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.spellcheck;
+
+import android.content.res.Resources;
+import android.view.textservice.SentenceSuggestionsInfo;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.RunInLocale;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * This code is mostly lifted directly from android.service.textservice.SpellCheckerService in
+ * the framework; maybe that should be protected instead, so that implementers don't have to
+ * rewrite everything for any small change.
+ */
+public class SentenceLevelAdapter {
+ public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
+ new SentenceSuggestionsInfo[] {};
+ private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
+ /**
+ * Container for split TextInfo parameters
+ */
+ public static class SentenceWordItem {
+ public final TextInfo mTextInfo;
+ public final int mStart;
+ public final int mLength;
+ public SentenceWordItem(TextInfo ti, int start, int end) {
+ mTextInfo = ti;
+ mStart = start;
+ mLength = end - start;
+ }
+ }
+
+ /**
+ * Container for originally queried TextInfo and parameters
+ */
+ public static class SentenceTextInfoParams {
+ final TextInfo mOriginalTextInfo;
+ final ArrayList<SentenceWordItem> mItems;
+ final int mSize;
+ public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
+ mOriginalTextInfo = ti;
+ mItems = items;
+ mSize = items.size();
+ }
+ }
+
+ private static class WordIterator {
+ private final SpacingAndPunctuations mSpacingAndPunctuations;
+ public WordIterator(final Resources res, final Locale locale) {
+ final RunInLocale<SpacingAndPunctuations> job
+ = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(final Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ mSpacingAndPunctuations = job.runInLocale(res, locale);
+ }
+
+ public int getEndOfWord(final CharSequence sequence, int index) {
+ final int length = sequence.length();
+ index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1);
+ while (index < length) {
+ final int codePoint = Character.codePointAt(sequence, index);
+ if (mSpacingAndPunctuations.isWordSeparator(codePoint)) {
+ // If it's a period, we want to stop here only if it's followed by another
+ // word separator. In all other cases we stop here.
+ if (Constants.CODE_PERIOD == codePoint) {
+ final int indexOfNextCodePoint =
+ index + Character.charCount(Constants.CODE_PERIOD);
+ if (indexOfNextCodePoint < length
+ && mSpacingAndPunctuations.isWordSeparator(
+ Character.codePointAt(sequence, indexOfNextCodePoint))) {
+ return index;
+ }
+ } else {
+ return index;
+ }
+ }
+ index += Character.charCount(codePoint);
+ }
+ return index;
+ }
+
+ public int getBeginningOfNextWord(final CharSequence sequence, int index) {
+ final int length = sequence.length();
+ if (index >= length) {
+ return -1;
+ }
+ index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1);
+ while (index < length) {
+ final int codePoint = Character.codePointAt(sequence, index);
+ if (!mSpacingAndPunctuations.isWordSeparator(codePoint)) {
+ return index;
+ }
+ index += Character.charCount(codePoint);
+ }
+ return -1;
+ }
+ }
+
+ private final WordIterator mWordIterator;
+ public SentenceLevelAdapter(final Resources res, final Locale locale) {
+ mWordIterator = new WordIterator(res, locale);
+ }
+
+ public SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
+ final WordIterator wordIterator = mWordIterator;
+ final CharSequence originalText = originalTextInfo.getText();
+ final int cookie = originalTextInfo.getCookie();
+ final int start = -1;
+ final int end = originalText.length();
+ final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
+ int wordStart = wordIterator.getBeginningOfNextWord(originalText, start);
+ int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
+ while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
+ if (wordEnd >= start && wordEnd > wordStart) {
+ final String query = originalText.subSequence(wordStart, wordEnd).toString();
+ final TextInfo ti = new TextInfo(query, cookie, query.hashCode());
+ wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
+ }
+ wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
+ if (wordStart == -1) {
+ break;
+ }
+ wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
+ }
+ return new SentenceTextInfoParams(originalTextInfo, wordItems);
+ }
+
+ public static SentenceSuggestionsInfo reconstructSuggestions(
+ SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
+ if (results == null || results.length == 0) {
+ return null;
+ }
+ if (originalTextInfoParams == null) {
+ return null;
+ }
+ final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
+ final int originalSequence =
+ originalTextInfoParams.mOriginalTextInfo.getSequence();
+
+ final int querySize = originalTextInfoParams.mSize;
+ final int[] offsets = new int[querySize];
+ final int[] lengths = new int[querySize];
+ final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
+ for (int i = 0; i < querySize; ++i) {
+ final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
+ SuggestionsInfo result = null;
+ for (int j = 0; j < results.length; ++j) {
+ final SuggestionsInfo cur = results[j];
+ if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
+ result = cur;
+ result.setCookieAndSequence(originalCookie, originalSequence);
+ break;
+ }
+ }
+ offsets[i] = item.mStart;
+ lengths[i] = item.mLength;
+ reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
+ }
+ return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
index 75075664f..a6437bac3 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -47,9 +47,9 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
synchronized (mLock) {
- return super.isValidWord(word);
+ return super.isInDictionary(word);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
index f2d981a9d..8c9d5d681 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
@@ -52,9 +52,9 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
synchronized (mLock) {
- return super.isValidWord(word);
+ return super.isInDictionary(word);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 79a735ad6..528d500d2 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -56,10 +56,18 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView {
super.setKeyboard(keyboard);
// With accessibility mode off, {@link #mAccessibilityDelegate} is set to null at the
// above {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call.
+ // With accessibility mode on, {@link #mAccessibilityDelegate} is set to a
+ // {@link MoreKeysKeyboardAccessibilityDelegate} object at the above
+ // {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call. And the object has to be
+ // overwritten by a {@link MoreSuggestionsAccessibilityDelegate} object here.
if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
- mAccessibilityDelegate = new MoreSuggestionsAccessibilityDelegate(this, mKeyDetector);
- mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_suggestions);
- mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_suggestions);
+ if (!(mAccessibilityDelegate instanceof MoreSuggestionsAccessibilityDelegate)) {
+ mAccessibilityDelegate = new MoreSuggestionsAccessibilityDelegate(
+ this, mKeyDetector);
+ mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_suggestions);
+ mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_suggestions);
+ }
+ mAccessibilityDelegate.setKeyboard(keyboard);
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 810bda758..19b48f081 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -379,10 +379,9 @@ final class SuggestionStripLayoutHelper {
} else {
wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
-
- // Disable this suggestion if the suggestion is null or empty.
- // TODO: Fix disabled {@link TextView}'s content description.
- wordView.setEnabled(!TextUtils.isEmpty(word));
+ // {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
+ // Use a simple {@link String} to avoid the issue.
+ wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
final float scaleX = getTextScaleX(word, width, wordView.getPaint());
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
@@ -461,14 +460,15 @@ final class SuggestionStripLayoutHelper {
}
final TextView wordView = mWordViews.get(positionInStrip);
- wordView.setEnabled(true);
- wordView.setTextColor(mColorAutoCorrect);
+ final String punctuation = punctuationSuggestions.getLabel(positionInStrip);
// {@link TextView#getTag()} is used to get the index in suggestedWords at
// {@link SuggestionStripView#onClick(View)}.
wordView.setTag(positionInStrip);
- wordView.setText(punctuationSuggestions.getLabel(positionInStrip));
+ wordView.setText(punctuation);
+ wordView.setContentDescription(punctuation);
wordView.setTextScaleX(1.0f);
wordView.setCompoundDrawables(null, null, null, null);
+ wordView.setTextColor(mColorAutoCorrect);
stripView.addView(wordView);
setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight);
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 97241498a..3be933ff7 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -163,7 +163,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
word.setOnLongClickListener(this);
mWordViews.add(word);
final View divider = inflater.inflate(R.layout.suggestion_divider, null);
- divider.setOnClickListener(this);
mDividerViews.add(divider);
final TextView info = new TextView(context, null, R.attr.suggestionWordStyle);
info.setTextColor(Color.WHITE);
@@ -429,6 +428,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public void onClick(final View view) {
+ AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
+ Constants.CODE_UNSPECIFIED, this);
if (view == mImportantNoticeStrip) {
mListener.showImportantNoticeContents();
return;
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java
index 8c3844ed8..0ee6236b1 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeUnit;
import android.content.Context;
import android.util.Log;
+import android.util.LruCache;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.latin.DictionaryFacilitator;
@@ -31,15 +32,16 @@ import com.android.inputmethod.latin.PrevWordsInfo;
* This class is used to prevent distracters being added to personalization
* or user history dictionaries
*/
-// TODO: Rename.
-public class DistracterFilterUsingSuggestion implements DistracterFilter {
- private static final String TAG = DistracterFilterUsingSuggestion.class.getSimpleName();
+public class DistracterFilterCheckingExactMatches implements DistracterFilter {
+ private static final String TAG = DistracterFilterCheckingExactMatches.class.getSimpleName();
private static final boolean DEBUG = false;
private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120;
+ private static final int MAX_DISTRACTERS_CACHE_SIZE = 512;
private final Context mContext;
private final DictionaryFacilitator mDictionaryFacilitator;
+ private final LruCache<String, Boolean> mDistractersCache;
private final Object mLock = new Object();
/**
@@ -47,9 +49,10 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter {
*
* @param context the context.
*/
- public DistracterFilterUsingSuggestion(final Context context) {
+ public DistracterFilterCheckingExactMatches(final Context context) {
mContext = context;
mDictionaryFacilitator = new DictionaryFacilitator();
+ mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE);
}
@Override
@@ -88,6 +91,7 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter {
synchronized (mLock) {
// Reset dictionaries for the locale.
try {
+ mDistractersCache.evictAll();
loadDictionariesForLocale(locale);
} catch (final InterruptedException e) {
Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter",
@@ -96,6 +100,15 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter {
}
}
}
+
+ final Boolean isCachedDistracter = mDistractersCache.get(testedWord);
+ if (isCachedDistracter != null && isCachedDistracter) {
+ if (DEBUG) {
+ Log.d(TAG, "testedWord: " + testedWord);
+ Log.d(TAG, "isDistracter: true (cache hit)");
+ }
+ return true;
+ }
// The tested word is a distracter when there is a word that is exact matched to the tested
// word and its probability is higher than the tested word's probability.
final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord);
@@ -107,6 +120,10 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter {
Log.d(TAG, "exactMatchFreq: " + exactMatchFreq);
Log.d(TAG, "isDistracter: " + isDistracter);
}
+ if (isDistracter) {
+ // Add the word to the cache.
+ mDistractersCache.put(testedWord, Boolean.TRUE);
+ }
return isDistracter;
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java
new file mode 100644
index 000000000..4ad4ba784
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import java.util.List;
+import java.util.Locale;
+
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.PrevWordsInfo;
+
+public class DistracterFilterCheckingIsInDictionary implements DistracterFilter {
+ private final DistracterFilter mDistracterFilter;
+ private final Dictionary mDictionary;
+
+ public DistracterFilterCheckingIsInDictionary(final DistracterFilter distracterFilter,
+ final Dictionary dictionary) {
+ mDistracterFilter = distracterFilter;
+ mDictionary = dictionary;
+ }
+
+ @Override
+ public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo,
+ String testedWord, Locale locale) {
+ if (mDictionary.isInDictionary(testedWord)) {
+ // This filter treats entries that are already in the dictionary as non-distracters
+ // because they have passed the filtering in the past.
+ return false;
+ } else {
+ return mDistracterFilter.isDistracterToWordsInDictionaries(
+ prevWordsInfo, testedWord, locale);
+ }
+ }
+
+ @Override
+ public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) {
+ // Do nothing.
+ }
+
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 4ed0f0a94..e4237a7f2 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -315,24 +315,6 @@ public final class StringUtils {
return true;
}
- /**
- * Returns true if all code points in text are whitespace, false otherwise. Empty is true.
- */
- // Interestingly enough, U+00A0 NO-BREAK SPACE and U+200B ZERO-WIDTH SPACE are not considered
- // whitespace, while EN SPACE, EM SPACE and IDEOGRAPHIC SPACES are.
- public static boolean containsOnlyWhitespace(final String text) {
- final int length = text.length();
- int i = 0;
- while (i < length) {
- final int codePoint = text.codePointAt(i);
- if (!Character.isWhitespace(codePoint)) {
- return false;
- }
- i += Character.charCount(codePoint);
- }
- return true;
- }
-
public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
final int[] sortedSeparators) {
boolean needsCapsNext = true;