diff options
Diffstat (limited to 'java/src')
6 files changed, 199 insertions, 68 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 84f2be585..b2b68f0ad 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -46,6 +46,7 @@ public class Key { * The key code (unicode or custom code) that this key generates. */ public final int mCode; + public final int mAltCode; /** Label to display */ public final CharSequence mLabel; @@ -108,7 +109,7 @@ public class Key { private final int mActionFlags; private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01; private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02; - private static final int ACTION_FLAGS_IGNORE_WHILE_TYPING = 0x04; + private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04; /** The current pressed state of this key */ private boolean mPressed; @@ -191,6 +192,7 @@ public class Key { mLabel = label; mOutputText = outputText; mCode = code; + mAltCode = Keyboard.CODE_DUMMY; mIcon = icon; // Horizontal gap is divided equally to both sides of the key. mX = x + mHorizontalGap / 2; @@ -290,6 +292,8 @@ public class Key { } else { mCode = Keyboard.CODE_DUMMY; } + mAltCode = style.getInt(keyAttr, + R.styleable.Keyboard_Key_altCode, Keyboard.CODE_DUMMY); keyAttr.recycle(); } @@ -334,8 +338,8 @@ public class Key { return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; } - public boolean ignoreWhileTyping() { - return (mActionFlags & ACTION_FLAGS_IGNORE_WHILE_TYPING) != 0; + public boolean altCodeWhileTyping() { + return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0; } public Typeface selectTypeface(Typeface defaultTypeface) { diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 4a28bf1de..b3b20ca21 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -24,6 +24,7 @@ import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.KeyboardShiftState; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -65,7 +66,6 @@ public class Keyboard { public static final int CODE_DIGIT0 = '0'; public static final int CODE_PLUS = '+'; - /** Special keys code. These should be aligned with values/keycodes.xml */ public static final int CODE_DUMMY = 0; public static final int CODE_SHIFT = -1; @@ -111,6 +111,7 @@ public class Keyboard { public final Map<Key, Drawable> mUnshiftedIcons; public final KeyboardIconsSet mIconsSet; + private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>(); private final KeyboardShiftState mShiftState = new KeyboardShiftState(); private final ProximityInfo mProximityInfo; @@ -145,6 +146,22 @@ public class Keyboard { return mProximityInfo; } + public Key getKey(int code) { + final Integer keyCode = code; + if (mKeyCache.containsKey(keyCode)) { + return mKeyCache.get(keyCode); + } + + for (final Key key : mKeys) { + if (key.mCode == code) { + mKeyCache.put(keyCode, key); + return key; + } + } + mKeyCache.put(keyCode, null); + return null; + } + public boolean hasShiftLockKey() { return !mShiftLockKeys.isEmpty(); } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index da60af3be..4e8765dcc 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -258,23 +258,27 @@ public class PointerTracker { // primaryCode is different from {@link Key#mCode}. private void callListenerOnCodeInput(Key key, int primaryCode, int[] keyCodes, int x, int y) { final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); - if (DEBUG_LISTENER) - Log.d(TAG, "onCodeInput: " + keyCodePrintable(primaryCode) + final boolean alterCode = key.altCodeWhileTyping() && mTimerProxy.isTyping(); + final int code = alterCode ? key.mAltCode : primaryCode; + // If code is CODE_DUMMY here, this key will be ignored or generate text. + final CharSequence text = (code != Keyboard.CODE_DUMMY) ? null : key.mOutputText; + if (DEBUG_LISTENER) { + Log.d(TAG, "onCodeInput: " + keyCodePrintable(code) + " text=" + text + " codes="+ Arrays.toString(keyCodes) + " x=" + x + " y=" + y - + " ignoreModifier=" + ignoreModifierKey); + + " ignoreModifier=" + ignoreModifierKey + " alterCode=" + alterCode); + } if (ignoreModifierKey) { return; } if (key.isEnabled()) { - mListener.onCodeInput(primaryCode, keyCodes, x, y); - } - } - - private void callListenerOnTextInput(Key key) { - if (DEBUG_LISTENER) - Log.d(TAG, "onTextInput: text=" + key.mOutputText); - if (key.isEnabled()) { - mListener.onTextInput(key.mOutputText); + if (code != Keyboard.CODE_DUMMY) { + mListener.onCodeInput(code, keyCodes, x, y); + } else if (text != null) { + mListener.onTextInput(text); + } + if (!key.altCodeWhileTyping() && !key.isModifier()) { + mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout); + } } } @@ -328,6 +332,23 @@ public class PointerTracker { if (key != null && key.isEnabled()) { key.onReleased(); mDrawingProxy.invalidateKey(key); + + if (key.isShift()) { + for (final Key shiftKey : mKeyboard.mShiftKeys) { + if (shiftKey != key) { + shiftKey.onReleased(); + mDrawingProxy.invalidateKey(shiftKey); + } + } + } + + if (key.altCodeWhileTyping()) { + final Key altKey = mKeyboard.getKey(key.mAltCode); + if (altKey != null) { + altKey.onReleased(); + mDrawingProxy.invalidateKey(altKey); + } + } } } @@ -338,6 +359,24 @@ public class PointerTracker { } key.onPressed(); mDrawingProxy.invalidateKey(key); + + if (key.isShift()) { + for (final Key shiftKey : mKeyboard.mShiftKeys) { + if (shiftKey != key) { + shiftKey.onPressed(); + mDrawingProxy.invalidateKey(shiftKey); + } + } + } + + if (key.altCodeWhileTyping() && mTimerProxy.isTyping()) { + final Key altKey = mKeyboard.getKey(key.mAltCode); + if (altKey != null) { + // TODO: Show altKey's preview. + altKey.onPressed(); + mDrawingProxy.invalidateKey(altKey); + } + } } } @@ -696,45 +735,27 @@ public class PointerTracker { callListenerOnCancelInput(); return; } - if (key.mOutputText != null) { - final boolean ignoreText = key.ignoreWhileTyping() && mTimerProxy.isTyping(); - if (!ignoreText) { - callListenerOnTextInput(key); - } - callListenerOnRelease(key, key.mCode, false); - if (!ignoreText) { - mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout); - } - } else { - int code = key.mCode; - final int[] codes = mKeyDetector.newCodeArray(); - mKeyDetector.getKeyAndNearbyCodes(x, y, codes); - - // If keyboard is in manual temporary upper case state and key has manual temporary - // uppercase letter as key hint letter, alternate character code should be sent. - if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) { - code = key.mHintLabel.charAt(0); - codes[0] = code; - } - // Swap the first and second values in the codes array if the primary code is not the - // first value but the second value in the array. This happens when key debouncing is - // in effect. - if (codes.length >= 2 && codes[0] != code && codes[1] == code) { - codes[1] = codes[0]; - codes[0] = code; - } - final boolean ignoreCode = key.ignoreWhileTyping() && mTimerProxy.isTyping(); - if (!ignoreCode) { - // TODO: It might be useful to register the nearest key code in codes[] instead of - // just ignoring. - callListenerOnCodeInput(key, code, codes, x, y); - } - callListenerOnRelease(key, code, false); - if (!key.ignoreWhileTyping() && !key.isModifier()) { - mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout); - } + int code = key.mCode; + final int[] codes = mKeyDetector.newCodeArray(); + mKeyDetector.getKeyAndNearbyCodes(x, y, codes); + + // If keyboard is in manual temporary upper case state and key has manual temporary + // uppercase letter as key hint letter, alternate character code should be sent. + if (mKeyboard.isManualTemporaryUpperCase() && key.hasUppercaseLetter()) { + code = key.mHintLabel.charAt(0); + codes[0] = code; + } + + // Swap the first and second values in the codes array if the primary code is not the + // first value but the second value in the array. This happens when key debouncing is + // in effect. + if (codes.length >= 2 && codes[0] != code && codes[1] == code) { + codes[1] = codes[0]; + codes[0] = code; } + callListenerOnCodeInput(key, code, codes, x, y); + callListenerOnRelease(key, code, false); } private long mPreviousEventTime; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 565edb901..218793500 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -152,6 +152,7 @@ public class KeyStyles { private void parseKeyStyleAttributes(TypedArray keyAttr) { // TODO: Currently not all Key attributes can be declared as style. readInt(keyAttr, R.styleable.Keyboard_Key_code); + readInt(keyAttr, R.styleable.Keyboard_Key_altCode); readText(keyAttr, R.styleable.Keyboard_Key_keyLabel); readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); readText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 739153044..c19a5a718 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -18,6 +18,8 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.ProximityInfo; +import android.util.Log; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -27,7 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * Class for a collection of dictionaries that behave like one dictionary. */ public class DictionaryCollection extends Dictionary { - + private final String TAG = DictionaryCollection.class.getSimpleName(); protected final List<Dictionary> mDictionaries; public DictionaryCollection() { @@ -75,7 +77,21 @@ public class DictionaryCollection extends Dictionary { dict.close(); } - public void addDictionary(Dictionary newDict) { - if (null != newDict) mDictionaries.add(newDict); + // Warning: this is not thread-safe. Take necessary precaution when calling. + public void addDictionary(final Dictionary newDict) { + if (null == newDict) return; + if (mDictionaries.contains(newDict)) { + Log.w(TAG, "This collection already contains this dictionary: " + newDict); + } + mDictionaries.add(newDict); + } + + // Warning: this is not thread-safe. Take necessary precaution when calling. + public void removeDictionary(final Dictionary dict) { + if (mDictionaries.contains(dict)) { + mDictionaries.remove(dict); + } else { + Log.w(TAG, "This collection does not contain this dictionary: " + dict); + } } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 93540ab12..9dfbe7a54 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -17,7 +17,9 @@ package com.android.inputmethod.latin.spellcheck; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Resources; +import android.preference.PreferenceManager; import android.service.textservice.SpellCheckerService; import android.text.TextUtils; import android.util.Log; @@ -41,21 +43,27 @@ import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WhitelistDictionary; import com.android.inputmethod.latin.WordComposer; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import java.util.TreeMap; +import java.util.HashSet; /** * Service for spell checking, using LatinIME's dictionaries and mechanisms. */ -public class AndroidSpellCheckerService extends SpellCheckerService { +public class AndroidSpellCheckerService extends SpellCheckerService + implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = AndroidSpellCheckerService.class.getSimpleName(); private static final boolean DBG = false; private static final int POOL_SIZE = 2; + public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts"; + private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case private static final int CAPITALIZE_FIRST = 1; // First only private static final int CAPITALIZE_ALL = 2; // All caps @@ -84,6 +92,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private double mSuggestionThreshold; // The threshold for a suggestion to be considered "recommended". private double mRecommendedThreshold; + // Whether to use the contacts dictionary + private boolean mUseContactsDictionary; + private final Object mUseContactsLock = new Object(); + + private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList = + new HashSet<WeakReference<DictionaryCollection>>(); @Override public void onCreate() { super.onCreate(); @@ -91,6 +105,57 @@ public class AndroidSpellCheckerService extends SpellCheckerService { Double.parseDouble(getString(R.string.spellchecker_suggestion_threshold_value)); mRecommendedThreshold = Double.parseDouble(getString(R.string.spellchecker_recommended_threshold_value)); + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + prefs.registerOnSharedPreferenceChangeListener(this); + onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { + if (!PREF_USE_CONTACTS_KEY.equals(key)) return; + synchronized(mUseContactsLock) { + mUseContactsDictionary = prefs.getBoolean(PREF_USE_CONTACTS_KEY, true); + if (mUseContactsDictionary) { + startUsingContactsDictionaryLocked(); + } else { + stopUsingContactsDictionaryLocked(); + } + } + } + + private void startUsingContactsDictionaryLocked() { + if (null == mContactsDictionary) { + mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); + } + final Iterator<WeakReference<DictionaryCollection>> iterator = + mDictionaryCollectionsList.iterator(); + while (iterator.hasNext()) { + final WeakReference<DictionaryCollection> dictRef = iterator.next(); + final DictionaryCollection dict = dictRef.get(); + if (null == dict) { + iterator.remove(); + } else { + dict.addDictionary(mContactsDictionary); + } + } + } + + private void stopUsingContactsDictionaryLocked() { + if (null == mContactsDictionary) return; + final SynchronouslyLoadedContactsDictionary contactsDict = mContactsDictionary; + mContactsDictionary = null; + final Iterator<WeakReference<DictionaryCollection>> iterator = + mDictionaryCollectionsList.iterator(); + while (iterator.hasNext()) { + final WeakReference<DictionaryCollection> dictRef = iterator.next(); + final DictionaryCollection dict = dictRef.get(); + if (null == dict) { + iterator.remove(); + } else { + dict.removeDictionary(contactsDict); + } + } + contactsDict.close(); } @Override @@ -274,13 +339,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService { for (Dictionary dict : oldWhitelistDictionaries.values()) { dict.close(); } - if (null != mContactsDictionary) { - // The synchronously loaded contacts dictionary should have been in one - // or several pools, but it is shielded against multiple closing and it's - // safe to call it several times. - final SynchronouslyLoadedContactsDictionary dictToClose = mContactsDictionary; - mContactsDictionary = null; - dictToClose.close(); + synchronized(mUseContactsLock) { + if (null != mContactsDictionary) { + // The synchronously loaded contacts dictionary should have been in one + // or several pools, but it is shielded against multiple closing and it's + // safe to call it several times. + final SynchronouslyLoadedContactsDictionary dictToClose = mContactsDictionary; + mContactsDictionary = null; + dictToClose.close(); + } } return false; } @@ -315,11 +382,16 @@ public class AndroidSpellCheckerService extends SpellCheckerService { mWhitelistDictionaries.put(localeStr, whitelistDictionary); } dictionaryCollection.addDictionary(whitelistDictionary); - if (null == mContactsDictionary) { - mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); + synchronized(mUseContactsLock) { + if (mUseContactsDictionary) { + if (null == mContactsDictionary) { + mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); + } + } + dictionaryCollection.addDictionary(mContactsDictionary); + mDictionaryCollectionsList.add( + new WeakReference<DictionaryCollection>(dictionaryCollection)); } - // TODO: add a setting to use or not contacts when checking spelling - dictionaryCollection.addDictionary(mContactsDictionary); return new DictAndProximity(dictionaryCollection, proximityInfo); } |