diff options
Diffstat (limited to 'java')
9 files changed, 156 insertions, 29 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index c2200b5ad..172ca2f25 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -107,7 +107,6 @@ </declare-styleable> <declare-styleable name="CandidateView"> - <attr name="autoCorrectionVisualFlashEnabled" format="boolean" /> <attr name="autoCorrectHighlight" format="integer"> <flag name="autoCorrectBold" value="0x01" /> <flag name="autoCorrectUnderline" value="0x02" /> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 6327ede38..1c7c1a172 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -39,7 +39,6 @@ <bool name="config_default_compat_recorrection_enabled">true</bool> <bool name="config_default_sound_enabled">false</bool> <bool name="config_auto_correction_spacebar_led_enabled">true</bool> - <bool name="config_auto_correction_suggestion_strip_visual_flash_enabled">false</bool> <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false --> <bool name="config_show_mini_keyboard_at_touched_point">false</bool> <!-- The language is never displayed if == 0, always displayed if < 0 --> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index a47eeed12..8145d0510 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -85,7 +85,6 @@ <item name="android:background">@drawable/candidate_feedback_background</item> </style> <style name="CandidateViewStyle" parent="SuggestionsStripBackgroundStyle"> - <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item> <item name="autoCorrectHighlight">autoCorrectBold</item> <item name="colorTypedWord">#FFFFFFFF</item> <item name="colorAutoCorrect">#FFFCAE00</item> @@ -189,8 +188,7 @@ <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item> </style> <style name="CandidateViewStyle.IceCreamSandwich" parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"> - <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item> - <item name="autoCorrectHighlight">autoCorrectBold|autoCorrectInvert</item> + <item name="autoCorrectHighlight">autoCorrectBold</item> <item name="colorTypedWord">#FFFFFFFF</item> <item name="colorAutoCorrect">#FF3DC8FF</item> <item name="colorSuggested">#FFFFFFFF</item> diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index ae9809e56..3dca9aae6 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -20,12 +20,18 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Paint; +import android.inputmethodservice.InputMethodService; +import android.media.AudioManager; import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; +import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.AccessibilityEventCompatUtils; +import com.android.inputmethod.compat.AudioManagerCompatWrapper; +import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; @@ -39,17 +45,19 @@ public class AccessibleKeyboardViewProxy { // Delay in milliseconds between key press DOWN and UP events private static final long DELAY_KEY_PRESS = 10; - private int mScaledEdgeSlop; + private InputMethodService mInputMethod; + private FlickGestureDetector mGestureDetector; private LatinKeyboardBaseView mView; private AccessibleKeyboardActionListener mListener; - private FlickGestureDetector mGestureDetector; + private AudioManagerCompatWrapper mAudioManager; + private int mScaledEdgeSlop; private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; private int mLastX = -1; private int mLastY = -1; - public static void init(Context context, SharedPreferences prefs) { - sInstance.initInternal(context, prefs); + public static void init(InputMethodService inputMethod, SharedPreferences prefs) { + sInstance.initInternal(inputMethod, prefs); sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance(); } @@ -65,15 +73,36 @@ public class AccessibleKeyboardViewProxy { // Not publicly instantiable. } - private void initInternal(Context context, SharedPreferences prefs) { + private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) { final Paint paint = new Paint(); paint.setTextAlign(Paint.Align.LEFT); paint.setTextSize(14.0f); paint.setAntiAlias(true); paint.setColor(Color.YELLOW); - mGestureDetector = new KeyboardFlickGestureDetector(context); - mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop(); + mInputMethod = inputMethod; + mGestureDetector = new KeyboardFlickGestureDetector(inputMethod); + mScaledEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop(); + + final AudioManager audioManager = (AudioManager) inputMethod + .getSystemService(Context.AUDIO_SERVICE); + mAudioManager = new AudioManagerCompatWrapper(audioManager); + } + + /** + * @return {@code true} if the device should not speak text (eg. non-control) characters + */ + private boolean shouldObscureInput() { + // Always speak if the user is listening through headphones. + if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) + return false; + + final EditorInfo info = mInputMethod.getCurrentInputEditorInfo(); + if (info == null) + return false; + + // Don't speak if the IME is connected to a password field. + return InputTypeCompatUtils.isPasswordInputType(info.inputType); } public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, @@ -90,8 +119,10 @@ public class AccessibleKeyboardViewProxy { if (key == null) break; + final boolean shouldObscure = shouldObscureInput(); final CharSequence description = KeyCodeDescriptionMapper.getInstance() - .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key); + .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key, + shouldObscure); if (description == null) return false; diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index ec4287dda..7302830d4 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -28,6 +28,9 @@ import com.android.inputmethod.latin.R; import java.util.HashMap; public class KeyCodeDescriptionMapper { + // The resource ID of the string spoken for obscured keys + private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot; + private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); // Map of key labels to spoken description resource IDs @@ -118,10 +121,12 @@ public class KeyCodeDescriptionMapper { * @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 */ - public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key) { + public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key, + boolean shouldObscure) { if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard); if (description != null) @@ -136,12 +141,12 @@ public class KeyCodeDescriptionMapper { } else if (label.length() == 1 || (keyboard.isManualTemporaryUpperCase() && !TextUtils .isEmpty(key.mHintLabel))) { - return getDescriptionForKeyCode(context, keyboard, key); + return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); } else { return label; } } else if (key.mCode != Keyboard.CODE_DUMMY) { - return getDescriptionForKeyCode(context, keyboard, key); + return getDescriptionForKeyCode(context, keyboard, key, shouldObscure); } return null; @@ -206,19 +211,29 @@ public class KeyCodeDescriptionMapper { * @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 */ - private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key) { + private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key, + boolean shouldObscure) { final int code = getCorrectKeyCode(keyboard, key); if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) { return context.getString(mShiftLockedKeyCodeMap.get(code)); } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) { return context.getString(mShiftedKeyCodeMap.get(code)); - } else if (mKeyCodeMap.containsKey(code)) { + } + + // 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); + } + + if (mKeyCodeMap.containsKey(code)) { return context.getString(mKeyCodeMap.get(code)); - } else if (Character.isDefined(code) && !Character.isISOControl(code)) { + } else if (isDefinedNonCtrl) { return Character.toString((char) code); } else { return context.getString(R.string.spoken_description_unknown, code); diff --git a/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java new file mode 100644 index 000000000..b6c3e2a88 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 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.compat; + +import android.media.AudioManager; + +import java.lang.reflect.Method; + +public class AudioManagerCompatWrapper { + private static final Method METHOD_isWiredHeadsetOn = CompatUtils.getMethod( + AudioManager.class, "isWiredHeadsetOn"); + private static final Method METHOD_isBluetoothA2dpOn = CompatUtils.getMethod( + AudioManager.class, "isBluetoothA2dpOn"); + + private final AudioManager mManager; + + public AudioManagerCompatWrapper(AudioManager manager) { + mManager = manager; + } + + /** + * Checks whether audio routing to the wired headset is on or off. + * + * @return true if audio is being routed to/from wired headset; + * false if otherwise + */ + public boolean isWiredHeadsetOn() { + return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isWiredHeadsetOn); + } + + /** + * Checks whether A2DP audio routing to the Bluetooth headset is on or off. + * + * @return true if A2DP audio is being routed to/from Bluetooth headset; + * false if otherwise + */ + public boolean isBluetoothA2dpOn() { + return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isBluetoothA2dpOn); + } +} diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 0640fd0b1..f499bc0bb 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -278,7 +278,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); - public final boolean mAutoCorrectionVisualFlashEnabled; public boolean mMoreSuggestionsAvailable; public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle, @@ -286,8 +285,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo super(words, dividers, infos); final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); - mAutoCorrectionVisualFlashEnabled = a.getBoolean( - R.styleable.CandidateView_autoCorrectionVisualFlashEnabled, false); mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); @@ -584,7 +581,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo final int width = getWidth(); final int countInStrip = mStripParams.layout( mSuggestions, mCandidatesStrip, mCandidatesPane, width); - final int countInPane = mPaneParams.layout( + mPaneParams.layout( mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width); } @@ -633,6 +630,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private static CharSequence getEllipsizedText(CharSequence text, int maxWidth, TextPaint paint) { + if (text == null) return null; paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); if (width <= maxWidth) { @@ -703,9 +701,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { - if (!mStripParams.mAutoCorrectionVisualFlashEnabled) { - return; - } final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord); if (inverted == null) return; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index c71841042..649774d78 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin.spellcheck; +import android.content.Intent; import android.content.res.Resources; import android.service.textservice.SpellCheckerService; import android.service.textservice.SpellCheckerService.Session; @@ -48,7 +49,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private static final int POOL_SIZE = 2; private final static String[] emptyArray = new String[0]; - private final Map<String, DictionaryPool> mDictionaryPools = + private Map<String, DictionaryPool> mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); @Override @@ -104,6 +105,16 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } } + @Override + public boolean onUnbind(final Intent intent) { + final Map<String, DictionaryPool> oldPools = mDictionaryPools; + mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); + for (DictionaryPool pool : oldPools.values()) { + pool.close(); + } + return false; + } + private DictionaryPool getDictionaryPool(final String locale) { DictionaryPool pool = mDictionaryPools.get(locale); if (null == pool) { @@ -167,7 +178,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService { dictInfo.mDictionary.getWords(composer, suggestionsGatherer, dictInfo.mProximityInfo); isInDict = dictInfo.mDictionary.isValidWord(text); - mDictionaryPool.offer(dictInfo); + if (!mDictionaryPool.offer(dictInfo)) { + Log.e(TAG, "Can't re-insert a dictionary into its pool"); + } } catch (InterruptedException e) { // I don't think this can happen. return new SuggestionsInfo(0, new String[0]); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index dfbfcc7f6..ee294f6b0 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -28,7 +28,8 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { private final AndroidSpellCheckerService mService; private final int mMaxSize; private final Locale mLocale; - private int mSize = 0; + private int mSize; + private volatile boolean mClosed; public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service, final Locale locale) { @@ -36,6 +37,8 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { mMaxSize = maxSize; mService = service; mLocale = locale; + mSize = 0; + mClosed = false; } @Override @@ -52,4 +55,24 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> { } } } + + public void close() { + synchronized(this) { + mClosed = true; + for (DictAndProximity dict : this) { + dict.mDictionary.close(); + } + clear(); + } + } + + @Override + public boolean offer(final DictAndProximity dict) { + if (mClosed) { + dict.mDictionary.close(); + return false; + } else { + return super.offer(dict); + } + } } |