diff options
Diffstat (limited to 'java/src')
63 files changed, 1258 insertions, 666 deletions
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java index 0b532f7f0..6e14bfa8b 100644 --- a/java/src/com/android/inputmethod/compat/CompatUtils.java +++ b/java/src/com/android/inputmethod/compat/CompatUtils.java @@ -22,7 +22,6 @@ import android.util.Log; import java.lang.reflect.Constructor; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; @@ -74,23 +73,22 @@ public class CompatUtils { return targetClass.getMethod(name, parameterTypes); } catch (SecurityException e) { // ignore - return null; } catch (NoSuchMethodException e) { // ignore - return null; } + return null; } public static Field getField(Class<?> targetClass, String name) { + if (targetClass == null || TextUtils.isEmpty(name)) return null; try { return targetClass.getField(name); } catch (SecurityException e) { // ignore - return null; } catch (NoSuchFieldException e) { // ignore - return null; } + return null; } public static Constructor<?> getConstructor(Class<?> targetClass, Class<?>[] types) { @@ -99,51 +97,49 @@ public class CompatUtils { return targetClass.getConstructor(types); } catch (SecurityException e) { // ignore - return null; } catch (NoSuchMethodException e) { // ignore - return null; } + return null; + } + + public static Object newInstance(Constructor<?> constructor, Object[] args) { + if (constructor == null) return null; + try { + return constructor.newInstance(args); + } catch (Exception e) { + Log.e(TAG, "Exception in newInstance: " + e.getClass().getSimpleName()); + } + return null; } public static Object invoke( Object receiver, Object defaultValue, Method method, Object... args) { - if (receiver == null || method == null) return defaultValue; + if (method == null) return defaultValue; try { return method.invoke(receiver, args); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception in invoke: IllegalArgumentException"); - return defaultValue; - } catch (IllegalAccessException e) { - Log.e(TAG, "Exception in invoke: IllegalAccessException"); - return defaultValue; - } catch (InvocationTargetException e) { - Log.e(TAG, "Exception in invoke: IllegalTargetException"); - return defaultValue; + } catch (Exception e) { + Log.e(TAG, "Exception in invoke: " + e.getClass().getSimpleName()); } + return defaultValue; } public static Object getFieldValue(Object receiver, Object defaultValue, Field field) { - if (receiver == null || field == null) return defaultValue; + if (field == null) return defaultValue; try { return field.get(receiver); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception in getFieldValue: IllegalArgumentException"); - return defaultValue; - } catch (IllegalAccessException e) { - Log.e(TAG, "Exception in getFieldValue: IllegalAccessException"); - return defaultValue; + } catch (Exception e) { + Log.e(TAG, "Exception in getFieldValue: " + e.getClass().getSimpleName()); } + return defaultValue; } public static void setFieldValue(Object receiver, Field field, Object value) { - if (receiver == null || field == null) return; + if (field == null) return; try { field.set(receiver, value); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Exception in setFieldValue: IllegalArgumentException"); - } catch (IllegalAccessException e) { - Log.e(TAG, "Exception in setFieldValue: IllegalAccessException"); + } catch (Exception e) { + Log.e(TAG, "Exception in setFieldValue: " + e.getClass().getSimpleName()); } } diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java index f6f4f7a59..2789bcb39 100644 --- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java @@ -78,6 +78,9 @@ public class EditorInfoCompatUtils { case EditorInfo.IME_ACTION_SEND: action = "actionSend"; break; + case EditorInfo.IME_ACTION_NEXT: + action = "actionNext"; + break; case EditorInfo.IME_ACTION_DONE: action = "actionDone"; break; diff --git a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java index c926be06f..7d00b6007 100644 --- a/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputConnectionCompatUtils.java @@ -18,15 +18,12 @@ package com.android.inputmethod.compat; import com.android.inputmethod.latin.EditingUtils.SelectedWord; -import android.util.Log; import android.view.inputmethod.InputConnection; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class InputConnectionCompatUtils { - private static final String TAG = InputConnectionCompatUtils.class.getSimpleName(); private static final Class<?> CLASS_CorrectionInfo = CompatUtils .getClass("android.view.inputmethod.CorrectionInfo"); private static final Class<?>[] INPUT_TYPE_CorrectionInfo = new Class<?>[] { int.class, @@ -53,18 +50,10 @@ public class InputConnectionCompatUtils { return; } Object[] args = { offset, oldText, newText }; - try { - Object correctionInfo = CONSTRUCTOR_CorrectionInfo.newInstance(args); + Object correctionInfo = CompatUtils.newInstance(CONSTRUCTOR_CorrectionInfo, args); + if (correctionInfo != null) { CompatUtils.invoke(ic, null, METHOD_InputConnection_commitCorrection, correctionInfo); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Error in commitCorrection: IllegalArgumentException"); - } catch (InstantiationException e) { - Log.e(TAG, "Error in commitCorrection: InstantiationException"); - } catch (IllegalAccessException e) { - Log.e(TAG, "Error in commitCorrection: IllegalAccessException"); - } catch (InvocationTargetException e) { - Log.e(TAG, "Error in commitCorrection: InvocationTargetException"); } } diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java index 806c355a9..667d86c42 100644 --- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatWrapper.java @@ -48,6 +48,8 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper CompatUtils.getMethod(CLASS_InputMethodSubtype, "containsExtraValueKey", String.class); private static final Method METHOD_getExtraValueOf = CompatUtils.getMethod(CLASS_InputMethodSubtype, "getExtraValueOf", String.class); + private static final Method METHOD_isAuxiliary = + CompatUtils.getMethod(CLASS_InputMethodSubtype, "isAuxiliary"); private final int mDummyNameResId; private final int mDummyIconResId; @@ -116,6 +118,10 @@ public final class InputMethodSubtypeCompatWrapper extends AbstractCompatWrapper return (String)CompatUtils.invoke(mObj, null, METHOD_getExtraValueOf, key); } + public boolean isAuxiliary() { + return (Boolean)CompatUtils.invoke(mObj, false, METHOD_isAuxiliary); + } + public boolean isDummy() { return !hasOriginalObject(); } diff --git a/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java index d85174188..6c2f0f799 100644 --- a/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/InputTypeCompatUtils.java @@ -37,6 +37,7 @@ public class InputTypeCompatUtils { (Integer) CompatUtils.getFieldValue(null, null, FIELD_InputType_TYPE_NUMBER_VARIATION_PASSWORD); private static final int WEB_TEXT_PASSWORD_INPUT_TYPE; + private static final int WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE; private static final int NUMBER_PASSWORD_INPUT_TYPE; private static final int TEXT_PASSWORD_INPUT_TYPE = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD; @@ -45,20 +46,35 @@ public class InputTypeCompatUtils { static { WEB_TEXT_PASSWORD_INPUT_TYPE = - OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD != null - ? InputType.TYPE_CLASS_TEXT | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD - : 0; + OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD != null + ? InputType.TYPE_CLASS_TEXT | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_PASSWORD + : 0; + WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE = + OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS != null + ? InputType.TYPE_CLASS_TEXT + | OBJ_InputType_TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS + : 0; NUMBER_PASSWORD_INPUT_TYPE = OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD != null ? InputType.TYPE_CLASS_NUMBER | OBJ_InputType_TYPE_NUMBER_VARIATION_PASSWORD : 0; } + private static boolean isWebEditTextInputType(int inputType) { + return inputType == (InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT); + } + private static boolean isWebPasswordInputType(int inputType) { return WEB_TEXT_PASSWORD_INPUT_TYPE != 0 && inputType == WEB_TEXT_PASSWORD_INPUT_TYPE; } + private static boolean isWebEmailAddressInputType(int inputType) { + return WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE != 0 + && inputType == WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE; + } + private static boolean isNumberPasswordInputType(int inputType) { return NUMBER_PASSWORD_INPUT_TYPE != 0 && inputType == NUMBER_PASSWORD_INPUT_TYPE; @@ -78,6 +94,13 @@ public class InputTypeCompatUtils { || isWebEmailAddressVariation(variation); } + public static boolean isWebInputType(int inputType) { + final int maskedInputType = + inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION); + return isWebEditTextInputType(maskedInputType) || isWebPasswordInputType(maskedInputType) + || isWebEmailAddressInputType(maskedInputType); + } + // Please refer to TextView.isPasswordInputType public static boolean isPasswordInputType(int inputType) { final int maskedInputType = diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java new file mode 100644 index 000000000..5b02de36e --- /dev/null +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -0,0 +1,78 @@ +/* + * 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 com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver; + +import android.content.Context; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; + +import java.lang.reflect.Constructor; +import java.util.Locale; + +public class SuggestionSpanUtils { + public static final String ACTION_SUGGESTION_PICKED = + "android.text.style.SUGGESTION_PICKED"; + 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"; + + private static final Class<?> CLASS_SuggestionSpan = CompatUtils + .getClass("android.text.style.SuggestionSpan"); + private static final Class<?>[] INPUT_TYPE_SuggestionSpan = new Class<?>[] { + 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 boolean SUGGESTION_SPAN_IS_SUPPORTED; + static { + SUGGESTION_SPAN_IS_SUPPORTED = + CLASS_SuggestionSpan != null && CONSTRUCTOR_SuggestionSpan != null; + } + + public static CharSequence getTextWithSuggestionSpan(Context context, + CharSequence suggestion, SuggestedWords suggestedWords) { + if (CONSTRUCTOR_SuggestionSpan == null || suggestedWords == null + || suggestedWords.size() == 0) { + return suggestion; + } + + final Spannable spannable; + if (suggestion instanceof Spannable) { + spannable = (Spannable) suggestion; + } else { + spannable = new SpannableString(suggestion); + } + // TODO: Use SUGGESTIONS_MAX_SIZE instead of 5. + final int N = Math.min(5, suggestedWords.size()); + final String[] suggestionsArray = new String[N]; + for (int i = 0; i < N; ++i) { + suggestionsArray[i] = suggestedWords.getWord(i).toString(); + } + final Object[] args = + { context, null, suggestionsArray, 0, + (Class<?>) SuggestionSpanPickedNotificationReceiver.class }; + final Object ss = CompatUtils.newInstance(CONSTRUCTOR_SuggestionSpan, args); + if (ss == null) { + return suggestion; + } + spannable.setSpan(ss, 0, suggestion.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return spannable; + } +} diff --git a/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java index e14a49c49..290e6b554 100644 --- a/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java +++ b/java/src/com/android/inputmethod/deprecated/LanguageSwitcherProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. + * 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 diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java index 753dceead..85993ea4d 100644 --- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java +++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -137,8 +137,8 @@ public class VoiceProxy implements VoiceInput.UiListener { @Override public void showHint(int viewResource) { View view = LayoutInflater.from(mService).inflate(viewResource, null); - mService.setCandidatesView(view); - mService.setCandidatesViewShown(true); +// mService.setCandidatesView(view); +// mService.setCandidatesViewShown(true); mIsShowingHint = true; } }); @@ -441,7 +441,7 @@ public class VoiceProxy implements VoiceInput.UiListener { } builder.setTypedWordValid(true).setHasMinimalSuggestion(true); mService.setSuggestions(builder.build()); - mService.setCandidatesViewShown(true); +// mService.setCandidatesViewShown(true); return true; } return false; @@ -526,7 +526,7 @@ public class VoiceProxy implements VoiceInput.UiListener { mHandler.post(new Runnable() { @Override public void run() { - mService.setCandidatesViewShown(false); +// mService.setCandidatesViewShown(false); mRecognizing = true; mVoiceInput.newView(); View v = mVoiceInput.getView(); @@ -536,7 +536,7 @@ public class VoiceProxy implements VoiceInput.UiListener { ((ViewGroup) p).removeView(v); } - View keyboardView = KeyboardSwitcher.getInstance().getInputView(); + View keyboardView = KeyboardSwitcher.getInstance().getKeyboardView(); // The full height of the keyboard is difficult to calculate // as the dimension is expressed in "mm" and not in "pixel" @@ -691,7 +691,7 @@ public class VoiceProxy implements VoiceInput.UiListener { if (mSubtypeSwitcher.isVoiceMode() && windowToken != null) { // Close keyboard view if it is been shown. if (KeyboardSwitcher.getInstance().isInputViewShown()) - KeyboardSwitcher.getInstance().getInputView().purgeKeyboardAndClosing(); + KeyboardSwitcher.getInstance().getKeyboardView().purgeKeyboardAndClosing(); startListening(false, windowToken); } // If we have no token, onAttachedToWindow will take care of showing dialog and start diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java index a1b49b475..fe70eef96 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Google Inc. + * Copyright (C) 2008-2009 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 @@ -43,6 +43,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Locale; +import java.util.Map.Entry; +import java.util.TreeMap; public class InputLanguageSelection extends PreferenceActivity { @@ -51,21 +53,17 @@ public class InputLanguageSelection extends PreferenceActivity { private HashMap<CheckBoxPreference, Locale> mLocaleMap = new HashMap<CheckBoxPreference, Locale>(); - private static class Loc implements Comparable<Object> { + private static class LocaleEntry implements Comparable<Object> { private static Collator sCollator = Collator.getInstance(); private String mLabel; public final Locale mLocale; - public Loc(String label, Locale locale) { + public LocaleEntry(String label, Locale locale) { this.mLabel = label; this.mLocale = locale; } - public void setLabel(String label) { - this.mLabel = label; - } - @Override public String toString() { return this.mLabel; @@ -73,7 +71,7 @@ public class InputLanguageSelection extends PreferenceActivity { @Override public int compareTo(Object o) { - return sCollator.compare(this.mLabel, ((Loc) o).mLabel); + return sCollator.compare(this.mLabel, ((LocaleEntry) o).mLabel); } } @@ -85,21 +83,58 @@ public class InputLanguageSelection extends PreferenceActivity { mPrefs = PreferenceManager.getDefaultSharedPreferences(this); mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, ""); String[] languageList = mSelectedLanguages.split(","); - ArrayList<Loc> availableLanguages = getUniqueLocales(); + ArrayList<LocaleEntry> availableLanguages = getUniqueLocales(); PreferenceGroup parent = getPreferenceScreen(); + final HashMap<Long, LocaleEntry> dictionaryIdLocaleMap = new HashMap<Long, LocaleEntry>(); + final TreeMap<LocaleEntry, Boolean> localeHasDictionaryMap = + new TreeMap<LocaleEntry, Boolean>(); for (int i = 0; i < availableLanguages.size(); i++) { - Locale locale = availableLanguages.get(i).mLocale; - final Pair<Boolean, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale); - final boolean hasDictionary = hasDictionaryOrLayout.first; + LocaleEntry loc = availableLanguages.get(i); + Locale locale = loc.mLocale; + final Pair<Long, Boolean> hasDictionaryOrLayout = hasDictionaryOrLayout(locale); + final Long dictionaryId = hasDictionaryOrLayout.first; final boolean hasLayout = hasDictionaryOrLayout.second; + final boolean hasDictionary = dictionaryId != null; // Add this locale to the supported list if: - // 1) this locale has a layout/ 2) this locale has a dictionary and the length - // of the locale is equal to or larger than 5. - if (!hasLayout && !(hasDictionary && locale.toString().length() >= 5)) { + // 1) this locale has a layout/ 2) this locale has a dictionary + // If some locales have no layout but have a same dictionary, the shortest locale + // will be added to the supported list. + if (!hasLayout && !hasDictionary) { continue; } + if (hasLayout) { + localeHasDictionaryMap.put(loc, hasDictionary); + } + if (!hasDictionary) { + continue; + } + if (dictionaryIdLocaleMap.containsKey(dictionaryId)) { + final String newLocale = locale.toString(); + final String oldLocale = + dictionaryIdLocaleMap.get(dictionaryId).mLocale.toString(); + // Check if this locale is more appropriate to be the candidate of the input locale. + if (oldLocale.length() <= newLocale.length() && !hasLayout) { + // Don't add this new locale to the map<dictionary id, locale> if: + // 1) the new locale's name is longer than the existing one, and + // 2) the new locale doesn't have its layout + continue; + } + } + dictionaryIdLocaleMap.put(dictionaryId, loc); + } + + for (LocaleEntry localeEntry : dictionaryIdLocaleMap.values()) { + if (!localeHasDictionaryMap.containsKey(localeEntry)) { + localeHasDictionaryMap.put(localeEntry, true); + } + } + + for (Entry<LocaleEntry, Boolean> entry : localeHasDictionaryMap.entrySet()) { + final LocaleEntry localeEntry = entry.getKey(); + final Locale locale = localeEntry.mLocale; + final Boolean hasDictionary = entry.getValue(); CheckBoxPreference pref = new CheckBoxPreference(this); - pref.setTitle(SubtypeSwitcher.getFullDisplayName(locale, true)); + pref.setTitle(localeEntry.mLabel); boolean checked = isLocaleIn(locale, languageList); pref.setChecked(checked); if (hasDictionary) { @@ -118,11 +153,11 @@ public class InputLanguageSelection extends PreferenceActivity { return false; } - private Pair<Boolean, Boolean> hasDictionaryOrLayout(Locale locale) { - if (locale == null) return new Pair<Boolean, Boolean>(false, false); + private Pair<Long, Boolean> hasDictionaryOrLayout(Locale locale) { + if (locale == null) return new Pair<Long, Boolean>(null, false); final Resources res = getResources(); final Locale saveLocale = Utils.setSystemLocale(res, locale); - final boolean hasDictionary = DictionaryFactory.isDictionaryAvailable(this, locale); + final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale); boolean hasLayout = false; try { @@ -141,7 +176,7 @@ public class InputLanguageSelection extends PreferenceActivity { } catch (IOException e) { } Utils.setSystemLocale(res, saveLocale); - return new Pair<Boolean, Boolean>(hasDictionary, hasLayout); + return new Pair<Long, Boolean>(dictionaryId, hasLayout); } private String get5Code(Locale locale) { @@ -174,13 +209,13 @@ public class InputLanguageSelection extends PreferenceActivity { SharedPreferencesCompat.apply(editor); } - public ArrayList<Loc> getUniqueLocales() { + public ArrayList<LocaleEntry> getUniqueLocales() { String[] locales = getAssets().getLocales(); Arrays.sort(locales); - ArrayList<Loc> uniqueLocales = new ArrayList<Loc>(); + ArrayList<LocaleEntry> uniqueLocales = new ArrayList<LocaleEntry>(); final int origSize = locales.length; - Loc[] preprocess = new Loc[origSize]; + LocaleEntry[] preprocess = new LocaleEntry[origSize]; int finalSize = 0; for (int i = 0 ; i < origSize; i++ ) { String s = locales[i]; @@ -202,26 +237,13 @@ public class InputLanguageSelection extends PreferenceActivity { if (finalSize == 0) { preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l); + new LocaleEntry(SubtypeSwitcher.getFullDisplayName(l, false), l); } else { - // check previous entry: - // same lang and a country -> upgrade to full name and - // insert ours with full name - // diff lang -> insert ours with lang-only name - if (preprocess[finalSize-1].mLocale.getLanguage().equals( - language)) { - preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName( - preprocess[finalSize-1].mLocale, false)); - preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l); + if (s.equals("zz_ZZ")) { + // ignore this locale } else { - String displayName; - if (s.equals("zz_ZZ")) { - // ignore this locale - } else { - displayName = SubtypeSwitcher.getFullDisplayName(l, true); - preprocess[finalSize++] = new Loc(displayName, l); - } + final String displayName = SubtypeSwitcher.getFullDisplayName(l, false); + preprocess[finalSize++] = new LocaleEntry(displayName, l); } } } diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java index 5ef236e31..1eedb5ee1 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -20,6 +20,7 @@ import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SharedPreferencesCompat; +import com.android.inputmethod.latin.Utils; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -125,8 +126,7 @@ public class LanguageSwitcher { private void constructLocales() { mLocales.clear(); for (final String lang : mSelectedLanguageArray) { - final Locale locale = new Locale(lang.substring(0, 2), - lang.length() > 4 ? lang.substring(3, 5) : ""); + final Locale locale = Utils.constructLocaleFromString(lang); mLocales.add(locale); } } diff --git a/java/src/com/android/inputmethod/latin/Recorrection.java b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java index 3fa6292ba..d40728d25 100644 --- a/java/src/com/android/inputmethod/latin/Recorrection.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java @@ -14,11 +14,22 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.deprecated.recorrection; import com.android.inputmethod.compat.InputConnectionCompatUtils; +import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.CandidateView; +import com.android.inputmethod.latin.EditingUtils; +import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.Settings; +import com.android.inputmethod.latin.Suggest; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.TextEntryState; +import com.android.inputmethod.latin.WordComposer; import android.content.SharedPreferences; import android.content.res.Resources; @@ -32,12 +43,13 @@ import java.util.ArrayList; /** * Manager of re-correction functionalities */ -public class Recorrection { +public class Recorrection implements SharedPreferences.OnSharedPreferenceChangeListener { private static final Recorrection sInstance = new Recorrection(); private LatinIME mService; private boolean mRecorrectionEnabled = false; - private final ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>(); + private final ArrayList<RecorrectionSuggestionEntries> mRecorrectionSuggestionsList = + new ArrayList<RecorrectionSuggestionEntries>(); public static Recorrection getInstance() { return sInstance; @@ -58,20 +70,17 @@ public class Recorrection { } private void initInternal(LatinIME context, SharedPreferences prefs) { - final Resources res = context.getResources(); - // If the option should not be shown, do not read the re-correction preference - // but always use the default setting defined in the resources. - if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) { - mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED, - res.getBoolean(R.bool.config_default_recorrection_enabled)); - } else { - mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) { + mRecorrectionEnabled = false; + return; } + updateRecorrectionEnabled(context.getResources(), prefs); mService = context; + prefs.registerOnSharedPreferenceChangeListener(this); } public void checkRecorrectionOnStart() { - if (!mRecorrectionEnabled) return; + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return; final InputConnection ic = mService.getCurrentInputConnection(); if (ic == null) return; @@ -98,42 +107,42 @@ public class Recorrection { } public void updateRecorrectionSelection(KeyboardSwitcher keyboardSwitcher, - CandidateView candidateView, int candidatesStart, int candidatesEnd, int newSelStart, - int newSelEnd, int oldSelStart, int lastSelectionStart, + CandidateView candidateView, int candidatesStart, int candidatesEnd, + int newSelStart, int newSelEnd, int oldSelStart, int lastSelectionStart, int lastSelectionEnd, boolean hasUncommittedTypedChars) { - if (mRecorrectionEnabled && mService.isShowingSuggestionsStrip()) { - // Don't look for corrections if the keyboard is not visible - if (keyboardSwitcher.isInputViewShown()) { - // Check if we should go in or out of correction mode. - if (mService.isSuggestionsRequested() - && (candidatesStart == candidatesEnd || newSelStart != oldSelStart - || TextEntryState.isRecorrecting()) - && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) { - if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) { - mService.mHandler.cancelUpdateBigramPredictions(); - mService.mHandler.postUpdateOldSuggestions(); - } else { - abortRecorrection(false); - // If showing the "touch again to save" hint, do not replace it. Else, - // show the bigrams if we are at the end of the text, punctuation otherwise. - if (candidateView != null - && !candidateView.isShowingAddToDictionaryHint()) { - InputConnection ic = mService.getCurrentInputConnection(); - if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) { - if (!mService.isShowingPunctuationList()) { - mService.setPunctuationSuggestions(); - } - } else { - mService.mHandler.postUpdateBigramPredictions(); - } + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return; + if (!mService.isShowingSuggestionsStrip()) return; + if (!keyboardSwitcher.isInputViewShown()) return; + if (!mService.isSuggestionsRequested()) return; + // Don't look for corrections if the keyboard is not visible + // Check if we should go in or out of correction mode. + if ((candidatesStart == candidatesEnd || newSelStart != oldSelStart || TextEntryState + .isRecorrecting()) + && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) { + if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) { + mService.mHandler.cancelUpdateBigramPredictions(); + mService.mHandler.postUpdateOldSuggestions(); + } else { + abortRecorrection(false); + // If showing the "touch again to save" hint, do not replace it. Else, + // show the bigrams if we are at the end of the text, punctuation + // otherwise. + if (candidateView != null && !candidateView.isShowingAddToDictionaryHint()) { + InputConnection ic = mService.getCurrentInputConnection(); + if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) { + if (!mService.isShowingPunctuationList()) { + mService.setPunctuationSuggestions(); } + } else { + mService.mHandler.postUpdateBigramPredictions(); } } } } } - public void saveWordInHistory(WordComposer word, CharSequence result) { + public void saveRecorrectionSuggestion(WordComposer word, CharSequence result) { + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return; if (word.size() <= 1) { return; } @@ -144,12 +153,13 @@ public class Recorrection { // Make a copy of the CharSequence, since it is/could be a mutable CharSequence final String resultCopy = result.toString(); - WordAlternatives entry = new WordAlternatives(resultCopy, new WordComposer(word)); - mWordHistory.add(entry); + RecorrectionSuggestionEntries entry = new RecorrectionSuggestionEntries( + resultCopy, new WordComposer(word)); + mRecorrectionSuggestionsList.add(entry); } public void clearWordsInHistory() { - mWordHistory.clear(); + mRecorrectionSuggestionsList.clear(); } /** @@ -160,11 +170,12 @@ public class Recorrection { */ public boolean applyTypedAlternatives(WordComposer word, Suggest suggest, KeyboardSwitcher keyboardSwitcher, EditingUtils.SelectedWord touching) { + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return false; // If we didn't find a match, search for result in typed word history WordComposer foundWord = null; - WordAlternatives alternatives = null; + RecorrectionSuggestionEntries alternatives = null; // Search old suggestions to suggest re-corrected suggestions. - for (WordAlternatives entry : mWordHistory) { + for (RecorrectionSuggestionEntries entry : mRecorrectionSuggestionsList) { if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) { foundWord = entry.mWordComposer; alternatives = entry; @@ -186,7 +197,7 @@ public class Recorrection { // Found a match, show suggestions if (foundWord != null || alternatives != null) { if (alternatives == null) { - alternatives = new WordAlternatives(touching.mWord, foundWord); + alternatives = new RecorrectionSuggestionEntries(touching.mWord, foundWord); } showRecorrections(suggest, keyboardSwitcher, alternatives); if (foundWord != null) { @@ -201,17 +212,18 @@ public class Recorrection { private void showRecorrections(Suggest suggest, KeyboardSwitcher keyboardSwitcher, - WordAlternatives alternatives) { - SuggestedWords.Builder builder = alternatives.getAlternatives(suggest, keyboardSwitcher); + RecorrectionSuggestionEntries entries) { + SuggestedWords.Builder builder = entries.getAlternatives(suggest, keyboardSwitcher); builder.setTypedWordValid(false).setHasMinimalSuggestion(false); - mService.showSuggestions(builder.build(), alternatives.getOriginalWord()); + mService.showSuggestions(builder.build(), entries.getOriginalWord()); } - public void setRecorrectionSuggestions(VoiceProxy voiceProxy, CandidateView candidateView, - Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word, - boolean hasUncommittedTypedChars, int lastSelectionStart, int lastSelectionEnd, - String wordSeparators) { + public void fetchAndDisplayRecorrectionSuggestions(VoiceProxy voiceProxy, + CandidateView candidateView, Suggest suggest, KeyboardSwitcher keyboardSwitcher, + WordComposer word, boolean hasUncommittedTypedChars, int lastSelectionStart, + int lastSelectionEnd, String wordSeparators) { if (!InputConnectionCompatUtils.RECORRECTION_SUPPORTED) return; + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED || !mRecorrectionEnabled) return; voiceProxy.setShowingVoiceSuggestions(false); if (candidateView != null && candidateView.isShowingAddToDictionaryHint()) { return; @@ -237,7 +249,7 @@ public class Recorrection { ic.endBatchEdit(); } else { abortRecorrection(true); - mService.setPunctuationSuggestions(); // Show the punctuation suggestions list + mService.updateBigramPredictions(); } } else { abortRecorrection(true); @@ -245,6 +257,7 @@ public class Recorrection { } public void abortRecorrection(boolean force) { + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) return; if (force || TextEntryState.isRecorrecting()) { TextEntryState.onAbortRecorrection(); mService.setCandidatesViewShown(mService.isCandidateStripVisible()); @@ -252,4 +265,23 @@ public class Recorrection { mService.clearSuggestions(); } } + + public void updateRecorrectionEnabled(Resources res, SharedPreferences prefs) { + // If the option should not be shown, do not read the re-correction preference + // but always use the default setting defined in the resources. + if (res.getBoolean(R.bool.config_enable_show_recorrection_option)) { + mRecorrectionEnabled = prefs.getBoolean(Settings.PREF_RECORRECTION_ENABLED, + res.getBoolean(R.bool.config_default_recorrection_enabled)); + } else { + mRecorrectionEnabled = res.getBoolean(R.bool.config_default_recorrection_enabled); + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { + if (SuggestionSpanUtils.SUGGESTION_SPAN_IS_SUPPORTED) return; + if (key.equals(Settings.PREF_RECORRECTION_ENABLED)) { + updateRecorrectionEnabled(mService.getResources(), prefs); + } + } } diff --git a/java/src/com/android/inputmethod/latin/WordAlternatives.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java index 0e9914400..5e6c87044 100644 --- a/java/src/com/android/inputmethod/latin/WordAlternatives.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java @@ -14,17 +14,20 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.deprecated.recorrection; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.Suggest; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.WordComposer; import android.text.TextUtils; -public class WordAlternatives { +public class RecorrectionSuggestionEntries { public final CharSequence mChosenWord; public final WordComposer mWordComposer; - public WordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { + public RecorrectionSuggestionEntries(CharSequence chosenWord, WordComposer wordComposer) { mChosenWord = chosenWord; mWordComposer = wordComposer; } @@ -54,6 +57,6 @@ public class WordAlternatives { private static SuggestedWords.Builder getTypedSuggestions( Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) { - return suggest.getSuggestedWordBuilder(keyboardSwitcher.getInputView(), word, null); + return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null); } -}
\ No newline at end of file +} diff --git a/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java index 0ef73d2d7..3c79cc218 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java +++ b/java/src/com/android/inputmethod/deprecated/voice/FieldContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/Hints.java b/java/src/com/android/inputmethod/deprecated/voice/Hints.java index 52a4f4e58..06b234381 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/Hints.java +++ b/java/src/com/android/inputmethod/deprecated/voice/Hints.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java index b57c16f40..dcb826e8f 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java +++ b/java/src/com/android/inputmethod/deprecated/voice/RecognitionView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java index 7721fe268..855a09a1d 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java +++ b/java/src/com/android/inputmethod/deprecated/voice/SettingsUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java index 8cc79de1e..25b314085 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java +++ b/java/src/com/android/inputmethod/deprecated/voice/SoundIndicator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. + * 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java index 7ee0de9c9..2dba01432 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java +++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInput.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java index 87b943426..22e8207bf 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java +++ b/java/src/com/android/inputmethod/deprecated/voice/VoiceInputLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Google Inc. + * Copyright (C) 2008 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java index a3025f252..8ed279f42 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java +++ b/java/src/com/android/inputmethod/deprecated/voice/WaveformImage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2009 Google Inc. + * Copyright (C) 2008-2009 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 diff --git a/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java index 310689cb2..6c5f52ae2 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java +++ b/java/src/com/android/inputmethod/deprecated/voice/Whitelist.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 5c59d4441..cb529461a 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -101,6 +101,10 @@ public class Key { /** Key is enabled and responds on press */ public boolean mEnabled = true; + // keyWidth constants + private static final int KEYWIDTH_FILL_RIGHT = 0; + private static final int KEYWIDTH_FILL_BOTH = -1; + private final static int[] KEY_STATE_NORMAL_ON = { android.R.attr.state_checkable, android.R.attr.state_checked @@ -140,12 +144,12 @@ public class Key { }; /** - * This constructor is being used only for key in mini popup keyboard. + * This constructor is being used only for key in popup mini keyboard. */ public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y, - int width, int edgeFlags) { + int width, int height, int edgeFlags) { mKeyboard = keyboard; - mHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); + mHeight = height - keyboard.getVerticalGap(); mGap = keyboard.getHorizontalGap(); mVisualInsetsLeft = mVisualInsetsRight = 0; mWidth = width - mGap; @@ -178,6 +182,7 @@ public class Key { * @param x the x coordinate of the top-left * @param y the y coordinate of the top-left * @param parser the XML parser containing the attributes for this key + * @param keyStyles active key styles set */ public Key(Resources res, Row row, int x, int y, XmlResourceParser parser, KeyStyles keyStyles) { @@ -185,6 +190,7 @@ public class Key { final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); + int keyWidth; try { mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_rowHeight, @@ -192,17 +198,13 @@ public class Key { mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_horizontalGap, mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap); - mWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, + keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth, - mKeyboard.getDisplayWidth(), row.mDefaultWidth) - mGap; + mKeyboard.getDisplayWidth(), row.mDefaultWidth); } finally { keyboardAttr.recycle(); } - // Horizontal gap is divided equally to both sides of the key. - mX = x + mGap / 2; - mY = y; - final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); try { @@ -216,6 +218,35 @@ public class Key { style = keyStyles.getEmptyKeyStyle(); } + final int keyboardWidth = mKeyboard.getDisplayWidth(); + int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x); + if (keyXPos < 0) { + // If keyXPos is negative, the actual x-coordinate will be k + keyXPos. + keyXPos += keyboardWidth; + if (keyXPos < x) { + // keyXPos shouldn't be less than x because drawable area for this key starts + // at x. Or, this key will overlaps the adjacent key on its left hand side. + keyXPos = x; + } + } + if (keyWidth == KEYWIDTH_FILL_RIGHT) { + // If keyWidth is zero, the actual key width will be determined to fill out the + // area up to the right edge of the keyboard. + keyWidth = keyboardWidth - keyXPos; + } else if (keyWidth <= KEYWIDTH_FILL_BOTH) { + // If keyWidth is negative, the actual key width will be determined to fill out the + // area between the nearest key on the left hand side and the right edge of the + // keyboard. + keyXPos = x; + keyWidth = keyboardWidth - keyXPos; + } + + // Horizontal gap is divided equally to both sides of the key. + mX = keyXPos + mGap / 2; + mY = y; + mWidth = keyWidth - mGap; + final CharSequence[] popupCharacters = style.getTextArray(keyAttr, R.styleable.Keyboard_Key_popupCharacters); if (res.getBoolean(R.bool.config_digit_popup_characters_enabled)) { @@ -275,7 +306,7 @@ public class Key { } private static boolean isDigitPopupCharacter(CharSequence label) { - return label.length() == 1 && Character.isDigit(label.charAt(0)); + return label != null && label.length() == 1 && Character.isDigit(label.charAt(0)); } private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 0b13afecb..7add43a6d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/KeyStyles.java index d464c2029..d53df788f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/KeyStyles.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 492883caf..267abccb3 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -151,16 +151,11 @@ public class Keyboard { * @param context the application or service context * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. * @param id keyboard identifier + * @param width keyboard width */ - public Keyboard(Context context, int xmlLayoutResId, KeyboardId id) { - this(context, xmlLayoutResId, id, - context.getResources().getDisplayMetrics().widthPixels, - context.getResources().getDisplayMetrics().heightPixels); - } - private Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width, - int height) { - Resources res = context.getResources(); + public Keyboard(Context context, int xmlLayoutResId, KeyboardId id, int width) { + final Resources res = context.getResources(); GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); GRID_SIZE = GRID_WIDTH * GRID_HEIGHT; @@ -168,7 +163,8 @@ public class Keyboard { final int horizontalEdgesPadding = (int)res.getDimension( R.dimen.keyboard_horizontal_edges_padding); mDisplayWidth = width - horizontalEdgesPadding * 2; - mDisplayHeight = height; + // TODO: Adjust the height by referring to the height of area available for drawing as well. + mDisplayHeight = res.getDisplayMetrics().heightPixels; mDefaultHorizontalGap = 0; setKeyWidth(mDisplayWidth / 10); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index 098af214e..7e67d6f6b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index f68b68f1d..7c03ec71e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -34,35 +34,40 @@ public class KeyboardId { public static final int MODE_URL = 1; public static final int MODE_EMAIL = 2; public static final int MODE_IM = 3; - public static final int MODE_WEB = 4; - public static final int MODE_PHONE = 5; - public static final int MODE_NUMBER = 6; + public static final int MODE_PHONE = 4; + public static final int MODE_NUMBER = 5; public final Locale mLocale; public final int mOrientation; + public final int mWidth; public final int mMode; public final int mXmlId; public final int mColorScheme; + public final boolean mWebInput; public final boolean mPasswordInput; public final boolean mHasSettingsKey; public final boolean mVoiceKeyEnabled; public final boolean mHasVoiceKey; public final int mImeAction; public final boolean mEnableShiftLock; + public final String mXmlName; + public final EditorInfo mAttribute; private final int mHashCode; public KeyboardId(String xmlName, int xmlId, int colorScheme, Locale locale, int orientation, - int mode, EditorInfo attribute, boolean hasSettingsKey, boolean voiceKeyEnabled, - boolean hasVoiceKey, boolean enableShiftLock) { + int width, int mode, EditorInfo attribute, boolean hasSettingsKey, + boolean voiceKeyEnabled, boolean hasVoiceKey, boolean enableShiftLock) { final int inputType = (attribute != null) ? attribute.inputType : 0; final int imeOptions = (attribute != null) ? attribute.imeOptions : 0; this.mLocale = locale; this.mOrientation = orientation; + this.mWidth = width; this.mMode = mode; this.mXmlId = xmlId; this.mColorScheme = colorScheme; + this.mWebInput = InputTypeCompatUtils.isWebInputType(inputType); this.mPasswordInput = InputTypeCompatUtils.isPasswordInputType(inputType) || InputTypeCompatUtils.isVisiblePasswordInputType(inputType); this.mHasSettingsKey = hasSettingsKey; @@ -73,14 +78,18 @@ public class KeyboardId { this.mImeAction = imeOptions & ( EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); this.mEnableShiftLock = enableShiftLock; + this.mXmlName = xmlName; + this.mAttribute = attribute; this.mHashCode = Arrays.hashCode(new Object[] { locale, orientation, + width, mode, xmlId, colorScheme, + mWebInput, mPasswordInput, hasSettingsKey, voiceKeyEnabled, @@ -90,6 +99,18 @@ public class KeyboardId { }); } + public KeyboardId cloneWithNewLayout(String xmlName, int xmlId) { + return new KeyboardId(xmlName, xmlId, mColorScheme, mLocale, mOrientation, mWidth, mMode, + mAttribute, mHasSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, mEnableShiftLock); + } + + public KeyboardId cloneWithNewGeometry(int width) { + if (mWidth == width) + return this; + return new KeyboardId(mXmlName, mXmlId, mColorScheme, mLocale, mOrientation, width, mMode, + mAttribute, mHasSettingsKey, mVoiceKeyEnabled, mHasVoiceKey, mEnableShiftLock); + } + public int getXmlId() { return mXmlId; } @@ -118,9 +139,11 @@ public class KeyboardId { boolean equals(KeyboardId other) { return other.mLocale.equals(this.mLocale) && other.mOrientation == this.mOrientation + && other.mWidth == this.mWidth && other.mMode == this.mMode && other.mXmlId == this.mXmlId && other.mColorScheme == this.mColorScheme + && other.mWebInput == this.mWebInput && other.mPasswordInput == this.mPasswordInput && other.mHasSettingsKey == this.mHasSettingsKey && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled @@ -136,18 +159,19 @@ public class KeyboardId { @Override public String toString() { - return String.format("[%s.xml %s %s %s imeAction=%s %s%s%s%s%s%s]", + return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s]", mXmlName, mLocale, - (mOrientation == 1 ? "port" : "land"), + (mOrientation == 1 ? "port" : "land"), mWidth, modeName(mMode), EditorInfoCompatUtils.imeOptionsName(mImeAction), + colorSchemeName(mColorScheme), + (mWebInput ? " webInput" : ""), (mPasswordInput ? " passwordInput" : ""), (mHasSettingsKey ? " hasSettingsKey" : ""), (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""), (mHasVoiceKey ? " hasVoiceKey" : ""), - (mEnableShiftLock ? " enableShiftLock" : ""), - colorSchemeName(mColorScheme) + (mEnableShiftLock ? " enableShiftLock" : "") ); } @@ -157,7 +181,6 @@ public class KeyboardId { case MODE_URL: return "url"; case MODE_EMAIL: return "email"; case MODE_IM: return "im"; - case MODE_WEB: return "web"; case MODE_PHONE: return "phone"; case MODE_NUMBER: return "number"; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java index 69ae7886a..c2db62a1c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -196,9 +196,20 @@ public class KeyboardParser { final int keyboardHeight = (int)keyboardAttr.getDimension( R.styleable.Keyboard_keyboardHeight, displayHeight / 2); final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2); - // Keyboard height will not exceed maxKeyboardHeight. - final int height = Math.min(keyboardHeight, maxKeyboardHeight); + R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2); + int minKeyboardHeight = getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2); + if (minKeyboardHeight < 0) { + // Specified fraction was negative, so it should be calculated against display + // width. + final int displayWidth = keyboard.getDisplayWidth(); + minKeyboardHeight = -getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2); + } + // Keyboard height will not exceed maxKeyboardHeight and will not be less than + // minKeyboardHeight. + final int height = Math.max( + Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight); final int width = keyboard.getDisplayWidth(); keyboard.setKeyboardHeight(height); @@ -269,7 +280,7 @@ public class KeyboardParser { if (TAG_KEY.equals(tag)) { parseKey(parser, row, keys); } else if (TAG_SPACER.equals(tag)) { - parseSpacer(parser, keys); + parseSpacer(parser, row, keys); } else if (TAG_INCLUDE.equals(tag)) { parseIncludeRowContent(parser, row, keys); } else if (TAG_SWITCH.equals(tag)) { @@ -316,19 +327,32 @@ public class KeyboardParser { } } - private void parseSpacer(XmlResourceParser parser, List<Key> keys) + private void parseSpacer(XmlResourceParser parser, Row row, List<Key> keys) throws XmlPullParserException, IOException { if (keys == null) { checkEndTag(TAG_SPACER, parser); } else { if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + final TypedArray keyboardAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); - final int gap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap, - mKeyboard.getDisplayWidth(), 0); - a.recycle(); + if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) + throw new IllegalAttribute(parser, "horizontalGap"); + final int defaultWidth = (row != null) ? row.mDefaultWidth : 0; + final int keyWidth = getDimensionOrFraction(keyboardAttr, R.styleable.Keyboard_keyWidth, + mKeyboard.getDisplayWidth(), defaultWidth); + keyboardAttr.recycle(); + + final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Key); + int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyXPos, mKeyboard.getDisplayWidth(), mCurrentX); + if (keyXPos < 0) { + // If keyXPos is negative, the actual x-coordinate will be display_width + keyXPos. + keyXPos += mKeyboard.getDisplayWidth(); + } + checkEndTag(TAG_SPACER, parser); - setSpacer(gap); + setSpacer(keyXPos, keyWidth); } } @@ -443,8 +467,10 @@ public class KeyboardParser { final TypedArray viewAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.KeyboardView); try { - final boolean modeMatched = matchInteger(a, - R.styleable.Keyboard_Case_mode, id.mMode); + final boolean modeMatched = matchTypedValue(a, + R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); + final boolean webInputMatched = matchBoolean(a, + R.styleable.Keyboard_Case_webInput, id.mWebInput); final boolean passwordInputMatched = matchBoolean(a, R.styleable.Keyboard_Case_passwordInput, id.mPasswordInput); final boolean settingsKeyMatched = matchBoolean(a, @@ -461,26 +487,30 @@ public class KeyboardParser { // this attribute with id.mImeOptions as integer value is enough for our purpose. final boolean imeActionMatched = matchInteger(a, R.styleable.Keyboard_Case_imeAction, id.mImeAction); + final boolean localeCodeMatched = matchString(a, + R.styleable.Keyboard_Case_localeCode, id.mLocale.toString()); final boolean languageCodeMatched = matchString(a, R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); final boolean countryCodeMatched = matchString(a, R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); - final boolean selected = modeMatched && passwordInputMatched && settingsKeyMatched - && voiceEnabledMatched && voiceKeyMatched && colorSchemeMatched - && imeActionMatched && languageCodeMatched && countryCodeMatched; + final boolean selected = modeMatched && webInputMatched && passwordInputMatched + && settingsKeyMatched && voiceEnabledMatched && voiceKeyMatched + && colorSchemeMatched && imeActionMatched && localeCodeMatched + && languageCodeMatched && countryCodeMatched; - if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, - textAttr(KeyboardId.modeName( - a.getInt(R.styleable.Keyboard_Case_mode, -1)), "mode"), + if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, + textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), textAttr(KeyboardId.colorSchemeName( viewAttr.getInt( - R.styleable.KeyboardView_colorScheme, -1)), "colorSchemeName"), + R.styleable.KeyboardView_colorScheme, -1)), "colorScheme"), + booleanAttr(a, R.styleable.Keyboard_Case_webInput, "webInput"), booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"), booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"), booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"), textAttr(EditorInfoCompatUtils.imeOptionsName( a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), + textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), "languageCode"), textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"), Boolean.toString(selected))); @@ -507,7 +537,30 @@ public class KeyboardParser { private static boolean matchString(TypedArray a, int index, String value) { // If <case> does not have "index" attribute, that means this <case> is wild-card for the // attribute. - return !a.hasValue(index) || a.getString(index).equals(value); + return !a.hasValue(index) || stringArrayContains(a.getString(index).split("\\|"), value); + } + + private static boolean matchTypedValue(TypedArray a, int index, int intValue, String strValue) { + // If <case> does not have "index" attribute, that means this <case> is wild-card for the + // attribute. + final TypedValue v = a.peekValue(index); + if (v == null) + return true; + + if (isIntegerValue(v)) { + return intValue == a.getInt(index, 0); + } else if (isStringValue(v)) { + return stringArrayContains(a.getString(index).split("\\|"), strValue); + } + return false; + } + + private static boolean stringArrayContains(String[] array, String value) { + for (final String elem : array) { + if (elem.equals(value)) + return true; + } + return false; } private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys) @@ -551,14 +604,14 @@ public class KeyboardParser { private void startRow(Row row) { mCurrentX = 0; - setSpacer(mHorizontalEdgesPadding); + setSpacer(mCurrentX, mHorizontalEdgesPadding); mCurrentRow = row; } private void endRow() { if (mCurrentRow == null) throw new InflateException("orphant end row tag"); - setSpacer(mHorizontalEdgesPadding); + setSpacer(mCurrentX, mHorizontalEdgesPadding); if (mCurrentX > mMaxRowWidth) mMaxRowWidth = mCurrentX; mCurrentY += mCurrentRow.mDefaultHeight; @@ -566,7 +619,7 @@ public class KeyboardParser { } private void endKey(Key key) { - mCurrentX += key.mGap + key.mWidth; + mCurrentX = key.mX - key.mGap / 2 + key.mWidth + key.mGap; } private void endKeyboard(int defaultVerticalGap) { @@ -574,23 +627,42 @@ public class KeyboardParser { mTotalHeight = mCurrentY - defaultVerticalGap; } - private void setSpacer(int gap) { - mCurrentX += gap; + private void setSpacer(int keyXPos, int width) { + mCurrentX = keyXPos + width; } public static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) { final TypedValue value = a.peekValue(index); if (value == null) return defValue; - if (value.type == TypedValue.TYPE_DIMENSION) { - return a.getDimensionPixelOffset(index, defValue); - } else if (value.type == TypedValue.TYPE_FRACTION) { + if (isFractionValue(value)) { // Round it to avoid values like 47.9999 from getting truncated return Math.round(a.getFraction(index, base, base, defValue)); + } else if (isDimensionValue(value)) { + return a.getDimensionPixelOffset(index, defValue); + } else if (isIntegerValue(value)) { + // For enum value. + return a.getInt(index, defValue); } return defValue; } + private static boolean isFractionValue(TypedValue v) { + return v.type == TypedValue.TYPE_FRACTION; + } + + private static boolean isDimensionValue(TypedValue v) { + return v.type == TypedValue.TYPE_DIMENSION; + } + + private static boolean isIntegerValue(TypedValue v) { + return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT; + } + + private static boolean isStringValue(TypedValue v) { + return v.type == TypedValue.TYPE_STRING; + } + @SuppressWarnings("serial") public static class ParseException extends InflateException { public ParseException(String msg, XmlResourceParser parser) { @@ -613,6 +685,13 @@ public class KeyboardParser { } @SuppressWarnings("serial") + private static class IllegalAttribute extends ParseException { + public IllegalAttribute(XmlResourceParser parser, String attribute) { + super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser); + } + } + + @SuppressWarnings("serial") private static class NonEmptyTag extends ParseException { public NonEmptyTag(String tag, XmlResourceParser parser) { super(tag + " must be empty tag", parser); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java b/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java index d5412791d..e015b5158 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardShiftState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 333fbc779..2512118d4 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -16,6 +16,16 @@ package com.android.inputmethod.keyboard; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.util.Log; +import android.view.ContextThemeWrapper; +import android.view.InflateException; +import android.view.LayoutInflater; +import android.view.View; +import android.view.inputmethod.EditorInfo; + import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; @@ -24,37 +34,30 @@ import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.util.Log; -import android.view.InflateException; -import android.view.inputmethod.EditorInfo; - import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Locale; public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = "KeyboardSwitcher"; - private static final boolean DEBUG = false; + private static final String TAG = KeyboardSwitcher.class.getSimpleName(); + private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; public static final boolean DEBUG_STATE = false; private static String sConfigDefaultKeyboardThemeId; public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902"; private static final int[] KEYBOARD_THEMES = { - R.layout.input_basic, - R.layout.input_basic_highcontrast, - R.layout.input_stone_normal, - R.layout.input_stone_bold, - R.layout.input_gingerbread, - R.layout.input_honeycomb, + R.style.KeyboardTheme, + R.style.KeyboardTheme_HighContrast, + R.style.KeyboardTheme_Stone, + R.style.KeyboardTheme_Stone_Bold, + R.style.KeyboardTheme_Gingerbread, + R.style.KeyboardTheme_Honeycomb, }; private SubtypeSwitcher mSubtypeSwitcher; private SharedPreferences mPrefs; - private LatinKeyboardView mInputView; + private LatinKeyboardView mKeyboardView; private LatinIME mInputMethodService; // TODO: Combine these key state objects with auto mode switch state. @@ -98,7 +101,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // Default is SETTINGS_KEY_MODE_AUTO. private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO; - private int mLayoutId; + private int mThemeIndex; + private int mKeyboardWidth; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); @@ -118,11 +122,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha try { sConfigDefaultKeyboardThemeId = ims.getString( R.string.config_default_keyboard_theme_id); - sInstance.mLayoutId = Integer.valueOf( + sInstance.mThemeIndex = Integer.valueOf( prefs.getString(PREF_KEYBOARD_LAYOUT, sConfigDefaultKeyboardThemeId)); } catch (NumberFormatException e) { sConfigDefaultKeyboardThemeId = "0"; - sInstance.mLayoutId = 0; + sInstance.mThemeIndex = 0; } prefs.registerOnSharedPreferenceChangeListener(sInstance); } @@ -142,7 +146,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled, boolean voiceButtonOnPrimary, boolean isSymbols) { - if (mInputView == null) return; + if (mKeyboardView == null) return; mAttribute = attribute; mVoiceKeyEnabled = voiceButtonEnabled; @@ -151,15 +155,39 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // Update the settings key state because number of enabled IMEs could have been changed mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService); final KeyboardId id = getKeyboardId(attribute, isSymbols); - makeSymbolsKeyboardIds(id.mMode, attribute); - mCurrentId = id; - mInputView.setKeyPreviewEnabled(mInputMethodService.getPopupOn()); + + // Note: This comment is only applied for phone number keyboard layout. + // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch + // between "phone keyboard" and "phone symbols keyboard". But on xlarge device, + // "@integer/key_shift" key code is used for that purpose in order to properly display + // "more" and "locked more" key labels. To achieve these behavior, we should initialize + // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" + // respectively here for xlarge device's layout switching. + mSymbolsId = makeSiblingKeyboardId(id, R.xml.kbd_symbols, R.xml.kbd_phone); + mSymbolsShiftedId = makeSiblingKeyboardId( + id, R.xml.kbd_symbols_shift, R.xml.kbd_phone_symbols); + setKeyboard(getKeyboard(id)); } + public void onSizeChanged() { + final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth(); + if (width == 0) + return; + mKeyboardWidth = width; + // Set keyboard with new width. + final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width); + setKeyboard(getKeyboard(newId)); + } + private void setKeyboard(final Keyboard newKeyboard) { - final Keyboard oldKeyboard = mInputView.getKeyboard(); - mInputView.setKeyboard(newKeyboard); + final Keyboard oldKeyboard = mKeyboardView.getKeyboard(); + mKeyboardView.setKeyboard(newKeyboard); + mCurrentId = newKeyboard.mId; + final Resources res = mInputMethodService.getResources(); + mKeyboardView.setKeyPreviewPopupEnabled( + Settings.Values.isKeyPreviewPopupEnabled(mPrefs, res), + Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, res)); final boolean localeChanged = (oldKeyboard == null) || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale); mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged); @@ -173,19 +201,19 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha final Locale savedLocale = Utils.setSystemLocale(res, mSubtypeSwitcher.getInputLocale()); - keyboard = new LatinKeyboard(mInputMethodService, id); + keyboard = new LatinKeyboard(mInputMethodService, id, id.mWidth); if (id.mEnableShiftLock) { keyboard.enableShiftLock(); } mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); - if (DEBUG) + if (DEBUG_CACHE) Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " + ((ref == null) ? "LOAD" : "GCed") + " id=" + id); Utils.setSystemLocale(res, savedLocale); - } else if (DEBUG) { + } else if (DEBUG_CACHE) { Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id); } @@ -242,33 +270,19 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha final boolean hasSettingsKey = hasSettingsKey(attribute); final Resources res = mInputMethodService.getResources(); final int orientation = res.getConfiguration().orientation; + if (mKeyboardWidth == 0) + mKeyboardWidth = res.getDisplayMetrics().widthPixels; final Locale locale = mSubtypeSwitcher.getInputLocale(); return new KeyboardId( - res.getResourceEntryName(xmlId), xmlId, charColorId, locale, orientation, mode, - attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, enableShiftLock); + res.getResourceEntryName(xmlId), xmlId, charColorId, locale, orientation, + mKeyboardWidth, mode, attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, + enableShiftLock); } - private void makeSymbolsKeyboardIds(final int mode, EditorInfo attribute) { - final Locale locale = mSubtypeSwitcher.getInputLocale(); - final Resources res = mInputMethodService.getResources(); - final int orientation = res.getConfiguration().orientation; - final int colorScheme = getColorScheme(); - final boolean hasVoiceKey = mVoiceKeyEnabled && !mVoiceButtonOnPrimary; - final boolean hasSettingsKey = hasSettingsKey(attribute); - // Note: This comment is only applied for phone number keyboard layout. - // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch - // between "phone keyboard" and "phone symbols keyboard". But on xlarge device, - // "@integer/key_shift" key code is used for that purpose in order to properly display - // "more" and "locked more" key labels. To achieve these behavior, we should initialize - // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" - // respectively here for xlarge device's layout switching. - int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols; - final String xmlName = res.getResourceEntryName(xmlId); - mSymbolsId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode, - attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, false); - xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift; - mSymbolsShiftedId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode, - attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, false); + private KeyboardId makeSiblingKeyboardId(KeyboardId base, int alphabet, int phone) { + final int xmlId = base.mMode == KeyboardId.MODE_PHONE ? phone : alphabet; + final String xmlName = mInputMethodService.getResources().getResourceEntryName(xmlId); + return base.cloneWithNewLayout(xmlName, xmlId); } public int getKeyboardMode() { @@ -280,18 +294,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public boolean isInputViewShown() { - return mInputView != null && mInputView.isShown(); + return mKeyboardView != null && mKeyboardView.isShown(); } public boolean isKeyboardAvailable() { - if (mInputView != null) - return mInputView.getKeyboard() != null; + if (mKeyboardView != null) + return mKeyboardView.getKeyboard() != null; return false; } public LatinKeyboard getLatinKeyboard() { - if (mInputView != null) { - final Keyboard keyboard = mInputView.getKeyboard(); + if (mKeyboardView != null) { + final Keyboard keyboard = mKeyboardView.getKeyboard(); if (keyboard instanceof LatinKeyboard) return (LatinKeyboard)keyboard; } @@ -344,7 +358,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha latinKeyboard.setShiftLocked(false); } if (latinKeyboard.setShifted(shifted)) { - mInputView.invalidateAllKeys(); + mKeyboardView.invalidateAllKeys(); } } } @@ -352,7 +366,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private void setShiftLocked(boolean shiftLocked) { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) { - mInputView.invalidateAllKeys(); + mKeyboardView.invalidateAllKeys(); } } @@ -394,7 +408,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null) { latinKeyboard.setAutomaticTemporaryUpperCase(); - mInputView.invalidateAllKeys(); + mKeyboardView.invalidateAllKeys(); } } @@ -489,7 +503,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // To be able to turn off caps lock by "double tap" on shift key, we should ignore // the second tap of the "double tap" from now for a while because we just have // already turned off caps lock above. - mInputView.startIgnoringDoubleTap(); + mKeyboardView.startIgnoringDoubleTap(); } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted() && !withSliding) { // Shift has been pressed without chording while shifted state. @@ -559,14 +573,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return; final LatinKeyboard keyboard; if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) { - mCurrentId = mSymbolsShiftedId; - keyboard = getKeyboard(mCurrentId); + keyboard = getKeyboard(mSymbolsShiftedId); // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To // enable the indicator, we need to call setShiftLocked(true). keyboard.setShiftLocked(true); } else { - mCurrentId = mSymbolsId; - keyboard = getKeyboard(mCurrentId); + keyboard = getKeyboard(mSymbolsId); // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the // indicator, we need to call setShiftLocked(false). keyboard.setShiftLocked(false); @@ -580,11 +592,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public boolean isVibrateAndSoundFeedbackRequired() { - return mInputView == null || !mInputView.isInSlidingKeyInput(); + return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput(); } private int getPointerCount() { - return mInputView == null ? 0 : mInputView.getPointerCount(); + return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount(); } private void toggleKeyboardMode() { @@ -597,7 +609,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public boolean hasDistinctMultitouch() { - return mInputView != null && mInputView.hasDistinctMultitouch(); + return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch(); } private static boolean isSpaceCharacter(int c) { @@ -694,53 +706,58 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - public LatinKeyboardView getInputView() { - return mInputView; + public LatinKeyboardView getKeyboardView() { + return mKeyboardView; } - public LatinKeyboardView onCreateInputView() { - createInputViewInternal(mLayoutId, true); - return mInputView; + public View onCreateInputView() { + return createInputView(mThemeIndex, true); } - private void createInputViewInternal(int newLayout, boolean forceReset) { - int layoutId = newLayout; - if (mLayoutId != layoutId || mInputView == null || forceReset) { - if (mInputView != null) { - mInputView.closing(); - } - if (KEYBOARD_THEMES.length <= layoutId) { - layoutId = Integer.valueOf(sConfigDefaultKeyboardThemeId); - } + // Instance variable only for {@link #createInputView(int, boolean)}. + private View mCurrentInputView; - Utils.GCUtils.getInstance().reset(); - boolean tryGC = true; - for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater( - ).inflate(KEYBOARD_THEMES[layoutId], null); - tryGC = false; - } catch (OutOfMemoryError e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait( - mLayoutId + "," + layoutId, e); - } catch (InflateException e) { - Log.w(TAG, "load keyboard failed: " + e); - tryGC = Utils.GCUtils.getInstance().tryGCOrWait( - mLayoutId + "," + layoutId, e); - } + private View createInputView(final int newThemeIndex, final boolean forceRecreate) { + if (mCurrentInputView != null && mThemeIndex == newThemeIndex && !forceRecreate) + return mCurrentInputView; + + if (mKeyboardView != null) { + mKeyboardView.closing(); + } + final int themeIndex = (newThemeIndex < KEYBOARD_THEMES.length) ? newThemeIndex + : Integer.valueOf(sConfigDefaultKeyboardThemeId); + + Utils.GCUtils.getInstance().reset(); + boolean tryGC = true; + for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { + try { + final Context themeContext = new ContextThemeWrapper(mInputMethodService, + KEYBOARD_THEMES[themeIndex]); + mCurrentInputView = LayoutInflater.from(themeContext).inflate( + R.layout.input_view, null); + tryGC = false; + } catch (OutOfMemoryError e) { + Log.w(TAG, "load keyboard failed: " + e); + tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mThemeIndex + "," + themeIndex, e); + } catch (InflateException e) { + Log.w(TAG, "load keyboard failed: " + e); + tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mThemeIndex + "," + themeIndex, e); } - mInputView.setOnKeyboardActionListener(mInputMethodService); - mLayoutId = layoutId; } + + mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById( + R.id.latin_keyboard_view); + mKeyboardView.setOnKeyboardActionListener(mInputMethodService); + mThemeIndex = themeIndex; + return mCurrentInputView; } - private void postSetInputView() { + private void postSetInputView(final View newInputView) { mInputMethodService.mHandler.post(new Runnable() { @Override public void run() { - if (mInputView != null) { - mInputMethodService.setInputView(mInputView); + if (newInputView != null) { + mInputMethodService.setInputView(newInputView); } mInputMethodService.updateInputViewShown(); } @@ -752,24 +769,22 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (PREF_KEYBOARD_LAYOUT.equals(key)) { final int layoutId = Integer.valueOf( sharedPreferences.getString(key, sConfigDefaultKeyboardThemeId)); - createInputViewInternal(layoutId, false); - postSetInputView(); + postSetInputView(createInputView(layoutId, false)); } else if (Settings.PREF_SETTINGS_KEY.equals(key)) { mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences, mInputMethodService); - createInputViewInternal(mLayoutId, true); - postSetInputView(); + postSetInputView(createInputView(mThemeIndex, true)); } } private int getColorScheme() { - return (mInputView != null) - ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE; + return (mKeyboardView != null) + ? mKeyboardView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE; } public void onAutoCorrectionStateChanged(boolean isAutoCorrection) { if (isAutoCorrection != mIsAutoCorrectionActive) { - LatinKeyboardView keyboardView = getInputView(); + LatinKeyboardView keyboardView = getKeyboardView(); mIsAutoCorrectionActive = isAutoCorrection; keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard()) .onAutoCorrectionStateChanged(isAutoCorrection)); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index c551ed49f..f8bce40b1 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -58,14 +58,22 @@ import java.util.WeakHashMap; * A view that renders a virtual {@link Keyboard}. It handles rendering of keys and detecting key * presses and touch movements. * + * @attr ref R.styleable#KeyboardView_backgroundDimAmount + * @attr ref R.styleable#KeyboardView_colorScheme * @attr ref R.styleable#KeyboardView_keyBackground + * @attr ref R.styleable#KeyboardView_keyHysteresisDistance + * @attr ref R.styleable#KeyboardView_keyLetterRatio + * @attr ref R.styleable#KeyboardView_keyLetterStyle * @attr ref R.styleable#KeyboardView_keyPreviewLayout * @attr ref R.styleable#KeyboardView_keyPreviewOffset - * @attr ref R.styleable#KeyboardView_labelTextSize - * @attr ref R.styleable#KeyboardView_keyTextSize + * @attr ref R.styleable#KeyboardView_keyPreviewHeight * @attr ref R.styleable#KeyboardView_keyTextColor + * @attr ref R.styleable#KeyboardView_keyTextColorDisabled + * @attr ref R.styleable#KeyboardView_labelTextRatio * @attr ref R.styleable#KeyboardView_verticalCorrection * @attr ref R.styleable#KeyboardView_popupLayout + * @attr ref R.styleable#KeyboardView_shadowColor + * @attr ref R.styleable#KeyboardView_shadowRadius */ public class KeyboardView extends View implements PointerTracker.UIProxy { private static final String TAG = KeyboardView.class.getSimpleName(); @@ -86,33 +94,36 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { private static final int HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL = -1; // XML attribute - private int mKeyLetterSize; - private int mKeyTextColor; - private int mKeyTextColorDisabled; - private Typeface mKeyLetterStyle = Typeface.DEFAULT; - private int mLabelTextSize; - private int mColorScheme = COLOR_SCHEME_WHITE; - private int mShadowColor; - private float mShadowRadius; - private Drawable mKeyBackground; - private float mBackgroundDimAmount; - private float mKeyHysteresisDistance; - private float mVerticalCorrection; - private int mPreviewOffset; - private int mPreviewHeight; - private int mPopupLayout; + private final float mKeyLetterRatio; + private final int mKeyTextColor; + private final int mKeyTextColorDisabled; + private final Typeface mKeyLetterStyle; + private final float mLabelTextRatio; + private final int mColorScheme; + private final int mShadowColor; + private final float mShadowRadius; + private final Drawable mKeyBackground; + private final float mBackgroundDimAmount; + private final float mKeyHysteresisDistance; + private final float mVerticalCorrection; + private final int mPreviewOffset; + private final int mPreviewHeight; + private final int mPopupLayout; // Main keyboard private Keyboard mKeyboard; + private int mKeyLetterSize; + private int mLabelTextSize; // Key preview private boolean mInForeground; private TextView mPreviewText; - private int mPreviewTextSizeLarge; - private boolean mShowKeyPreview = true; - private int mKeyPreviewDisplayedY; + private float mPreviewTextRatio; + private int mPreviewTextSize; + private boolean mShowKeyPreviewPopup = true; + private int mKeyPreviewPopupDisplayedY = -1; private final int mDelayBeforePreview; - private final int mDelayAfterPreview; + private int mDelayAfterPreview; private ViewGroup mPreviewPlacer; private final int[] mCoordinates = new int[2]; @@ -300,74 +311,36 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView); - int previewLayout = 0; - int keyTextSize = 0; - int n = a.getIndexCount(); - - for (int i = 0; i < n; i++) { - int attr = a.getIndex(i); - - switch (attr) { - case R.styleable.KeyboardView_keyBackground: - mKeyBackground = a.getDrawable(attr); - break; - case R.styleable.KeyboardView_keyHysteresisDistance: - mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.KeyboardView_verticalCorrection: - mVerticalCorrection = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.KeyboardView_keyPreviewLayout: - previewLayout = a.getResourceId(attr, 0); - break; - case R.styleable.KeyboardView_keyPreviewOffset: - mPreviewOffset = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.KeyboardView_keyPreviewHeight: - mPreviewHeight = a.getDimensionPixelSize(attr, 80); - break; - case R.styleable.KeyboardView_keyLetterSize: - mKeyLetterSize = a.getDimensionPixelSize(attr, 18); - break; - case R.styleable.KeyboardView_keyTextColor: - mKeyTextColor = a.getColor(attr, 0xFF000000); - break; - case R.styleable.KeyboardView_keyTextColorDisabled: - mKeyTextColorDisabled = a.getColor(attr, 0xFF000000); - break; - case R.styleable.KeyboardView_labelTextSize: - mLabelTextSize = a.getDimensionPixelSize(attr, 14); - break; - case R.styleable.KeyboardView_popupLayout: - mPopupLayout = a.getResourceId(attr, 0); - break; - case R.styleable.KeyboardView_shadowColor: - mShadowColor = a.getColor(attr, 0); - break; - case R.styleable.KeyboardView_shadowRadius: - mShadowRadius = a.getFloat(attr, 0f); - break; - // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount) - case R.styleable.KeyboardView_backgroundDimAmount: - mBackgroundDimAmount = a.getFloat(attr, 0.5f); - break; - case R.styleable.KeyboardView_keyLetterStyle: - mKeyLetterStyle = Typeface.defaultFromStyle(a.getInt(attr, Typeface.NORMAL)); - break; - case R.styleable.KeyboardView_colorScheme: - mColorScheme = a.getInt(attr, COLOR_SCHEME_WHITE); - break; - } - } + mKeyBackground = a.getDrawable(R.styleable.KeyboardView_keyBackground); + mKeyHysteresisDistance = a.getDimensionPixelOffset( + R.styleable.KeyboardView_keyHysteresisDistance, 0); + mVerticalCorrection = a.getDimensionPixelOffset( + R.styleable.KeyboardView_verticalCorrection, 0); + final int previewLayout = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0); + mPreviewOffset = a.getDimensionPixelOffset(R.styleable.KeyboardView_keyPreviewOffset, 0); + mPreviewHeight = a.getDimensionPixelSize(R.styleable.KeyboardView_keyPreviewHeight, 80); + mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio); + mKeyTextColor = a.getColor(R.styleable.KeyboardView_keyTextColor, 0xFF000000); + mKeyTextColorDisabled = a.getColor( + R.styleable.KeyboardView_keyTextColorDisabled, 0xFF000000); + mLabelTextRatio = getRatio(a, R.styleable.KeyboardView_labelTextRatio); + mPopupLayout = a.getResourceId(R.styleable.KeyboardView_popupLayout, 0); + mShadowColor = a.getColor(R.styleable.KeyboardView_shadowColor, 0); + mShadowRadius = a.getFloat(R.styleable.KeyboardView_shadowRadius, 0f); + // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount) + mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f); + mKeyLetterStyle = Typeface.defaultFromStyle( + a.getInt(R.styleable.KeyboardView_keyLetterStyle, Typeface.NORMAL)); + mColorScheme = a.getInt(R.styleable.KeyboardView_colorScheme, COLOR_SCHEME_WHITE); final Resources res = getResources(); if (previewLayout != 0) { mPreviewText = (TextView) LayoutInflater.from(context).inflate(previewLayout, null); - mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); + mPreviewTextRatio = getRatio(res, R.fraction.key_preview_text_ratio); } else { - mShowKeyPreview = false; + mShowKeyPreviewPopup = false; } mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); @@ -376,7 +349,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPaint = new Paint(); mPaint.setAntiAlias(true); - mPaint.setTextSize(keyTextSize); mPaint.setTextAlign(Align.CENTER); mPaint.setAlpha(255); @@ -460,6 +432,16 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); } + // Read fraction value in TypedArray as float. + private static float getRatio(TypedArray a, int index) { + return a.getFraction(index, 1000, 1000, 1) / 1000.0f; + } + + // Read fraction value in resource as float. + private static float getRatio(Resources res, int id) { + return res.getFraction(id, 1000, 1000) / 1000.0f; + } + public void startIgnoringDoubleTap() { if (ENABLE_CAPSLOCK_BY_DOUBLETAP) mHandler.startIgnoringDoubleTap(); @@ -480,6 +462,12 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { return mKeyboardActionListener; } + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + // TODO: Should notify InputMethodService instead? + KeyboardSwitcher.getInstance().onSizeChanged(); + } + /** * Attaches a keyboard to this view. The keyboard can be switched at any time and the * view will re-layout itself to accommodate the keyboard. @@ -506,6 +494,10 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { invalidateAllKeys(); mKeyDetector.setProximityThreshold(keyboard.getMostCommonKeyWidth()); mPopupPanelCache.clear(); + final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap(); + mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio); + mLabelTextSize = (int)(keyHeight * mLabelTextRatio); + mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio); } /** @@ -530,19 +522,21 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { * Enables or disables the key feedback popup. This is a popup that shows a magnified * version of the depressed key. By default the preview is enabled. * @param previewEnabled whether or not to enable the key feedback preview - * @see #isKeyPreviewEnabled() + * @param delay the delay after which the preview is dismissed + * @see #isKeyPreviewPopupEnabled() */ - public void setKeyPreviewEnabled(boolean previewEnabled) { - mShowKeyPreview = previewEnabled; + public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { + mShowKeyPreviewPopup = previewEnabled; + mDelayAfterPreview = delay; } /** * Returns the enabled state of the key feedback preview * @return whether or not the key feedback preview is enabled - * @see #setKeyPreviewEnabled(boolean) + * @see #setKeyPreviewPopupEnabled(boolean, int) */ - public boolean isKeyPreviewEnabled() { - return mShowKeyPreview; + public boolean isKeyPreviewPopupEnabled() { + return mShowKeyPreviewPopup; } public int getColorScheme() { @@ -865,7 +859,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { @Override public void showKeyPreview(int keyIndex, PointerTracker tracker) { - if (mShowKeyPreview) { + if (mShowKeyPreviewPopup) { mHandler.showKeyPreview(mDelayBeforePreview, keyIndex, tracker); } else if (mKeyboard.needSpacebarPreview(keyIndex)) { // Show key preview (in this case, slide language switcher) without any delay. @@ -875,13 +869,15 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { @Override public void dismissKeyPreview(PointerTracker tracker) { - if (mShowKeyPreview) { + if (mShowKeyPreviewPopup) { mHandler.cancelShowKeyPreview(tracker); mHandler.dismissKeyPreview(mDelayAfterPreview, tracker); } else if (mKeyboard.needSpacebarPreview(KeyDetector.NOT_A_KEY)) { // Dismiss key preview (in this case, slide language switcher) without any delay. mPreviewText.setVisibility(View.INVISIBLE); } + // Clear key preview display position. + mKeyPreviewPopupDisplayedY = -1; } private void addKeyPreview(TextView keyPreview) { @@ -938,7 +934,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyLetterSize); previewText.setTypeface(Typeface.DEFAULT_BOLD); } else { - previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge); + previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSize); previewText.setTypeface(mKeyLetterStyle); } } else { @@ -960,7 +956,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { final int previewX = keyDrawX - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0]; final int previewY = key.mY - previewHeight + mCoordinates[1] + mPreviewOffset; // Record key preview position to display mini-keyboard later at the same position - mKeyPreviewDisplayedY = previewY; + mKeyPreviewPopupDisplayedY = previewY; // Place the key preview. // TODO: Adjust position of key previews which touch screen edges @@ -1078,7 +1074,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { }); final Keyboard keyboard = new MiniKeyboardBuilder(this, mKeyboard.getPopupKeyboardResId(), - parentKey).build(); + parentKey, mKeyboard).build(); miniKeyboardView.setKeyboard(keyboard); container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), @@ -1111,7 +1107,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { mPopupWindow.setClippingEnabled(false); } mPopupMiniKeyboardPanel = popupPanel; - popupPanel.showPanel(this, parentKey, tracker, mKeyPreviewDisplayedY, mPopupWindow); + popupPanel.showPanel(this, parentKey, tracker, mKeyPreviewPopupDisplayedY, mPopupWindow); invalidateAllKeys(); return true; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index c279769f6..fe27ab412 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -78,7 +78,7 @@ public class LatinKeyboard extends Keyboard { // of the most common key width of this keyboard). private static final int SPACEBAR_DRAG_WIDTH = 3; // Minimum width of space key preview (proportional to keyboard width). - private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f; + private static final float SPACEBAR_POPUP_MIN_RATIO = 0.5f; // Height in space key the language name will be drawn. (proportional to space key height) public static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; // If the full language name needs to be smaller than this value to be drawn on space key, @@ -88,8 +88,8 @@ public class LatinKeyboard extends Keyboard { private static final String SMALL_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "small"; private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium"; - public LatinKeyboard(Context context, KeyboardId id) { - super(context, id.getXmlId(), id); + public LatinKeyboard(Context context, KeyboardId id, int width) { + super(context, id.getXmlId(), id, width); final Resources res = context.getResources(); mContext = context; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index c98076f35..185f1f8f7 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -16,10 +16,6 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.deprecated.VoiceProxy; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.Utils; - import android.content.Context; import android.graphics.Canvas; import android.text.TextUtils; @@ -27,6 +23,10 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; +import com.android.inputmethod.deprecated.VoiceProxy; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.Utils; + // TODO: We should remove this class public class LatinKeyboardView extends KeyboardView { private static final String TAG = LatinKeyboardView.class.getSimpleName(); @@ -47,7 +47,7 @@ public class LatinKeyboardView extends KeyboardView { private int mLastY; public LatinKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, 0); + super(context, attrs); } public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { @@ -55,14 +55,14 @@ public class LatinKeyboardView extends KeyboardView { } @Override - public void setKeyPreviewEnabled(boolean previewEnabled) { + public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) { // Phone and number keyboard never shows popup preview (except language switch). - super.setKeyPreviewEnabled(false); + super.setKeyPreviewPopupEnabled(false, delay); } else { - super.setKeyPreviewEnabled(previewEnabled); + super.setKeyPreviewPopupEnabled(previewEnabled, delay); } } @@ -173,7 +173,8 @@ public class LatinKeyboardView extends KeyboardView { if (!mDroppingEvents) { mDroppingEvents = true; // Send an up event - MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(), + MotionEvent translated = MotionEvent.obtain( + me.getEventTime(), me.getEventTime(), MotionEvent.ACTION_UP, mLastX, mLastY, me.getMetaState()); super.onTouchEvent(translated); diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index 5dde15e94..2d6766f2d 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. + * 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 @@ -23,8 +23,8 @@ import java.util.List; public class MiniKeyboard extends Keyboard { private int mDefaultKeyCoordX; - public MiniKeyboard(Context context, int xmlLayoutResId, KeyboardId id) { - super(context, xmlLayoutResId, id); + public MiniKeyboard(Context context, int xmlLayoutResId, Keyboard parentKeyboard) { + super(context, xmlLayoutResId, null, parentKeyboard.getMinWidth()); } public void setDefaultCoordX(int pos) { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java index e540fa106..6e939123d 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -181,22 +181,25 @@ public class MiniKeyboardBuilder { } } - public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey) { + public MiniKeyboardBuilder(KeyboardView view, int layoutTemplateResId, Key parentKey, + Keyboard parentKeyboard) { final Context context = view.getContext(); mRes = context.getResources(); - final MiniKeyboard keyboard = new MiniKeyboard(context, layoutTemplateResId, null); + final MiniKeyboard keyboard = new MiniKeyboard( + context, layoutTemplateResId, parentKeyboard); mKeyboard = keyboard; mPopupCharacters = parentKey.mPopupCharacters; final int keyWidth = getMaxKeyWidth(view, mPopupCharacters, keyboard.getKeyWidth()); final MiniKeyboardLayoutParams params = new MiniKeyboardLayoutParams( mPopupCharacters.length, parentKey.mMaxPopupColumn, - keyWidth, keyboard.getRowHeight(), + keyWidth, parentKeyboard.getRowHeight(), parentKey.mX + (parentKey.mWidth + parentKey.mGap) / 2 - keyWidth / 2, view.getMeasuredWidth()); mParams = params; - keyboard.setHeight(params.mNumRows * params.mRowHeight - keyboard.getVerticalGap()); + keyboard.setRowHeight(params.mRowHeight); + keyboard.setHeight(params.mNumRows * params.mRowHeight); keyboard.setMinWidth(params.mNumColumns * params.mKeyWidth); keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2); } @@ -235,7 +238,7 @@ public class MiniKeyboardBuilder { final CharSequence label = mPopupCharacters[n]; final int row = n / params.mNumColumns; final Key key = new Key(mRes, keyboard, label, params.getX(n, row), params.getY(row), - params.mKeyWidth, params.getRowFlags(row)); + params.mKeyWidth, params.mRowHeight, params.getRowFlags(row)); keys.add(key); } return keyboard; diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java index c4459f616..cc5c3bbfe 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java b/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java index f215db876..ebbc79a9e 100644 --- a/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java +++ b/java/src/com/android/inputmethod/keyboard/ModifierKeyState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 953d487dc..6b4e9469f 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -633,8 +633,6 @@ public class PointerTracker { private void startLongPressTimer(int keyIndex) { Key key = getKey(keyIndex); - if (!key.mEnabled) - return; if (key.mCode == Keyboard.CODE_SHIFT) { mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); } else if (key.mManualTemporaryUpperCaseCode != Keyboard.CODE_DUMMY diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java index b3ed1e26f..eecbb26f3 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerKeyState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java index 68de8df8a..9e287c67d 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTrackerQueue.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java index 32c25801d..ff78ee5c9 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java +++ b/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java index 12031f1ea..60d87f789 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.latin.R; - import android.content.Context; import android.content.res.Resources; import android.os.SystemClock; @@ -27,6 +25,8 @@ import android.view.MotionEvent; import android.view.View; import android.widget.PopupWindow; +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. @@ -41,7 +41,7 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { private long mDownTime; public PopupMiniKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.keyboardViewStyle); + this(context, attrs, R.attr.popupMiniKeyboardViewStyle); } public PopupMiniKeyboardView(Context context, AttributeSet attrs, int defStyle) { @@ -55,13 +55,14 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { R.dimen.mini_keyboard_slide_allowance)); // Remove gesture detector on mini-keyboard mGestureDetector = null; - setKeyPreviewEnabled(false); + setKeyPreviewPopupEnabled(false, 0); } @Override - public void setKeyPreviewEnabled(boolean previewEnabled) { - // Mini keyboard needs no pop-up key preview displayed. - super.setKeyPreviewEnabled(false); + public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { + // Mini keyboard needs no pop-up key preview displayed, so we pass always false with a + // delay of 0. The delay does not matter actually since the popup is not shown anyway. + super.setKeyPreviewPopupEnabled(false, 0); } @Override @@ -75,15 +76,17 @@ public class PopupMiniKeyboardView extends KeyboardView implements PopupPanel { final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX() : parentKey.mX + parentKey.mWidth / 2; final int pointY = parentKey.mY; - final int miniKeyboardX = pointX - miniKeyboard.getDefaultCoordX() - - container.getPaddingLeft() - + parentKeyboardView.getPaddingLeft() + mCoordinates[0]; + final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX() + + parentKeyboardView.getPaddingLeft(); + final int miniKeyboardX = Math.max(0, Math.min(miniKeyboardLeft, + parentKeyboardView.getWidth() - miniKeyboard.getMinWidth())) + - container.getPaddingLeft() + mCoordinates[0]; final int miniKeyboardY = pointY - parentKeyboard.getVerticalGap() - (container.getMeasuredHeight() - container.getPaddingBottom()) + parentKeyboardView.getPaddingTop() + mCoordinates[1]; final int x = miniKeyboardX; - final int y = parentKeyboardView.isKeyPreviewEnabled() && miniKeyboard.isOneRowKeyboard() - ? keyPreviewY : miniKeyboardY; + final int y = parentKeyboardView.isKeyPreviewPopupEnabled() && + miniKeyboard.isOneRowKeyboard() && keyPreviewY >= 0 ? keyPreviewY : miniKeyboardY; if (miniKeyboard.setShifted(parentKeyboard.isShiftedOrShiftLocked())) { invalidateAllKeys(); diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 80d6de952..33acc6907 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Google Inc. + * 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 diff --git a/java/src/com/android/inputmethod/keyboard/Row.java b/java/src/com/android/inputmethod/keyboard/Row.java index 3618c0448..40d7e1472 100644 --- a/java/src/com/android/inputmethod/keyboard/Row.java +++ b/java/src/com/android/inputmethod/keyboard/Row.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java b/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java index 9229208a9..ba15624f0 100644 --- a/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java +++ b/java/src/com/android/inputmethod/keyboard/ShiftKeyState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java index b279c1c7e..5cf31cb14 100644 --- a/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java +++ b/java/src/com/android/inputmethod/keyboard/SlidingLocaleDrawable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -38,7 +38,7 @@ import android.view.ViewConfiguration; * movement on the spacebar. */ public class SlidingLocaleDrawable extends Drawable { - + private static final int SLIDE_SPEED_MULTIPLIER_RATIO = 150; private final Context mContext; private final Resources mRes; private final int mWidth; @@ -90,7 +90,7 @@ public class SlidingLocaleDrawable extends Drawable { mCurrentLanguage = null; return; } - mDiff = diff; + mDiff = Math.max(diff, diff * SLIDE_SPEED_MULTIPLIER_RATIO / 100); if (mDiff > mWidth) mDiff = mWidth; if (mDiff < -mWidth) mDiff = -mWidth; if (Math.abs(mDiff) > mThreshold) mHitThreshold = true; diff --git a/java/src/com/android/inputmethod/keyboard/SwipeTracker.java b/java/src/com/android/inputmethod/keyboard/SwipeTracker.java index 730cdc390..975b13b50 100644 --- a/java/src/com/android/inputmethod/keyboard/SwipeTracker.java +++ b/java/src/com/android/inputmethod/keyboard/SwipeTracker.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java index 307b81d43..460930f16 100644 --- a/java/src/com/android/inputmethod/latin/AutoDictionary.java +++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -41,13 +41,8 @@ public class AutoDictionary extends ExpandableDictionary { static final int FREQUENCY_FOR_PICKED = 3; // Weight added to a user typing a new word that doesn't get corrected (or is reverted) static final int FREQUENCY_FOR_TYPED = 1; - // A word that is frequently typed and gets promoted to the user dictionary, uses this - // frequency. - static final int FREQUENCY_FOR_AUTO_ADD = 250; // If the user touches a typed word 2 times or more, it will become valid. private static final int VALIDITY_THRESHOLD = 2 * FREQUENCY_FOR_PICKED; - // If the user touches a typed word 4 times or more, it will be added to the user dict. - private static final int PROMOTION_THRESHOLD = 4 * FREQUENCY_FOR_PICKED; private LatinIME mIme; // Locale for which this auto dictionary is storing words @@ -151,11 +146,6 @@ public class AutoDictionary extends ExpandableDictionary { freq = freq < 0 ? addFrequency : freq + addFrequency; super.addWord(word, freq); - if (freq >= PROMOTION_THRESHOLD) { - mIme.promoteToUserDictionary(word, FREQUENCY_FOR_AUTO_ADD); - freq = 0; - } - synchronized (mPendingWritesLock) { // Write a null frequency if it is to be deleted from the db mPendingWrites.put(word, freq == 0 ? null : new Integer(freq)); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index d95fb9638..9748d6006 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -196,8 +196,9 @@ public class BinaryDictionary extends Dictionary { Arrays.fill(outputChars, (char) 0); Arrays.fill(scores, 0); + final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo(); return getSuggestionsNative( - mNativeDict, keyboard.getProximityInfo(), + mNativeDict, proximityInfo, codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, mFlags, outputChars, scores); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 562580d41..7ce92920d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -78,16 +78,20 @@ class BinaryDictionaryGetter { } else { try { // If that was no-go, try to find a publicly exported dictionary. - return BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); + List<AssetFileAddress> listFromContentProvider = + BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); + if (null != listFromContentProvider) { + return listFromContentProvider; + } + // If the list is null, fall through and return the fallback } catch (FileNotFoundException e) { Log.e(TAG, "Unable to create dictionary file from provider for locale " + locale.toString() + ": falling back to internal dictionary"); - return Arrays.asList(loadFallbackResource(context, fallbackResId)); } catch (IOException e) { Log.e(TAG, "Unable to read source data for locale " + locale.toString() + ": falling back to internal dictionary"); - return Arrays.asList(loadFallbackResource(context, fallbackResId)); } + return Arrays.asList(loadFallbackResource(context, fallbackResId)); } } } diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index abdf30e6b..fe3c72f4c 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -16,8 +16,6 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; - import android.content.Context; import android.content.res.Resources; import android.graphics.Color; @@ -40,11 +38,12 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; + import java.util.ArrayList; import java.util.List; @@ -57,6 +56,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo private static final boolean DBG = LatinImeLogger.sDBG; private final ArrayList<View> mWords = new ArrayList<View>(); + private final ArrayList<View> mDividers = new ArrayList<View>(); private final boolean mConfigCandidateHighlightFontColorEnabled; private final CharacterStyle mInvertedForegroundColorSpan; private final CharacterStyle mInvertedBackgroundColorSpan; @@ -148,10 +148,11 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo tv.setOnClickListener(this); if (i == 0) tv.setOnLongClickListener(this); - ImageView divider = (ImageView)v.findViewById(R.id.candidate_divider); - // Do not display divider of first candidate. - divider.setVisibility(i == 0 ? INVISIBLE : VISIBLE); mWords.add(v); + if (i > 0) { + View divider = inflater.inflate(R.layout.candidate_divider, null); + mDividers.add(divider); + } } scrollTo(0, getScrollY()); @@ -237,6 +238,8 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo } else { dv.setVisibility(GONE); } + if (i > 0) + addView(mDividers.get(i - 1)); addView(v); } @@ -275,7 +278,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo setSuggestions(builder.build()); mShowingAddToDictionary = true; // Disable R.string.hint_add_to_dictionary button - TextView tv = (TextView)getChildAt(1).findViewById(R.id.candidate_word); + TextView tv = (TextView)mWords.get(1).findViewById(R.id.candidate_word); tv.setClickable(false); } @@ -308,7 +311,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo previewText.setText(word); previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - View v = getChildAt(index); + View v = mWords.get(index); final int[] offsetInWindow = new int[2]; v.getLocationInWindow(offsetInWindow); final int posX = offsetInWindow[0]; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 605676d70..bba331868 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -142,6 +142,25 @@ public class DictionaryFactory { return hasDictionary; } + // TODO: Do not use the size of the dictionary as an unique dictionary ID. + public static Long getDictionaryId(Context context, Locale locale) { + final Resources res = context.getResources(); + final Locale saveLocale = Utils.setSystemLocale(res, locale); + + final int resourceId = Utils.getMainDictionaryResourceId(res); + final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); + final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) + ? afd.getLength() + : null; + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + } + + Utils.setSystemLocale(res, saveLocale); + return size; + } + // TODO: Find the Right Way to find out whether the resource is a placeholder or not. // Suggestion : strip the locale, open the placeholder file and store its offset. // Upon opening the file, if it's the same offset, then it's the placeholder. diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java index 39e7e402f..e56aa695d 100644 --- a/java/src/com/android/inputmethod/latin/EditingUtils.java +++ b/java/src/com/android/inputmethod/latin/EditingUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Google Inc. + * Copyright (C) 2009 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 diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 26391fe46..97a4a1816 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -229,6 +229,7 @@ public class ExpandableDictionary extends Dictionary { * Returns the word's frequency or -1 if not found */ protected int getWordFrequency(CharSequence word) { + // Case-sensitive search Node node = searchNode(mRoots, word, 0, word.length()); return (node == null) ? -1 : node.mFrequency; } @@ -366,12 +367,16 @@ public class ExpandableDictionary extends Dictionary { /** * Adds bigrams to the in-memory trie structure that is being used to retrieve any word - * @param frequency frequency for this bigrams - * @param addFrequency if true, it adds to current frequency + * @param frequency frequency for this bigram + * @param addFrequency if true, it adds to current frequency, else it overwrites the old value * @return returns the final frequency */ private int addOrSetBigram(String word1, String word2, int frequency, boolean addFrequency) { - Node firstWord = searchWord(mRoots, word1, 0, null); + // We don't want results to be different according to case of the looked up left hand side + // word. We do want however to return the correct case for the right hand side. + // So we want to squash the case of the left hand side, and preserve that of the right + // hand side word. + Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null); Node secondWord = searchWord(mRoots, word2, 0, null); LinkedList<NextWord> bigram = firstWord.mNGrams; if (bigram == null || bigram.size() == 0) { @@ -437,8 +442,12 @@ public class ExpandableDictionary extends Dictionary { } } - private void runReverseLookUp(final CharSequence previousWord, final WordCallback callback) { - Node prevWord = searchNode(mRoots, previousWord, 0, previousWord.length()); + private void runBigramReverseLookUp(final CharSequence previousWord, + final WordCallback callback) { + // Search for the lowercase version of the word only, because that's where bigrams + // store their sons. + Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0, + previousWord.length()); if (prevWord != null && prevWord.mNGrams != null) { reverseLookUp(prevWord.mNGrams, callback); } @@ -448,7 +457,7 @@ public class ExpandableDictionary extends Dictionary { public void getBigrams(final WordComposer codes, final CharSequence previousWord, final WordCallback callback) { if (!reloadDictionaryIfRequired()) { - runReverseLookUp(previousWord, callback); + runBigramReverseLookUp(previousWord, callback); } } @@ -494,14 +503,20 @@ public class ExpandableDictionary extends Dictionary { } /** - * Search for the terminal node of the word + * Recursively search for the terminal node of the word. + * + * One iteration takes the full word to search for and the current index of the recursion. + * + * @param children the node of the trie to search under. + * @param word the word to search for. Only read [offset..length] so there may be trailing chars + * @param offset the index in {@code word} this recursion should operate on. + * @param length the length of the input word. * @return Returns the terminal node of the word if the word exists */ private Node searchNode(final NodeArray children, final CharSequence word, final int offset, final int length) { - // TODO Consider combining with addWordRec final int count = children.mLength; - char currentChar = word.charAt(offset); + final char currentChar = word.charAt(offset); for (int j = 0; j < count; j++) { final Node node = children.mData[j]; if (node.mCode == currentChar) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8b130aecd..a4a04ffb1 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,21 +16,6 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.compat.CompatUtils; -import com.android.inputmethod.compat.EditorInfoCompatUtils; -import com.android.inputmethod.compat.InputConnectionCompatUtils; -import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; -import com.android.inputmethod.compat.InputMethodServiceCompatWrapper; -import com.android.inputmethod.compat.InputTypeCompatUtils; -import com.android.inputmethod.deprecated.LanguageSwitcherProxy; -import com.android.inputmethod.deprecated.VoiceProxy; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardActionListener; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.KeyboardView; -import com.android.inputmethod.keyboard.LatinKeyboard; -import com.android.inputmethod.keyboard.LatinKeyboardView; - import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -58,7 +43,6 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; @@ -68,7 +52,23 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.InputConnection; -import android.widget.LinearLayout; + +import com.android.inputmethod.compat.CompatUtils; +import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.compat.InputConnectionCompatUtils; +import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodServiceCompatWrapper; +import com.android.inputmethod.compat.InputTypeCompatUtils; +import com.android.inputmethod.compat.SuggestionSpanUtils; +import com.android.inputmethod.deprecated.LanguageSwitcherProxy; +import com.android.inputmethod.deprecated.VoiceProxy; +import com.android.inputmethod.deprecated.recorrection.Recorrection; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.LatinKeyboard; +import com.android.inputmethod.keyboard.LatinKeyboardView; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -81,7 +81,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final String TAG = LatinIME.class.getSimpleName(); private static final boolean PERF_DEBUG = false; private static final boolean TRACE = false; - private static boolean DEBUG = LatinImeLogger.sDBG; + private static boolean DEBUG; /** * The private IME option used to indicate that no microphone should be @@ -216,15 +216,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void handleMessage(Message msg) { final KeyboardSwitcher switcher = mKeyboardSwitcher; - final LatinKeyboardView inputView = switcher.getInputView(); + final LatinKeyboardView inputView = switcher.getKeyboardView(); switch (msg.what) { case MSG_UPDATE_SUGGESTIONS: updateSuggestions(); break; case MSG_UPDATE_OLD_SUGGESTIONS: - mRecorrection.setRecorrectionSuggestions(mVoiceProxy, mCandidateView, mSuggest, - mKeyboardSwitcher, mWord, mHasUncommittedTypedChars, mLastSelectionStart, - mLastSelectionEnd, mSettingsValues.mWordSeparators); + mRecorrection.fetchAndDisplayRecorrectionSuggestions(mVoiceProxy, mCandidateView, + mSuggest, mKeyboardSwitcher, mWord, mHasUncommittedTypedChars, + mLastSelectionStart, mLastSelectionEnd, mSettingsValues.mWordSeparators); break; case MSG_UPDATE_SHIFT_STATE: switcher.updateShiftState(); @@ -306,7 +306,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void startDisplayLanguageOnSpacebar(boolean localeChanged) { removeMessages(MSG_FADEOUT_LANGUAGE_ON_SPACEBAR); removeMessages(MSG_DISMISS_LANGUAGE_ON_SPACEBAR); - final LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); + final LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { final LatinKeyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); // The language is always displayed when the delay is negative. @@ -357,6 +357,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mRecorrection = Recorrection.getInstance(); + DEBUG = LatinImeLogger.sDBG; loadSettings(); @@ -405,7 +406,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void initSuggest() { final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); - final Locale keyboardLocale = new Locale(localeStr); + final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); final Resources res = mResources; final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale); @@ -439,7 +440,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar /* package private */ void resetSuggestMainDict() { final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); - final Locale keyboardLocale = new Locale(localeStr); + final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); int mainDicResId = Utils.getMainDictionaryResourceId(mResources); mSuggest.resetMainDict(this, mainDicResId, keyboardLocale); } @@ -486,24 +487,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } @Override - public View onCreateCandidatesView() { - LayoutInflater inflater = getLayoutInflater(); - LinearLayout container = (LinearLayout)inflater.inflate(R.layout.candidates, null); - mCandidateViewContainer = container; - mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height); - mCandidateView = (CandidateView) container.findViewById(R.id.candidates); + public void setInputView(View view) { + super.setInputView(view); + mCandidateViewContainer = view.findViewById(R.id.candidates_container); + mCandidateView = (CandidateView) view.findViewById(R.id.candidates); mCandidateView.setService(this); - setCandidatesViewShown(true); - return container; + mCandidateStripHeight = (int)mResources.getDimension(R.dimen.candidate_strip_height); + } + + @Override + public void setCandidatesView(View view) { + // To ensure that CandidatesView will never be set. + return; } @Override public void onStartInputView(EditorInfo attribute, boolean restarting) { final KeyboardSwitcher switcher = mKeyboardSwitcher; - LatinKeyboardView inputView = switcher.getInputView(); + LatinKeyboardView inputView = switcher.getKeyboardView(); if (DEBUG) { - Log.d(TAG, "onStartInputView: " + inputView); + Log.d(TAG, "onStartInputView: inputType=" + ((attribute == null) ? "none" + : String.format("0x%08x", attribute.inputType))); } // In landscape mode, this method gets called without the input view being created. if (inputView == null) { @@ -549,13 +554,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar switcher.updateShiftState(); } - setCandidatesViewShownInternal(isCandidateStripVisible(), false /* needsInputViewShown */ ); + setSuggestionStripShownInternal(isCandidateStripVisible(), /* needsInputViewShown */ false); // Delay updating suggestions because keyboard input view may not be shown at this point. mHandler.postUpdateSuggestions(); updateCorrectionMode(); - inputView.setKeyPreviewEnabled(mSettingsValues.mPopupOn); + inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn, + mSettingsValues.mKeyPreviewPopupDismissDelay); inputView.setProximityCorrectionEnabled(true); // If we just entered a text field, maybe it has some old text that requires correction mRecorrection.checkRecorrectionOnStart(); @@ -622,6 +628,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } @Override + public void onWindowHidden() { + super.onWindowHidden(); + KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); + if (inputView != null) inputView.closing(); + } + + @Override public void onFinishInput() { super.onFinishInput(); @@ -630,7 +643,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mVoiceProxy.flushVoiceInputLogs(mConfigurationChanging); - KeyboardView inputView = mKeyboardSwitcher.getInputView(); + KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites(); if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites(); @@ -639,7 +652,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onFinishInputView(boolean finishingInput) { super.onFinishInputView(finishingInput); - KeyboardView inputView = mKeyboardSwitcher.getInputView(); + KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.setForeground(false); // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestions(); @@ -684,7 +697,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // If the composing span has been cleared, save the typed word in the history for // recorrection before we reset the candidate strip. Then, we'll be able to show // suggestions for recorrection right away. - mRecorrection.saveWordInHistory(mWord, mComposing); + mRecorrection.saveRecorrectionSuggestion(mWord, mComposing); } mComposing.setLength(0); mHasUncommittedTypedChars = false; @@ -795,11 +808,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // When in fullscreen mode, show completions generated by the application setSuggestions(builder.build()); mBestWord = null; - setCandidatesViewShown(true); + setSuggestionStripShown(true); } } - private void setCandidatesViewShownInternal(boolean shown, boolean needsInputViewShown) { + private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) { // TODO: Modify this if we support candidates with hard keyboard if (onEvaluateInputViewShown()) { final boolean shouldShowCandidates = shown @@ -807,26 +820,25 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (isExtractViewShown()) { // No need to have extra space to show the key preview. mCandidateViewContainer.setMinimumHeight(0); - super.setCandidatesViewShown(shown); + mCandidateViewContainer.setVisibility( + shouldShowCandidates ? View.VISIBLE : View.GONE); } else { // We must control the visibility of the suggestion strip in order to avoid clipped // key previews, even when we don't show the suggestion strip. mCandidateViewContainer.setVisibility( shouldShowCandidates ? View.VISIBLE : View.INVISIBLE); - super.setCandidatesViewShown(true); } } } - @Override - public void setCandidatesViewShown(boolean shown) { - setCandidatesViewShownInternal(shown, true /* needsInputViewShown */ ); + private void setSuggestionStripShown(boolean shown) { + setSuggestionStripShownInternal(shown, /* needsInputViewShown */true); } @Override public void onComputeInsets(InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); - final KeyboardView inputView = mKeyboardSwitcher.getInputView(); + final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView == null) return; final int containerHeight = mCandidateViewContainer.getHeight(); @@ -868,8 +880,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: - if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) { - if (mKeyboardSwitcher.getInputView().handleBack()) { + if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getKeyboardView() != null) { + if (mKeyboardSwitcher.getKeyboardView().handleBack()) { return true; } } @@ -1005,14 +1017,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private void onSettingsKeyPressed() { - if (!isShowingOptionDialog()) { - if (!mSettingsValues.mEnableShowSubtypeSettings) { - showSubtypeSelectorAndSettings(); - } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) { - showOptionsMenu(); - } else { - launchSettings(); - } + if (isShowingOptionDialog()) + return; + if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) { + showSubtypeSelectorAndSettings(); + } else if (Utils.hasMultipleEnabledIMEsOrSubtypes(mImm)) { + showOptionsMenu(); + } else { + launchSettings(); } } @@ -1221,7 +1233,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (!mHasUncommittedTypedChars) { mHasUncommittedTypedChars = true; mComposing.setLength(0); - mRecorrection.saveWordInHistory(mWord, mBestWord); + mRecorrection.saveRecorrectionSuggestion(mWord, mBestWord); mWord.reset(); clearSuggestions(); } @@ -1356,7 +1368,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar commitTyped(getCurrentInputConnection()); mVoiceProxy.handleClose(); requestHideSelf(0); - LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); + LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) inputView.closing(); } @@ -1392,7 +1404,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (DEBUG) { Log.d(TAG, "Switch to keyboard view."); } - View v = mKeyboardSwitcher.getInputView(); + View v = mKeyboardSwitcher.getKeyboardView(); if (v != null) { // Confirms that the keyboard view doesn't have parent view. ViewParent p = v.getParent(); @@ -1401,7 +1413,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } setInputView(v); } - setCandidatesViewShown(isCandidateStripVisible()); + setSuggestionStripShown(isCandidateStripVisible()); updateInputViewShown(); mHandler.postUpdateSuggestions(); } @@ -1411,9 +1423,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public void setSuggestions(SuggestedWords words) { - if (mVoiceProxy.getAndResetIsShowingHint()) { - setCandidatesView(mCandidateViewContainer); - } +// if (mVoiceProxy.getAndResetIsShowingHint()) { +// setCandidatesView(mCandidateViewContainer); +// } if (mCandidateView != null) { mCandidateView.setSuggestions(words); @@ -1443,7 +1455,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(), mSettingsValues.mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getInputView(), word, prevWord); + mKeyboardSwitcher.getKeyboardView(), word, prevWord); boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); final CharSequence typedWord = word.getTypedWord(); @@ -1464,14 +1476,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // in most cases, suggestion count is 1 when typed word's length is 1, but we do always // need to clear the previous state when the user starts typing a word (i.e. typed word's // length == 1). - if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid - || mCandidateView.isShowingAddToDictionaryHint()) { - builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(correctionAvailable); - } else { - final SuggestedWords previousSuggestions = mCandidateView.getSuggestions(); - if (previousSuggestions == mSettingsValues.mSuggestPuncList) - return; - builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions); + if (typedWord != null) { + if (builder.size() > 1 || typedWord.length() == 1 || typedWordValid + || mCandidateView.isShowingAddToDictionaryHint()) { + builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion( + correctionAvailable); + } else { + final SuggestedWords previousSuggestions = mCandidateView.getSuggestions(); + if (previousSuggestions == mSettingsValues.mSuggestPuncList) + return; + builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions); + } } showSuggestions(builder.build(), typedWord); } @@ -1489,7 +1504,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { mBestWord = null; } - setCandidatesViewShown(isCandidateStripVisible()); + setSuggestionStripShown(isCandidateStripVisible()); } private boolean pickDefaultSuggestion(int separatorCode) { @@ -1501,7 +1516,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mBestWord != null && mBestWord.length() > 0) { TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord, separatorCode); mJustAccepted = true; - pickSuggestion(mBestWord); + commitBestWord(mBestWord); // Add the word to the auto dictionary if it's not a known word addToAutoAndUserBigramDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED); return true; @@ -1548,7 +1563,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // a magic space even if it was a normal space. This is meant to help in case the user // pressed space on purpose of displaying the suggestion strip punctuation. final char primaryCode = suggestion.charAt(0); - final int toLeft = (ic == null) ? 0 : ic.getTextBeforeCursor(1, 0).charAt(0); + final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : ""; + final int toLeft = (ic == null || TextUtils.isEmpty(beforeText)) + ? 0 : beforeText.charAt(0); final boolean oldMagicSpace = mJustAddedMagicSpace; if (Keyboard.CODE_SPACE == toLeft) mJustAddedMagicSpace = true; onCodeInput(primaryCode, new int[] { primaryCode }, @@ -1566,7 +1583,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mWord.reset(); } mJustAccepted = true; - pickSuggestion(suggestion); + commitBestWord(suggestion); // Add the word to the auto dictionary if it's not a known word if (index == 0) { addToAutoAndUserBigramDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED); @@ -1602,12 +1619,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // TextEntryState.State.PICKED_SUGGESTION state. TextEntryState.typedCharacter((char) Keyboard.CODE_SPACE, true, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - // From there on onUpdateSelection() will fire so suggestions will be updated - } else if (!showingAddToDictionaryHint) { + } + if (!showingAddToDictionaryHint) { // If we're not showing the "Touch again to save", then show corrections again. // In case the cursor position doesn't change, make sure we show the suggestions again. - clearSuggestions(); - mHandler.postUpdateOldSuggestions(); + updateBigramPredictions(); + // Updating the predictions right away may be slow and feel unresponsive on slower + // terminals. On the other hand if we just postUpdateBigramPredictions() it will + // take a noticeable delay to update them which may feel uneasy. } if (showingAddToDictionaryHint) { mCandidateView.showAddToDictionaryHint(suggestion); @@ -1620,25 +1639,25 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar /** * Commits the chosen word to the text field and saves it for later * retrieval. - * @param suggestion the suggestion picked by the user to be committed to - * the text field */ - private void pickSuggestion(CharSequence suggestion) { + private void commitBestWord(CharSequence bestWord) { KeyboardSwitcher switcher = mKeyboardSwitcher; if (!switcher.isKeyboardAvailable()) return; InputConnection ic = getCurrentInputConnection(); if (ic != null) { - mVoiceProxy.rememberReplacedWord(suggestion, mSettingsValues.mWordSeparators); - ic.commitText(suggestion, 1); + mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators); + SuggestedWords suggestedWords = mCandidateView.getSuggestions(); + ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( + this, bestWord, suggestedWords), 1); } - mRecorrection.saveWordInHistory(mWord, suggestion); + mRecorrection.saveRecorrectionSuggestion(mWord, bestWord); mHasUncommittedTypedChars = false; - mCommittedLength = suggestion.length(); + mCommittedLength = bestWord.length(); } private static final WordComposer sEmptyWordComposer = new WordComposer(); - private void updateBigramPredictions() { + public void updateBigramPredictions() { if (mSuggest == null || !isSuggestionsRequested()) return; @@ -1650,7 +1669,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(), mSettingsValues.mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getInputView(), sEmptyWordComposer, prevWord); + mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord); if (builder.size() > 0) { // Explicitly supply an empty typed word (the no-second-arg version of @@ -1663,7 +1682,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void setPunctuationSuggestions() { setSuggestions(mSettingsValues.mSuggestPuncList); - setCandidatesViewShown(isCandidateStripVisible()); + setSuggestionStripShown(isCandidateStripVisible()); } private void addToAutoAndUserBigramDictionaries(CharSequence suggestion, int frequencyDelta) { @@ -1872,7 +1891,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // if mAudioManager is null, we don't have the ringer state yet // mAudioManager will be set by updateRingerMode if (mAudioManager == null) { - if (mKeyboardSwitcher.getInputView() != null) { + if (mKeyboardSwitcher.getKeyboardView() != null) { updateRingerMode(); } } @@ -1899,7 +1918,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (!mSettingsValues.mVibrateOn) { return; } - LatinKeyboardView inputView = mKeyboardSwitcher.getInputView(); + LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) { inputView.performHapticFeedback( HapticFeedbackConstants.KEYBOARD_TAP, @@ -1907,18 +1926,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - public void promoteToUserDictionary(String word, int frequency) { - if (mUserDictionary.isValidWord(word)) return; - mUserDictionary.addWord(word, frequency); - } - public WordComposer getCurrentWord() { return mWord; } - public boolean getPopupOn() { - return mSettingsValues.mPopupOn; - } boolean isSoundOn() { return mSettingsValues.mSoundOn && !mSilentModeOn; } @@ -2026,7 +2037,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void showOptionsMenuInternal(CharSequence title, CharSequence[] items, DialogInterface.OnClickListener listener) { - final IBinder windowToken = mKeyboardSwitcher.getInputView().getWindowToken(); + final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken(); if (windowToken == null) return; AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(true); @@ -2062,7 +2073,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar p.println(" TextEntryState.state=" + TextEntryState.getState()); p.println(" mSoundOn=" + mSettingsValues.mSoundOn); p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn); - p.println(" mPopupOn=" + mSettingsValues.mPopupOn); + p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn); } // Characters per second measurement diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 5eb365774..956c51e06 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -56,7 +56,7 @@ public class Settings extends PreferenceActivity public static final String PREF_GENERAL_SETTINGS_KEY = "general_settings"; public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_SOUND_ON = "sound_on"; - public static final String PREF_POPUP_ON = "popup_on"; + public static final String PREF_KEY_PREVIEW_POPUP_ON = "popup_on"; public static final String PREF_RECORRECTION_ENABLED = "recorrection_enabled"; public static final String PREF_AUTO_CAP = "auto_cap"; public static final String PREF_SETTINGS_KEY = "settings_key"; @@ -77,6 +77,9 @@ public class Settings extends PreferenceActivity public static final String PREF_MISC_SETTINGS_KEY = "misc_settings"; + public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = + "pref_key_preview_popup_dismiss_delay"; + public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; // Dialog ids @@ -84,7 +87,6 @@ public class Settings extends PreferenceActivity public static class Values { // From resources: - public final boolean mEnableShowSubtypeSettings; public final boolean mSwipeDownDismissKeyboardEnabled; public final int mDelayBeforeFadeoutLanguageOnSpacebar; public final int mDelayUpdateSuggestions; @@ -102,7 +104,8 @@ public class Settings extends PreferenceActivity // From preferences: public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn) public final boolean mVibrateOn; - public final boolean mPopupOn; // Warning : this escapes through LatinIME#isPopupOn + public final boolean mKeyPreviewPopupOn; + public final int mKeyPreviewPopupDismissDelay; public final boolean mAutoCap; public final boolean mQuickFixes; public final boolean mAutoCorrectEnabled; @@ -117,15 +120,13 @@ public class Settings extends PreferenceActivity final Resources res = context.getResources(); final Locale savedLocale; if (null != localeStr) { - final Locale keyboardLocale = new Locale(localeStr); + final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); savedLocale = Utils.setSystemLocale(res, keyboardLocale); } else { savedLocale = null; } // Get the resources - mEnableShowSubtypeSettings = res.getBoolean( - R.bool.config_enable_show_subtype_settings); mSwipeDownDismissKeyboardEnabled = res.getBoolean( R.bool.config_swipe_down_dismiss_keyboard_enabled); mDelayBeforeFadeoutLanguageOnSpacebar = res.getInteger( @@ -161,7 +162,8 @@ public class Settings extends PreferenceActivity mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, res.getBoolean(R.bool.config_default_sound_enabled)); - mPopupOn = isPopupEnabled(prefs, res); + mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res); + mKeyPreviewPopupDismissDelay = getKeyPreviewPopupDismissDelay(prefs, res); mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true); mQuickFixes = isQuickFixesEnabled(prefs, res); @@ -172,6 +174,8 @@ public class Settings extends PreferenceActivity && isBigramPredictionEnabled(prefs, res); mAutoCorrectionThreshold = getAutoCorrectionThreshold(prefs, res); + + Utils.setSystemLocale(res, savedLocale); } public boolean isSuggestedPunctuation(int code) { @@ -200,6 +204,7 @@ public class Settings extends PreferenceActivity return sp.getBoolean(Settings.PREF_QUICK_FIXES, resources.getBoolean( R.bool.config_default_quick_fixes)); } + private static boolean isAutoCorrectEnabled(SharedPreferences sp, Resources resources) { final String currentAutoCorrectionSetting = sp.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, @@ -208,13 +213,24 @@ public class Settings extends PreferenceActivity R.string.auto_correction_threshold_mode_index_off); return !currentAutoCorrectionSetting.equals(autoCorrectionOff); } - private static boolean isPopupEnabled(SharedPreferences sp, Resources resources) { + + // Public to access from KeyboardSwitcher. Should it have access to some + // process-global instance instead? + public static boolean isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources) { final boolean showPopupOption = resources.getBoolean( R.bool.config_enable_show_popup_on_keypress_option); if (!showPopupOption) return resources.getBoolean(R.bool.config_default_popup_preview); - return sp.getBoolean(Settings.PREF_POPUP_ON, + return sp.getBoolean(Settings.PREF_KEY_PREVIEW_POPUP_ON, resources.getBoolean(R.bool.config_default_popup_preview)); } + + // Likewise + public static int getKeyPreviewPopupDismissDelay(SharedPreferences sp, + Resources resources) { + return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, + Integer.toString(resources.getInteger(R.integer.config_delay_after_preview)))); + } + private static boolean isBigramSuggestionEnabled(SharedPreferences sp, Resources resources, boolean autoCorrectEnabled) { final boolean showBigramSuggestionsOption = resources.getBoolean( @@ -225,11 +241,13 @@ public class Settings extends PreferenceActivity return sp.getBoolean(Settings.PREF_BIGRAM_SUGGESTIONS, resources.getBoolean( R.bool.config_default_bigram_suggestions)); } + private static boolean isBigramPredictionEnabled(SharedPreferences sp, Resources resources) { return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean( R.bool.config_default_bigram_prediction)); } + private static double getAutoCorrectionThreshold(SharedPreferences sp, Resources resources) { final String currentAutoCorrectionSetting = sp.getString( @@ -255,6 +273,7 @@ public class Settings extends PreferenceActivity } return autoCorrectionThreshold; } + private static SuggestedWords createSuggestPuncList(final String puncs) { SuggestedWords.Builder builder = new SuggestedWords.Builder(); if (puncs != null) { @@ -272,6 +291,7 @@ public class Settings extends PreferenceActivity private ListPreference mSettingsKeyPreference; private ListPreference mShowCorrectionSuggestionsPreference; private ListPreference mAutoCorrectionThreshold; + private ListPreference mKeyPreviewPopupDismissDelay; // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary private CheckBoxPreference mBigramSuggestion; // Prediction: use bigrams to predict the next word when there is no input for it yet @@ -297,6 +317,8 @@ public class Settings extends PreferenceActivity @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); + final Resources res = getResources(); + addPreferencesFromResource(R.xml.prefs); mInputLanguageSelection = (PreferenceScreen) findPreference(PREF_SUBTYPES); mInputLanguageSelection.setOnPreferenceClickListener(this); @@ -329,16 +351,14 @@ public class Settings extends PreferenceActivity (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS_KEY); final PreferenceGroup textCorrectionGroup = (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS_KEY); - final PreferenceGroup bigramGroup = - (PreferenceGroup) findPreference(PREF_NGRAM_SETTINGS_KEY); - final boolean showSettingsKeyOption = getResources().getBoolean( + final boolean showSettingsKeyOption = res.getBoolean( R.bool.config_enable_show_settings_key_option); if (!showSettingsKeyOption) { generalSettings.removePreference(mSettingsKeyPreference); } - final boolean showVoiceKeyOption = getResources().getBoolean( + final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { generalSettings.removePreference(mVoicePreference); @@ -348,43 +368,57 @@ public class Settings extends PreferenceActivity generalSettings.removePreference(findPreference(PREF_VIBRATE_ON)); } - final boolean showSubtypeSettings = getResources().getBoolean( - R.bool.config_enable_show_subtype_settings); - if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED - && !showSubtypeSettings) { + if (InputMethodServiceCompatWrapper.CAN_HANDLE_ON_CURRENT_INPUT_METHOD_SUBTYPE_CHANGED) { generalSettings.removePreference(findPreference(PREF_SUBTYPES)); } - final boolean showPopupOption = getResources().getBoolean( + final boolean showPopupOption = res.getBoolean( R.bool.config_enable_show_popup_on_keypress_option); if (!showPopupOption) { - generalSettings.removePreference(findPreference(PREF_POPUP_ON)); + generalSettings.removePreference(findPreference(PREF_KEY_PREVIEW_POPUP_ON)); } - final boolean showRecorrectionOption = getResources().getBoolean( + final boolean showRecorrectionOption = res.getBoolean( R.bool.config_enable_show_recorrection_option); if (!showRecorrectionOption) { generalSettings.removePreference(findPreference(PREF_RECORRECTION_ENABLED)); } - final boolean showQuickFixesOption = getResources().getBoolean( + final boolean showQuickFixesOption = res.getBoolean( R.bool.config_enable_quick_fixes_option); if (!showQuickFixesOption) { textCorrectionGroup.removePreference(findPreference(PREF_QUICK_FIXES)); } - final boolean showBigramSuggestionsOption = getResources().getBoolean( + final boolean showBigramSuggestionsOption = res.getBoolean( R.bool.config_enable_bigram_suggestions_option); if (!showBigramSuggestionsOption) { textCorrectionGroup.removePreference(findPreference(PREF_BIGRAM_SUGGESTIONS)); textCorrectionGroup.removePreference(findPreference(PREF_BIGRAM_PREDICTIONS)); } - final boolean showUsabilityModeStudyOption = getResources().getBoolean( + final boolean showUsabilityModeStudyOption = res.getBoolean( R.bool.config_enable_usability_study_mode_option); if (!showUsabilityModeStudyOption) { getPreferenceScreen().removePreference(findPreference(PREF_USABILITY_STUDY_MODE)); } + + mKeyPreviewPopupDismissDelay = + (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + final String[] entries = new String[] { + res.getString(R.string.key_preview_popup_dismiss_no_delay), + res.getString(R.string.key_preview_popup_dismiss_default_delay), + }; + final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger( + R.integer.config_delay_after_preview)); + mKeyPreviewPopupDismissDelay.setEntries(entries); + mKeyPreviewPopupDismissDelay.setEntryValues( + new String[] { "0", popupDismissDelayDefaultValue }); + if (null == mKeyPreviewPopupDismissDelay.getValue()) { + mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue); + } + mKeyPreviewPopupDismissDelay.setEnabled( + Settings.Values.isKeyPreviewPopupEnabled(prefs, res)); } @Override @@ -403,6 +437,7 @@ public class Settings extends PreferenceActivity } updateSettingsKeySummary(); updateShowCorrectionSuggestionsSummary(); + updateKeyPreviewPopupDelaySummary(); } @Override @@ -421,6 +456,12 @@ public class Settings extends PreferenceActivity .equals(mVoiceModeOff)) { showVoiceConfirmation(); } + } else if (key.equals(PREF_KEY_PREVIEW_POPUP_ON)) { + final ListPreference popupDismissDelay = + (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY); + if (null != popupDismissDelay) { + popupDismissDelay.setEnabled(prefs.getBoolean(PREF_KEY_PREVIEW_POPUP_ON, true)); + } } ensureConsistencyOfAutoCorrectionSettings(); mVoiceOn = !(prefs.getString(PREF_VOICE_SETTINGS_KEY, mVoiceModeOff) @@ -428,6 +469,7 @@ public class Settings extends PreferenceActivity updateVoiceModeSummary(); updateSettingsKeySummary(); updateShowCorrectionSuggestionsSummary(); + updateKeyPreviewPopupDelaySummary(); } @Override @@ -454,6 +496,11 @@ public class Settings extends PreferenceActivity [mSettingsKeyPreference.findIndexOfValue(mSettingsKeyPreference.getValue())]); } + private void updateKeyPreviewPopupDelaySummary() { + final ListPreference lp = mKeyPreviewPopupDismissDelay; + lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]); + } + private void showVoiceConfirmation() { mOkClicked = false; showDialog(VOICE_INPUT_CONFIRM_DIALOG); diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index d8012087b..8b51af880 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -92,10 +92,9 @@ public class SubtypeSwitcher { } public static void init(LatinIME service, SharedPreferences prefs) { + SubtypeLocale.init(service); sInstance.initialize(service, prefs); sInstance.updateAllParameters(); - - SubtypeLocale.init(service); } private SubtypeSwitcher() { @@ -281,14 +280,8 @@ public class SubtypeSwitcher { // "en_US" --> language: en & country: US // "en" --> language: en // "" --> the system locale - mLocaleSplitter.setString(inputLocaleStr); - if (mLocaleSplitter.hasNext()) { - String language = mLocaleSplitter.next(); - if (mLocaleSplitter.hasNext()) { - mInputLocale = new Locale(language, mLocaleSplitter.next()); - } else { - mInputLocale = new Locale(language); - } + if (!TextUtils.isEmpty(inputLocaleStr)) { + mInputLocale = Utils.constructLocaleFromString(inputLocaleStr); mInputLocaleStr = inputLocaleStr; } else { mInputLocale = mSystemLocale; @@ -420,7 +413,7 @@ public class SubtypeSwitcher { final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); final LatinKeyboard keyboard = switcher.getLatinKeyboard(); if (keyboard != null) { - keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getInputView()); + keyboard.updateShortcutKey(isShortcutImeReady(), switcher.getKeyboardView()); } } @@ -510,7 +503,7 @@ public class SubtypeSwitcher { private void triggerVoiceIME() { if (!mService.isInputViewShown()) return; VoiceProxy.getInstance().startListening(false, - KeyboardSwitcher.getInstance().getInputView().getWindowToken()); + KeyboardSwitcher.getInstance().getKeyboardView().getWindowToken()); } ////////////////////////////////////// @@ -549,12 +542,12 @@ public class SubtypeSwitcher { || mEnabledKeyboardSubtypesOfCurrentInputMethod.size() == 0) return; mCurrentKeyboardSubtypeIndex = getCurrentIndex(); mNextKeyboardSubtype = getNextKeyboardSubtypeInternal(mCurrentKeyboardSubtypeIndex); - Locale locale = new Locale(mNextKeyboardSubtype.getLocale()); - mNextLanguage = getDisplayLanguage(locale); + Locale locale = Utils.constructLocaleFromString(mNextKeyboardSubtype.getLocale()); + mNextLanguage = getFullDisplayName(locale, true); mPreviousKeyboardSubtype = getPreviousKeyboardSubtypeInternal( mCurrentKeyboardSubtypeIndex); - locale = new Locale(mPreviousKeyboardSubtype.getLocale()); - mPreviousLanguage = getDisplayLanguage(locale); + locale = Utils.constructLocaleFromString(mPreviousKeyboardSubtype.getLocale()); + mPreviousLanguage = getFullDisplayName(locale, true); } private int normalize(int index) { @@ -591,11 +584,12 @@ public class SubtypeSwitcher { } public static String getDisplayLanguage(Locale locale) { - return toTitleCase(locale.getDisplayLanguage(locale)); + return toTitleCase(SubtypeLocale.getFullDisplayName(locale)); } public static String getMiddleDisplayLanguage(Locale locale) { - return toTitleCase((new Locale(locale.getLanguage()).getDisplayLanguage(locale))); + return toTitleCase((Utils.constructLocaleFromString( + locale.getLanguage()).getDisplayLanguage(locale))); } public static String getShortDisplayLanguage(Locale locale) { diff --git a/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java new file mode 100644 index 000000000..4a3f42d5d --- /dev/null +++ b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java @@ -0,0 +1,43 @@ +/* + * 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 com.android.inputmethod.compat.SuggestionSpanUtils; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class SuggestionSpanPickedNotificationReceiver extends BroadcastReceiver { + private static final boolean DBG = LatinImeLogger.sDBG; + private static final String TAG = + SuggestionSpanPickedNotificationReceiver.class.getSimpleName(); + + @Override + public void onReceive(Context context, Intent intent) { + if (SuggestionSpanUtils.ACTION_SUGGESTION_PICKED.equals(intent.getAction())) { + if (DBG) { + final String before = intent.getStringExtra( + SuggestionSpanUtils.SUGGESTION_SPAN_PICKED_BEFORE); + final String after = intent.getStringExtra( + SuggestionSpanUtils.SUGGESTION_SPAN_PICKED_AFTER); + Log.d(TAG, "Received notification picked: " + before + "," + after); + } + } + } +} diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java index a32a6461a..5b615ca29 100644 --- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Google Inc. + * Copyright (C) 2010 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 @@ -158,7 +158,7 @@ public class UserBigramDictionary extends ExpandableDictionary { * Pair will be added to the userbigram database. */ public int addBigrams(String word1, String word2) { - // remove caps + // remove caps if second word is autocapitalized if (mIme != null && mIme.getCurrentWord().isAutoCapitalized()) { word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1); } diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index d165de32d..6bdc0a857 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import com.android.inputmethod.compat.InputMethodInfoCompatWrapper; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; @@ -43,7 +44,10 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.List; import java.util.Locale; public class Utils { @@ -108,7 +112,34 @@ public class Utils { } public static boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManagerCompatWrapper imm) { - return imm.getEnabledInputMethodList().size() > 1 + final List<InputMethodInfoCompatWrapper> enabledImis = imm.getEnabledInputMethodList(); + + // Filters out IMEs that have auxiliary subtypes only (including either implicitly or + // explicitly enabled ones). + final ArrayList<InputMethodInfoCompatWrapper> filteredImis = + new ArrayList<InputMethodInfoCompatWrapper>(); + + outerloop: + for (InputMethodInfoCompatWrapper imi : enabledImis) { + // We can return true immediately after we find two or more filtered IMEs. + if (filteredImis.size() > 1) return true; + final List<InputMethodSubtypeCompatWrapper> subtypes = + imm.getEnabledInputMethodSubtypeList(imi, true); + // IMEs that have no subtypes should be included. + if (subtypes.isEmpty()) { + filteredImis.add(imi); + continue; + } + // IMEs that have one or more non-auxiliary subtypes should be included. + for (InputMethodSubtypeCompatWrapper subtype : subtypes) { + if (!subtype.isAuxiliary()) { + filteredImis.add(imi); + continue outerloop; + } + } + } + + return filteredImis.size() > 1 // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled // input method subtype (The current IME should be LatinIME.) || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; @@ -537,8 +568,6 @@ public class Utils { return KeyboardId.MODE_IM; } else if (variation == InputType.TYPE_TEXT_VARIATION_FILTER) { return KeyboardId.MODE_TEXT; - } else if (variation == InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { - return KeyboardId.MODE_WEB; } else { return KeyboardId.MODE_TEXT; } @@ -662,4 +691,28 @@ public class Utils { res.updateConfiguration(conf, res.getDisplayMetrics()); return saveLocale; } + + private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>(); + + public static Locale constructLocaleFromString(String localeStr) { + if (localeStr == null) + return null; + synchronized (sLocaleCache) { + if (sLocaleCache.containsKey(localeStr)) + return sLocaleCache.get(localeStr); + Locale retval = null; + String[] localeParams = localeStr.split("_", 3); + if (localeParams.length == 1) { + retval = new Locale(localeParams[0]); + } else if (localeParams.length == 2) { + retval = new Locale(localeParams[0], localeParams[1]); + } else if (localeParams.length == 3) { + retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); + } + if (retval != null) { + sLocaleCache.put(localeStr, retval); + } + return retval; + } + } } diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index 2389d4e3c..4377373d2 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -39,6 +39,7 @@ public class WhitelistDictionary extends Dictionary { public static WhitelistDictionary init(Context context) { synchronized (sInstance) { if (context != null) { + // Wordlist is initialized by the proper language in Suggestion.java#init sInstance.initWordlist( context.getResources().getStringArray(R.array.wordlist_whitelist)); } else { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index cf0592920..af5e4b179 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -62,7 +62,7 @@ public class WordComposer { mYCoordinates = new int[N]; } - WordComposer(WordComposer source) { + public WordComposer(WordComposer source) { init(source); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java new file mode 100644 index 000000000..e3407a269 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java @@ -0,0 +1,114 @@ +/* + * 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.spellcheck; + +import android.content.Context; +import android.content.res.Resources; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.Dictionary.DataType; +import com.android.inputmethod.latin.Dictionary.WordCallback; +import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.WordComposer; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; + +/** + * Implements spell checking methods. + */ +public class SpellChecker { + + public final Dictionary mDictionary; + + public SpellChecker(final Context context, final Locale locale) { + final Resources resources = context.getResources(); + final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); + mDictionary = DictionaryFactory.createDictionaryFromManager(context, locale, + fallbackResourceId); + } + + // Note : this must be reentrant + /** + * Finds out whether a word is in the dictionary or not. + * + * @param text the sequence containing the word to check for. + * @param start the index of the first character of the word in text. + * @param end the index of the next-to-last character in text. + * @return true if the word is in the dictionary, false otherwise. + */ + public boolean isCorrect(final CharSequence text, final int start, final int end) { + return mDictionary.isValidWord(text.subSequence(start, end)); + } + + private static class SuggestionsGatherer implements WordCallback { + private final int DEFAULT_SUGGESTION_LENGTH = 16; + private final List<String> mSuggestions = new LinkedList<String>(); + private int[] mScores = new int[DEFAULT_SUGGESTION_LENGTH]; + private int mLength = 0; + + synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score, + int dicTypeId, DataType dataType) { + if (mLength >= mScores.length) { + final int newLength = mScores.length * 2; + mScores = new int[newLength]; + } + final int positionIndex = Arrays.binarySearch(mScores, 0, mLength, score); + // binarySearch returns the index if the element exists, and -<insertion index> - 1 + // if it doesn't. See documentation for binarySearch. + final int insertionIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; + System.arraycopy(mScores, insertionIndex, mScores, insertionIndex + 1, + mLength - insertionIndex); + mLength += 1; + mScores[insertionIndex] = score; + mSuggestions.add(insertionIndex, new String(word, wordOffset, wordLength)); + return true; + } + + public List<String> getGatheredSuggestions() { + return mSuggestions; + } + } + + // Note : this must be reentrant + /** + * Gets a list of suggestions for a specific string. + * + * This returns a list of possible corrections for the text passed as an + * arguments. It may split or group words, and even perform grammatical + * analysis. + * + * @param text the sequence containing the word to check for. + * @param start the index of the first character of the word in text. + * @param end the index of the next-to-last character in text. + * @return a list of possible suggestions to replace the text. + */ + public List<String> getSuggestions(final CharSequence text, final int start, final int end) { + final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(); + final WordComposer composer = new WordComposer(); + for (int i = start; i < end; ++i) { + int character = text.charAt(i); + composer.add(character, new int[] { character }, + WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); + } + mDictionary.getWords(composer, suggestionsGatherer); + return suggestionsGatherer.getGatheredSuggestions(); + } +} |