aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-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/latin/DictionaryFacilitator.java9
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java18
4 files changed, 112 insertions, 53 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/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 48b6a4622..bdf39238a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -574,6 +574,12 @@ public class DictionaryFacilitator {
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,
@@ -581,8 +587,7 @@ public class DictionaryFacilitator {
this /* dictionaryFacilitator */, spacingAndPunctuations,
new DistracterFilterCheckingIsInDictionary(
mDistracterFilter, personalizationDict));
- if (personalizationDict == null || languageModelParams == null
- || languageModelParams.isEmpty()) {
+ if (languageModelParams == null || languageModelParams.isEmpty()) {
if (callback != null) {
callback.onFinished();
}
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java
index 1f1475a53..0ee6236b1 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.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;
@@ -36,9 +37,11 @@ public class DistracterFilterCheckingExactMatches implements DistracterFilter {
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();
/**
@@ -49,6 +52,7 @@ public class DistracterFilterCheckingExactMatches implements DistracterFilter {
public DistracterFilterCheckingExactMatches(final Context context) {
mContext = context;
mDictionaryFacilitator = new DictionaryFacilitator();
+ mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE);
}
@Override
@@ -87,6 +91,7 @@ public class DistracterFilterCheckingExactMatches 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",
@@ -95,6 +100,15 @@ public class DistracterFilterCheckingExactMatches 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);
@@ -106,6 +120,10 @@ public class DistracterFilterCheckingExactMatches 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;
}
}