diff options
Diffstat (limited to 'java/src')
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; } } |