diff options
Diffstat (limited to 'java/src')
13 files changed, 306 insertions, 119 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 4c109c708..cef82267f 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -23,7 +23,6 @@ import android.graphics.Paint; import android.inputmethodservice.InputMethodService; import android.util.Log; import android.view.MotionEvent; -import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; @@ -38,18 +37,12 @@ public class AccessibleKeyboardViewProxy { private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName(); private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy(); - // Delay in milliseconds between key press DOWN and UP events - private static final long DELAY_KEY_PRESS = 10; - private InputMethodService mInputMethod; private FlickGestureDetector mGestureDetector; private LatinKeyboardView mView; private AccessibleKeyboardActionListener mListener; - private int mScaledEdgeSlop; private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; - private int mLastX = -1; - private int mLastY = -1; public static void init(InputMethodService inputMethod, SharedPreferences prefs) { sInstance.initInternal(inputMethod, prefs); @@ -77,7 +70,6 @@ public class AccessibleKeyboardViewProxy { mInputMethod = inputMethod; mGestureDetector = new KeyboardFlickGestureDetector(inputMethod); - mScaledEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop(); } public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event, @@ -143,27 +135,10 @@ public class AccessibleKeyboardViewProxy { if (keyIndex != mLastHoverKeyIndex) { fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); mLastHoverKeyIndex = keyIndex; - mLastX = x; - mLastY = y; fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true); } return true; - case MotionEventCompatUtils.ACTION_HOVER_EXIT: - final int width = mView.getWidth(); - final int height = mView.getHeight(); - - if (x < mScaledEdgeSlop || y < mScaledEdgeSlop || x >= (width - mScaledEdgeSlop) - || y >= (height - mScaledEdgeSlop)) { - fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false); - mLastHoverKeyIndex = KeyDetector.NOT_A_KEY; - mLastX = -1; - mLastY = -1; - } else if (mLastHoverKeyIndex != KeyDetector.NOT_A_KEY) { - fireKeyPressEvent(tracker, mLastX, mLastY, event.getEventTime()); - } - - return true; } return false; @@ -197,11 +172,6 @@ public class AccessibleKeyboardViewProxy { } } - private void fireKeyPressEvent(PointerTracker tracker, int x, int y, long eventTime) { - tracker.onDownEvent(x, y, eventTime, mView); - tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS); - } - private class KeyboardFlickGestureDetector extends FlickGestureDetector { public KeyboardFlickGestureDetector(Context context) { super(context); diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 2766cc3f1..164d2b748 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -16,6 +16,7 @@ package com.android.inputmethod.compat; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; @@ -27,6 +28,7 @@ import android.text.TextUtils; import android.util.Log; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Locale; @@ -38,9 +40,6 @@ public class SuggestionSpanUtils { public static final String SUGGESTION_SPAN_PICKED_AFTER = "after"; public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before"; public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode"; - // TODO: Use the API constant after it gets public. - public static final int FLAG_AUTO_CORRECTION = 0x0004; - public static final int SUGGESTION_MAX_SIZE = 5; public static final boolean SUGGESTION_SPAN_IS_SUPPORTED; private static final Class<?> CLASS_SuggestionSpan = CompatUtils @@ -49,20 +48,36 @@ public class SuggestionSpanUtils { Context.class, Locale.class, String[].class, int.class, Class.class }; private static final Constructor<?> CONSTRUCTOR_SuggestionSpan = CompatUtils .getConstructor(CLASS_SuggestionSpan, INPUT_TYPE_SuggestionSpan); + public static final Field FIELD_FLAG_AUTO_CORRECTION + = CompatUtils.getField(CLASS_SuggestionSpan, "FLAG_AUTO_CORRECTION"); + public static final Field FIELD_SUGGESTION_MAX_SIZE + = CompatUtils.getField(CLASS_SuggestionSpan, "SUGGESTION_MAX_SIZE"); + public static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils + .getFieldValue(null, null, FIELD_FLAG_AUTO_CORRECTION);; + public static final Integer OBJ_SUGGESTION_MAX_SIZE = (Integer) CompatUtils + .getFieldValue(null, null, FIELD_SUGGESTION_MAX_SIZE);; + static { SUGGESTION_SPAN_IS_SUPPORTED = CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null; + if (LatinImeLogger.sDBG) { + if (SUGGESTION_SPAN_IS_SUPPORTED + && (OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTION_MAX_SIZE == null)) { + Log.e(TAG, "Field is accidentially null."); + } + } } public static CharSequence getTextWithAutoCorrectionIndicatorUnderline( Context context, CharSequence text) { - if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null) { + if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null + || OBJ_FLAG_AUTO_CORRECTION == null) { return text; } final Spannable spannable = text instanceof Spannable ? (Spannable) text : new SpannableString(text); final Object[] args = - { context, null, new String[] {}, FLAG_AUTO_CORRECTION, + { context, null, new String[] {}, (int)OBJ_FLAG_AUTO_CORRECTION, (Class<?>) SuggestionSpanPickedNotificationReceiver.class }; final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args); if (ss == null) { @@ -78,7 +93,8 @@ public class SuggestionSpanUtils { CharSequence pickedWord, SuggestedWords suggestedWords) { if (TextUtils.isEmpty(pickedWord) || CONSTRUCTOR_SuggestionSpan == null || suggestedWords == null || suggestedWords.size() == 0 - || suggestedWords.getInfo(0).isObsoleteSuggestedWord()) { + || suggestedWords.getInfo(0).isObsoleteSuggestedWord() + || OBJ_SUGGESTION_MAX_SIZE == null) { return pickedWord; } @@ -90,7 +106,7 @@ public class SuggestionSpanUtils { } final ArrayList<String> suggestionsList = new ArrayList<String>(); for (int i = 0; i < suggestedWords.size(); ++i) { - if (suggestionsList.size() >= SUGGESTION_MAX_SIZE) { + if (suggestionsList.size() >= OBJ_SUGGESTION_MAX_SIZE) { break; } final CharSequence word = suggestedWords.getWord(i); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index c51f1849b..762039625 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -38,7 +38,6 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; -import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; @@ -329,10 +328,12 @@ public class LatinKeyboard extends Keyboard { Math.max(0, Math.min(y, mOccupiedHeight - 1))); } + private static final int[] ATTR_TEXT_SIZE = { android.R.attr.textSize }; + public static int getTextSizeFromTheme(Theme theme, int style, int defValue) { - TypedArray array = theme.obtainStyledAttributes( - style, new int[] { android.R.attr.textSize }); - int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); + final TypedArray a = theme.obtainStyledAttributes(style, ATTR_TEXT_SIZE); + final int textSize = a.getDimensionPixelSize(a.getResourceId(0, 0), defValue); + a.recycle(); return textSize; } } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index a24195e87..aab52e139 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -624,16 +624,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } @Override - public boolean dispatchTouchEvent(MotionEvent event) { - // Drop non-hover touch events when touch exploration is enabled. - if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { - return false; - } - - return super.dispatchTouchEvent(event); - } - - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { final PointerTracker tracker = getPointerTracker(0); diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java index 0e6e129bb..f2c5b7b49 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java @@ -27,7 +27,6 @@ import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.latin.R; - /** * A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting * key presses and touch movements. @@ -115,7 +114,7 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { @Override public DrawingProxy getDrawingProxy() { - return this; + return this; } @Override @@ -174,9 +173,15 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { return x; } + private boolean mIsDismissing; + @Override public boolean dismissMoreKeysPanel() { - return mController.dismissMoreKeysPanel(); + if (mIsDismissing) return false; + mIsDismissing = true; + final boolean dismissed = mController.dismissMoreKeysPanel(); + mIsDismissing = false; + return dismissed; } @Override diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index dd31d17c8..de64639b0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; @@ -168,7 +169,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { keyboardAttr.recycle(); TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); - mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr, + mDefaultKeyWidth = KeyboardBuilder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth, params.mDefaultKeyWidth); keyAttr.recycle(); @@ -268,14 +269,17 @@ public class KeyboardBuilder<KP extends KeyboardParams> { public KeyboardBuilder<KP> load(KeyboardId id) { mParams.mId = id; + final XmlResourceParser parser = mResources.getXml(id.getXmlId()); try { - parseKeyboard(id.getXmlId()); + parseKeyboard(parser); } catch (XmlPullParserException e) { Log.w(TAG, "keyboard XML parse error: " + e); throw new IllegalArgumentException(e); } catch (IOException e) { Log.w(TAG, "keyboard XML parse error: " + e); throw new RuntimeException(e); + } finally { + parser.close(); } return this; } @@ -288,9 +292,9 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return new Keyboard(mParams); } - private void parseKeyboard(int resId) throws XmlPullParserException, IOException { + private void parseKeyboard(XmlResourceParser parser) + throws XmlPullParserException, IOException { if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); - final XmlPullParser parser = mResources.getXml(resId); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { @@ -319,7 +323,10 @@ public class KeyboardBuilder<KP extends KeyboardParams> { if (TAG_KEYBOARD.equals(tag)) { final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); - return keyboardAttr.getString(R.styleable.Keyboard_keyboardLocale); + final String locale = keyboardAttr.getString( + R.styleable.Keyboard_keyboardLocale); + keyboardAttr.recycle(); + return locale; } else { throw new IllegalStartTag(parser, TAG_KEYBOARD); } @@ -535,7 +542,12 @@ public class KeyboardBuilder<KP extends KeyboardParams> { throw new ParseException("No keyboardLayout attribute in <include/>", parser); if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); - parseMerge(mResources.getLayout(keyboardLayout), row, skip); + final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout); + try { + parseMerge(parserForInclude, row, skip); + } finally { + parserForInclude.close(); + } } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 05aa305f5..d57154ad5 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -28,7 +28,6 @@ import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.media.AudioManager; import android.net.ConnectivityManager; -import android.os.Build; import android.os.Debug; import android.os.Message; import android.os.SystemClock; @@ -182,7 +181,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // TODO: Create an inner class to group options and pseudo-options to improve readability. // These variables are initialized according to the {@link EditorInfo#inputType}. - private boolean mShouldInsertMagicSpace; + private boolean mInsertSpaceOnPickSuggestionManually; private boolean mInputTypeNoAutoCorrect; private boolean mIsSettingsSuggestionStripOn; private boolean mApplicationSpecifiedCompletionOn; @@ -373,6 +372,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private boolean mHasPendingFinishInput; public void startOrientationChanging() { + removeMessages(MSG_PENDING_IMS_CALLBACK); + resetPendingImsCallback(); mIsOrientationChanging = true; final LatinIME latinIme = getOuterInstance(); latinIme.mKeyboardSwitcher.saveKeyboardState(); @@ -718,6 +719,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType) || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)); + // The EditorInfo might have a flag that affects fullscreen mode. + // Note: This call should be done by InputMethodService? + updateFullscreenMode(); initializeInputAttributes(attribute); inputView.closing(); @@ -734,7 +738,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) { mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); - } + } mVoiceProxy.loadSettings(attribute, mPrefs); // This will work only when the subtype is not supported. LanguageSwitcherProxy.loadSettings(); @@ -745,8 +749,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mSuggestionsView != null) mSuggestionsView.clear(); - // The EditorInfo might have a flag that affects fullscreen mode. - updateFullscreenMode(); setSuggestionStripShownInternal( isSuggestionsStripVisible(), /* needsInputViewShown */ false); // Delay updating suggestions because keyboard input view may not be shown at this point. @@ -776,7 +778,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar inputType, attribute.imeOptions)); } - mShouldInsertMagicSpace = false; + mInsertSpaceOnPickSuggestionManually = false; mInputTypeNoAutoCorrect = false; mIsSettingsSuggestionStripOn = false; mApplicationSpecifiedCompletionOn = false; @@ -791,9 +793,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } if (InputTypeCompatUtils.isEmailVariation(variation) || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { - mShouldInsertMagicSpace = false; + // The point in turning this off is that we don't want to insert a space after + // a name when filling a form: we can't delete trailing spaces when changing fields + mInsertSpaceOnPickSuggestionManually = false; } else { - mShouldInsertMagicSpace = true; + mInsertSpaceOnPickSuggestionManually = true; } if (InputTypeCompatUtils.isEmailVariation(variation)) { mIsSettingsSuggestionStripOn = false; @@ -1058,9 +1062,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar super.updateFullscreenMode(); if (mKeyPreviewBackingView == null) return; - // In extract mode, no need to have extra space to show the key preview. + // In fullscreen mode, no need to have extra space to show the key preview. // If not, we should have extra space above the keyboard to show the key preview. - mKeyPreviewBackingView.setVisibility(isExtractViewShown() ? View.GONE : View.VISIBLE); + mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE); } @Override @@ -1589,6 +1593,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public boolean isShowingPunctuationList() { + if (mSuggestionsView == null) return false; return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions(); } @@ -1815,8 +1820,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final int rawPrimaryCode = suggestion.charAt(0); // Maybe apply the "bidi mirrored" conversions for parentheses final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); - final int primaryCode = Key.getRtlParenthesisCode( - rawPrimaryCode, keyboard.mIsRtlKeyboard); + final boolean isRtl = keyboard != null && keyboard.mIsRtlKeyboard; + final int primaryCode = Key.getRtlParenthesisCode(rawPrimaryCode, isRtl); final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : ""; final int toLeft = (ic == null || TextUtils.isEmpty(beforeText)) @@ -1850,7 +1855,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar suggestion.toString(), index, suggestions.mWords); TextEntryState.acceptedSuggestion(mComposingStringBuilder.toString(), suggestion); // Follow it with a space - if (mShouldInsertMagicSpace && !recorrecting) { + if (mInsertSpaceOnPickSuggestionManually && !recorrecting) { sendMagicSpace(); } @@ -2144,16 +2149,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } }; - // update sound effect volume + // update keypress sound volume private void updateSoundEffectVolume() { - final String[] volumePerHardwareList = mResources.getStringArray(R.array.keypress_volumes); - final String hardwarePrefix = Build.HARDWARE + ","; - for (final String element : volumePerHardwareList) { - if (element.startsWith(hardwarePrefix)) { - mFxVolume = Float.parseFloat(element.substring(element.lastIndexOf(',') + 1)); - break; - } - } + mFxVolume = Utils.getCurrentKeypressSoundVolume(mPrefs, mResources); } // update flags for silent mode @@ -2334,7 +2332,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar p.println(" mCorrectionMode=" + mCorrectionMode); p.println(" mHasUncommittedTypedChars=" + mHasUncommittedTypedChars); p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled); - p.println(" mShouldInsertMagicSpace=" + mShouldInsertMagicSpace); + p.println(" mInsertSpaceOnPickSuggestionManually=" + mInsertSpaceOnPickSuggestionManually); p.println(" mApplicationSpecifiedCompletionOn=" + mApplicationSpecifiedCompletionOn); p.println(" TextEntryState.state=" + TextEntryState.getState()); p.println(" mSoundOn=" + mSettingsValues.mSoundOn); diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java index 51f6c040d..c61dd6313 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java @@ -35,7 +35,6 @@ import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.KeyEventHandler; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; - /** * A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting * key presses and touch movements. @@ -125,7 +124,7 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { @Override public DrawingProxy getDrawingProxy() { - return this; + return this; } @Override @@ -180,10 +179,15 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { return x; } + private boolean mIsDismissing; + @Override public boolean dismissMoreKeysPanel() { - if (mController == null) return false; - return mController.dismissMoreKeysPanel(); + if (mIsDismissing) return false; + mIsDismissing = true; + final boolean dismissed = mController.dismissMoreKeysPanel(); + mIsDismissing = false; + return dismissed; } @Override diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index d9508f4c1..eeb0299b1 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -26,6 +26,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; +import android.media.AudioManager; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; @@ -91,9 +92,11 @@ public class Settings extends InputMethodSettingsActivity public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; - public static final String PREF_VIBRATION_DURATION_SETTINGS = + public static final String PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; + public static final String PREF_KEYPRESS_SOUND_VOLUME = + "pref_keypress_sound_volume"; // Dialog ids private static final int VOICE_INPUT_CONFIRM_DIALOG = 0; @@ -327,7 +330,8 @@ public class Settings extends InputMethodSettingsActivity } private PreferenceScreen mInputLanguageSelection; - private PreferenceScreen mVibrationDurationSettingsPref; + private PreferenceScreen mKeypressVibrationDurationSettingsPref; + private PreferenceScreen mKeypressSoundVolumeSettingsPref; private ListPreference mVoicePreference; private CheckBoxPreference mShowSettingsKeyPreference; private ListPreference mShowCorrectionSuggestionsPreference; @@ -341,7 +345,8 @@ public class Settings extends InputMethodSettingsActivity private boolean mVoiceOn; private AlertDialog mDialog; - private TextView mVibrationSettingsTextView; + private TextView mKeypressVibrationDurationSettingsTextView; + private TextView mKeypressSoundVolumeSettingsTextView; private boolean mOkClicked = false; private String mVoiceModeOff; @@ -477,19 +482,34 @@ public class Settings extends InputMethodSettingsActivity } } - mVibrationDurationSettingsPref = - (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); - if (mVibrationDurationSettingsPref != null) { - mVibrationDurationSettingsPref.setOnPreferenceClickListener( + mKeypressVibrationDurationSettingsPref = + (PreferenceScreen) findPreference(PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS); + if (mKeypressVibrationDurationSettingsPref != null) { + mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener( new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference arg0) { - showVibrationSettingsDialog(); + showKeypressVibrationDurationSettingsDialog(); return true; } }); - updateVibrationDurationSettingsSummary(prefs, res); + updateKeypressVibrationDurationSettingsSummary(prefs, res); } + + mKeypressSoundVolumeSettingsPref = + (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME); + if (mKeypressSoundVolumeSettingsPref != null) { + mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference arg0) { + showKeypressSoundVolumeSettingDialog(); + return true; + } + }); + updateKeypressSoundVolumeSummary(prefs, res); + } + refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res); } @SuppressWarnings("unused") @@ -537,6 +557,7 @@ public class Settings extends InputMethodSettingsActivity updateVoiceModeSummary(); updateShowCorrectionSuggestionsSummary(); updateKeyPreviewPopupDelaySummary(); + refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources()); } @Override @@ -637,26 +658,44 @@ public class Settings extends InputMethodSettingsActivity } } - private void updateVibrationDurationSettingsSummary(SharedPreferences sp, Resources res) { - if (mVibrationDurationSettingsPref != null) { - mVibrationDurationSettingsPref.setSummary( + private void refreshEnablingsOfKeypressSoundAndVibrationSettings( + SharedPreferences sp, Resources res) { + if (mKeypressVibrationDurationSettingsPref != null) { + final boolean hasVibrator = VibratorCompatWrapper.getInstance(this).hasVibrator(); + final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON, + res.getBoolean(R.bool.config_default_vibration_enabled)); + mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn); + } + + if (mKeypressSoundVolumeSettingsPref != null) { + final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON, + res.getBoolean(R.bool.config_default_sound_enabled)); + mKeypressSoundVolumeSettingsPref.setEnabled(soundOn); + } + } + + private void updateKeypressVibrationDurationSettingsSummary( + SharedPreferences sp, Resources res) { + if (mKeypressVibrationDurationSettingsPref != null) { + mKeypressVibrationDurationSettingsPref.setSummary( Utils.getCurrentVibrationDuration(sp, res) + res.getString(R.string.settings_ms)); } } - private void showVibrationSettingsDialog() { + private void showKeypressVibrationDurationSettingsDialog() { final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); final Activity context = getActivityInternal(); final Resources res = context.getResources(); final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.prefs_vibration_duration_settings); + builder.setTitle(R.string.prefs_keypress_vibration_duration_settings); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { - final int ms = Integer.valueOf(mVibrationSettingsTextView.getText().toString()); - sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); - updateVibrationDurationSettingsSummary(sp, res); + final int ms = Integer.valueOf( + mKeypressVibrationDurationSettingsTextView.getText().toString()); + sp.edit().putInt(Settings.PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS, ms).apply(); + updateKeypressVibrationDurationSettingsSummary(sp, res); } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @@ -669,13 +708,13 @@ public class Settings extends InputMethodSettingsActivity R.layout.vibration_settings_dialog, null); final int currentMs = Utils.getCurrentVibrationDuration( getPreferenceManager().getSharedPreferences(), getResources()); - mVibrationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value); + mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value); final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings); sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { final int tempMs = arg1; - mVibrationSettingsTextView.setText(String.valueOf(tempMs)); + mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs)); } @Override @@ -689,7 +728,67 @@ public class Settings extends InputMethodSettingsActivity } }); sb.setProgress(currentMs); - mVibrationSettingsTextView.setText(String.valueOf(currentMs)); + mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs)); + builder.setView(v); + builder.create().show(); + } + + private void updateKeypressSoundVolumeSummary(SharedPreferences sp, Resources res) { + if (mKeypressSoundVolumeSettingsPref != null) { + mKeypressSoundVolumeSettingsPref.setSummary( + String.valueOf((int)(Utils.getCurrentKeypressSoundVolume(sp, res) * 100))); + } + } + + private void showKeypressSoundVolumeSettingDialog() { + final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); + final Activity context = getActivityInternal(); + final Resources res = context.getResources(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.prefs_keypress_sound_volume_settings); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + final float volume = + ((float)Integer.valueOf( + mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100; + sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply(); + updateKeypressSoundVolumeSummary(sp, res); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + }); + final View v = context.getLayoutInflater().inflate( + R.layout.sound_effect_volume_dialog, null); + final int currentVolumeInt = (int)(Utils.getCurrentKeypressSoundVolume( + getPreferenceManager().getSharedPreferences(), getResources()) * 100); + mKeypressSoundVolumeSettingsTextView = + (TextView)v.findViewById(R.id.sound_effect_volume_value); + final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar); + sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { + final int tempVolume = arg1; + mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume)); + } + + @Override + public void onStartTrackingTouch(SeekBar arg0) { + } + + @Override + public void onStopTrackingTouch(SeekBar arg0) { + final float tempVolume = ((float)arg0.getProgress()) / 100; + am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume); + } + }); + sb.setProgress(currentVolumeInt); + mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt)); builder.setView(v); builder.create().show(); } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java new file mode 100644 index 000000000..444c7f5f0 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java @@ -0,0 +1,53 @@ +/* + * 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.latin; + +import android.content.Context; + +import com.android.inputmethod.keyboard.ProximityInfo; + +public class SynchronouslyLoadedContactsDictionary extends ContactsDictionary { + private boolean mClosed; + + public SynchronouslyLoadedContactsDictionary(final Context context) { + super(context, Suggest.DIC_CONTACTS); + mClosed = false; + } + + @Override + public synchronized void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { + blockingReloadDictionaryIfRequired(); + getWordsInner(codes, callback, proximityInfo); + } + + @Override + public synchronized boolean isValidWord(CharSequence word) { + blockingReloadDictionaryIfRequired(); + return getWordFrequency(word) > -1; + } + + // Protect against multiple closing + @Override + public synchronized void close() { + // Actually with the current implementation of ContactsDictionary it's safe to close + // several times, so the following protection is really only for foolproofing + if (mClosed) return; + mClosed = true; + super.close(); + } +} diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index 67da4fa50..0bbbf3995 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -179,8 +179,9 @@ public class UserDictionary extends ExpandableDictionary { new Thread("addWord") { @Override public void run() { + Cursor cursor = null; try { - final Cursor cursor = client.query(Words.CONTENT_URI, PROJECTION_ADD, + cursor = client.query(Words.CONTENT_URI, PROJECTION_ADD, "word=? and ((locale IS NULL) or (locale=?))", new String[] { word, mLocale }, null); if (cursor != null && cursor.moveToFirst()) { @@ -201,6 +202,9 @@ public class UserDictionary extends ExpandableDictionary { // If we come here, the activity is already about to be killed, and we // have no means of contacting the content provider any more. // See ContentResolver#insert, inside the catch(){} + } finally { + if (null != cursor) cursor.close(); + client.release(); } } }.start(); diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index f6343f1d7..b29ff1975 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -779,7 +779,7 @@ public class Utils { } public static int getCurrentVibrationDuration(SharedPreferences sp, Resources res) { - final int ms = sp.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1); + final int ms = sp.getInt(Settings.PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS, -1); if (ms >= 0) { return ms; } @@ -794,6 +794,22 @@ public class Utils { return -1; } + public static float getCurrentKeypressSoundVolume(SharedPreferences sp, Resources res) { + final float volume = sp.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f); + if (volume >= 0) { + return volume; + } + + final String[] volumePerHardwareList = res.getStringArray(R.array.keypress_volumes); + final String hardwarePrefix = Build.HARDWARE + ","; + for (final String element : volumePerHardwareList) { + if (element.startsWith(hardwarePrefix)) { + return Float.parseFloat(element.substring(element.lastIndexOf(',') + 1)); + } + } + return -1.0f; + } + public static boolean willAutoCorrect(SuggestedWords suggestions) { return !suggestions.mTypedWordValid && suggestions.mHasAutoCorrectionCandidate && !suggestions.shouldBlockAutoCorrection(); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index f9e6a5e68..095c2c51c 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -35,6 +35,7 @@ import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.Flag; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.SynchronouslyLoadedContactsDictionary; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WhitelistDictionary; @@ -60,11 +61,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private static final int CAPITALIZE_ALL = 2; // All caps private final static String[] EMPTY_STRING_ARRAY = new String[0]; - private final static SuggestionsInfo NOT_IN_DICT_EMPTY_SUGGESTIONS = - new SuggestionsInfo(0, EMPTY_STRING_ARRAY); - private final static SuggestionsInfo IN_DICT_EMPTY_SUGGESTIONS = - new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, - EMPTY_STRING_ARRAY); private final static Flag[] USE_FULL_EDIT_DISTANCE_FLAG_ARRAY; static { // See BinaryDictionary.java for an explanation of these flags @@ -82,6 +78,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { Collections.synchronizedMap(new TreeMap<String, Dictionary>()); private Map<String, Dictionary> mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); + private SynchronouslyLoadedContactsDictionary mContactsDictionary; // The threshold for a candidate to be offered as a suggestion. private double mSuggestionThreshold; @@ -101,6 +98,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return new AndroidSpellCheckerSession(this); } + private static SuggestionsInfo getNotInDictEmptySuggestions() { + return new SuggestionsInfo(0, EMPTY_STRING_ARRAY); + } + + private static SuggestionsInfo getInDictEmptySuggestions() { + return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, + EMPTY_STRING_ARRAY); + } + private static class SuggestionsGatherer implements WordCallback { public static class Result { public final String[] mSuggestions; @@ -267,6 +273,14 @@ 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(); + } return false; } @@ -300,6 +314,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService { mWhitelistDictionaries.put(localeStr, whitelistDictionary); } dictionaryCollection.addDictionary(whitelistDictionary); + if (null == mContactsDictionary) { + mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this); + } + // TODO: add a setting to use or not contacts when checking spelling + dictionaryCollection.addDictionary(mContactsDictionary); return new DictAndProximity(dictionaryCollection, proximityInfo); } @@ -393,9 +412,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); - if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS; - return dictInfo.mDictionary.isValidWord(text) ? IN_DICT_EMPTY_SUGGESTIONS - : NOT_IN_DICT_EMPTY_SUGGESTIONS; + if (null == dictInfo) return getNotInDictEmptySuggestions(); + return dictInfo.mDictionary.isValidWord(text) ? getInDictEmptySuggestions() + : getNotInDictEmptySuggestions(); } finally { if (null != dictInfo) { if (!mDictionaryPool.offer(dictInfo)) { @@ -430,7 +449,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); - if (null == dictInfo) return NOT_IN_DICT_EMPTY_SUGGESTIONS; + if (null == dictInfo) return getNotInDictEmptySuggestions(); dictInfo.mDictionary.getWords(composer, suggestionsGatherer, dictInfo.mProximityInfo); isInDict = dictInfo.mDictionary.isValidWord(text); @@ -475,7 +494,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { throw e; } else { Log.e(TAG, "Exception while spellcheking: " + e); - return NOT_IN_DICT_EMPTY_SUGGESTIONS; + return getNotInDictEmptySuggestions(); } } } |