From 36fcf25833f7c8876cbc8286e0c159b052dc2626 Mon Sep 17 00:00:00 2001 From: Amith Yamasani Date: Mon, 12 Oct 2009 13:48:35 -0700 Subject: [PATCH] Input language switching. Bug: 2331173 New feature to enable fast switching between input languages for multilingual users. Keyboard settings lets you select a bunch of languages to switch between from the Latin IME. Use the Globe icon to toggle between languages. Needs more work and some layout changes in specialized keyboards. Also added a Russian keyboard (needs some pixel TLC at the right edge). --- AndroidManifest.xml | 7 + res/drawable-hdpi/sym_keyboard_globe.png | Bin 0 -> 2093 bytes res/values/keycodes.xml | 24 +++ res/values/strings.xml | 3 + res/xml-de/kbd_qwerty.xml | 16 +- res/xml-fr/kbd_qwerty.xml | 16 +- res/xml-ru/kbd_qwerty.xml | 150 +++++++++++++++ res/xml/kbd_qwerty.xml | 20 +- res/xml/kbd_symbols.xml | 2 +- res/xml/language_prefs.xml | 19 ++ res/xml/prefs.xml | 8 + .../inputmethod/latin/BinaryDictionary.java | 2 + .../android/inputmethod/latin/Dictionary.java | 5 + .../latin/InputLanguageSelection.java | 178 ++++++++++++++++++ .../inputmethod/latin/KeyboardSwitcher.java | 40 +++- .../android/inputmethod/latin/LatinIME.java | 164 +++++++++++++--- .../inputmethod/latin/LatinKeyboard.java | 89 ++++++++- .../inputmethod/latin/LatinKeyboardView.java | 1 + .../android/inputmethod/latin/Suggest.java | 6 + 19 files changed, 689 insertions(+), 61 deletions(-) create mode 100644 res/drawable-hdpi/sym_keyboard_globe.png create mode 100644 res/values/keycodes.xml create mode 100755 res/xml-ru/kbd_qwerty.xml create mode 100644 res/xml/language_prefs.xml create mode 100644 src/com/android/inputmethod/latin/InputLanguageSelection.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 697dce28a..d32d94748 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -23,6 +23,13 @@ + + + + + + diff --git a/res/drawable-hdpi/sym_keyboard_globe.png b/res/drawable-hdpi/sym_keyboard_globe.png new file mode 100644 index 0000000000000000000000000000000000000000..fa747642d9d91642bf56f769150e095fa372b542 GIT binary patch literal 2093 zcmV+|2-5e7P)W#8 zh3CX`2%_tMLWuoO#z9V3DM2925Oj1EIJ`C?_A*95Nl?*MLE}=6j256FOw(0In3I@v zj^AYTQY2swP;)RNQp{2Uy_6A|;4lf8BC^ba^CHAs1c_JxEIHu4Iy*bt%fZ3Ha%N^m z3Qds7WHSd29B8MPj{*J$i~^p~{RENK&Sx8CL}W3*5#R-g03-s^Pn|kddHeS5c43y* z*Vos3=FFKIe}DfRcqSPT3Gf3f0lY=lmA{M+^JJW8lJ@{9hYlV3dU$wvNSH^Pfc7Ia zc>ZI+2Y}@OSAZ3flFj8$3fc}N699+}3=GU{Zf?F|?1k5_UvJIL&8@Jx{ESGwL)<Mv(Gb#7C5hw#x?KaQX7(B4=l37yQGcN00h9ZroVj)6?_R z99l+3#(8>Hr=LJ7)kjWFPEOPP`}eyY9UZMhLqp5!>+9Qa9G^LP@?_Dvb?XA*){cTh zZHf@{oke6yTOhO5)6>%%f1HH2Y15|fYin!!A+loeB~49DBNR0lNfMPxh5d01>B`E= z8$Lcho-Qsf<}oob=W#JaN?lb|ReK z+qA{T#>N`@Jw#@T@{U9Rq*rRSX4oE$kAkaX+Tt$Mci#l^+nxx2d`JaXhnZEtUHAMa1_&hf6Ut`6+?_xC>> z85wyZjxzMA$jHdAa1Oj@&%t|A0q+9bh?tD`okg^-OM-%e{2Iqp{S=ky<42B%Q zd`Sl8*Z-hatIZ)A5+HhXN~KbRR~%1IPmfu>dbPhnjgjKqym@m}e0;nQ5O^-zf}bR= zTD2-jqtPgJI-N2kB*c^b235fF&Ye3lKI|1D-D8*y2|;38hE9xNiO<#5)y>}C-cFq6 zjg3!DO-(?^yF&i407W1}*xA`x&s9)WRMkTPA9Z(k>%*8zLoooOf2Bxin7+mL8TC;2!hQN(z0ogcDZG8MWXef8h_9X^m-P z+9IjQvJxYfHm0=&NF&b7dK56cntV>_VR%nHc<`W$%>d=Xg>!ht=;-JZh#C)0B^vgg ze9xe{*Q{BCR+m6-n&cc;=I7_ z=sS{&7cVv?BqW4FQMDk&hmcH9PqVKR1^CrUlpCm@qNAgoQGT$MmzI`(1B?BWtTM(~ zMi5I7IaPgh#vnif=-_=!GGN1%qNO-1y&z$k#>U^<+S-2SeXFdj4F7{VE1c8SN>g7S z%$F=_>rjj*Lh9dV>T82-BO`_aOJkMC0t~9M2Som_ckkXEq)+Hka^Pp2 z6Kjk)@@(JsQn7F&hv#8#;;?~<{Rtv4PMD%d)|n#nvd1wpuAVW4XT}}Lh>v6VqnIht zk<6?oAM-u1;>pQ5*(u}Z;SZ~{T>qsHtzP9wPb@Et%8b`N+FLk + + + + + -102 + diff --git a/res/values/strings.xml b/res/values/strings.xml index 753d0dc9e..3b3965e09 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -208,4 +208,7 @@ ".gov" ".edu" + + + Select input languages diff --git a/res/xml-de/kbd_qwerty.xml b/res/xml-de/kbd_qwerty.xml index 56113e217..2da609ca8 100755 --- a/res/xml-de/kbd_qwerty.xml +++ b/res/xml-de/kbd_qwerty.xml @@ -100,7 +100,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + @@ -116,9 +117,7 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - - - + @@ -156,7 +153,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + diff --git a/res/xml-fr/kbd_qwerty.xml b/res/xml-fr/kbd_qwerty.xml index d47042ea7..7c1b24bad 100644 --- a/res/xml-fr/kbd_qwerty.xml +++ b/res/xml-fr/kbd_qwerty.xml @@ -102,7 +102,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + @@ -118,9 +119,7 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - - - + @@ -158,7 +155,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + diff --git a/res/xml-ru/kbd_qwerty.xml b/res/xml-ru/kbd_qwerty.xml new file mode 100755 index 000000000..c0afbd7b7 --- /dev/null +++ b/res/xml-ru/kbd_qwerty.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/kbd_qwerty.xml b/res/xml/kbd_qwerty.xml index 0493b9932..d914f46d2 100755 --- a/res/xml/kbd_qwerty.xml +++ b/res/xml/kbd_qwerty.xml @@ -114,7 +114,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + @@ -130,10 +131,12 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + + - + android:keyWidth="15%p"/--> @@ -170,7 +175,8 @@ android:popupKeyboard="@xml/kbd_popup_template" android:popupCharacters="_" android:keyWidth="20%p" android:keyEdgeFlags="left"/> - + @@ -181,4 +187,4 @@ android:keyWidth="20%p" android:keyEdgeFlags="right"/> - + diff --git a/res/xml/kbd_symbols.xml b/res/xml/kbd_symbols.xml index b1b75c620..5a30c7b8f 100755 --- a/res/xml/kbd_symbols.xml +++ b/res/xml/kbd_symbols.xml @@ -120,7 +120,7 @@ android:iconPreview="@drawable/sym_keyboard_feedback_delete" android:isRepeatable="true"/> - + + + + + diff --git a/res/xml/prefs.xml b/res/xml/prefs.xml index 2fc82aadb..d5075c53a 100644 --- a/res/xml/prefs.xml +++ b/res/xml/prefs.xml @@ -37,6 +37,14 @@ android:defaultValue="true" /> + + + + diff --git a/src/com/android/inputmethod/latin/BinaryDictionary.java b/src/com/android/inputmethod/latin/BinaryDictionary.java index 14c543514..36991845c 100644 --- a/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -125,8 +125,10 @@ public class BinaryDictionary extends Dictionary { return isValidWordNative(mNativeDict, chars, chars.length); } + @Override public synchronized void close() { if (mNativeDict != 0) { + System.err.println("Closing BinaryDictionary"); closeNative(mNativeDict); mNativeDict = 0; } diff --git a/src/com/android/inputmethod/latin/Dictionary.java b/src/com/android/inputmethod/latin/Dictionary.java index fdf34264a..6c1c856e7 100644 --- a/src/com/android/inputmethod/latin/Dictionary.java +++ b/src/com/android/inputmethod/latin/Dictionary.java @@ -86,4 +86,9 @@ abstract public class Dictionary { return true; } + /** + * Override to clean up any resources. + */ + public void close() { + } } diff --git a/src/com/android/inputmethod/latin/InputLanguageSelection.java b/src/com/android/inputmethod/latin/InputLanguageSelection.java new file mode 100644 index 000000000..5bd1391c3 --- /dev/null +++ b/src/com/android/inputmethod/latin/InputLanguageSelection.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2008-2009 Google Inc. + * + * 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 java.text.Collator; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.PreferenceActivity; +import android.preference.PreferenceGroup; +import android.preference.PreferenceManager; +import android.util.Log; + +public class InputLanguageSelection extends PreferenceActivity { + + private String mSelectedLanguages; + private ArrayList mAvailableLanguages = new ArrayList(); + + private static class Loc implements Comparable { + static Collator sCollator = Collator.getInstance(); + + String label; + Locale locale; + + public Loc(String label, Locale locale) { + this.label = label; + this.locale = locale; + } + + @Override + public String toString() { + return this.label; + } + + public int compareTo(Object o) { + return sCollator.compare(this.label, ((Loc) o).label); + } + } + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + addPreferencesFromResource(R.xml.language_prefs); + // Get the settings preferences + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + mSelectedLanguages = sp.getString(LatinIME.PREF_SELECTED_LANGUAGES, ""); + String[] languageList = mSelectedLanguages.split(","); + mAvailableLanguages = getUniqueLocales(); + PreferenceGroup parent = getPreferenceScreen(); + for (int i = 0; i < mAvailableLanguages.size(); i++) { + CheckBoxPreference pref = new CheckBoxPreference(this); + Locale locale = mAvailableLanguages.get(i).locale; + pref.setTitle(locale.getDisplayName(locale)); + boolean checked = isLocaleIn(locale, languageList); + pref.setChecked(checked); + parent.addPreference(pref); + } + } + + private boolean isLocaleIn(Locale locale, String[] list) { + String lang = get5Code(locale); + for (int i = 0; i < list.length; i++) { + if (lang.equalsIgnoreCase(list[i])) return true; + } + // If it matches the current locale + Locale displayLocale = getResources().getConfiguration().locale; + if (lang.equalsIgnoreCase(get5Code(displayLocale))) { + return true; + } + return false; + } + + private String get5Code(Locale locale) { + return locale.getLanguage() + "_" + locale.getCountry(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + // Save the selected languages + String checkedLanguages = ""; + PreferenceGroup parent = getPreferenceScreen(); + int count = parent.getPreferenceCount(); + for (int i = 0; i < count; i++) { + CheckBoxPreference pref = (CheckBoxPreference) parent.getPreference(i); + if (pref.isChecked()) { + Locale locale = mAvailableLanguages.get(i).locale; + checkedLanguages += get5Code(locale) + ","; + } + } + if (checkedLanguages.length() < 1) checkedLanguages = null; // Save null + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + Editor editor = sp.edit(); + editor.putString(LatinIME.PREF_SELECTED_LANGUAGES, checkedLanguages); + editor.commit(); + } + + ArrayList getUniqueLocales() { + String[] locales = getAssets().getLocales(); + Arrays.sort(locales); + ArrayList uniqueLocales = new ArrayList(); + + final int origSize = locales.length; + Loc[] preprocess = new Loc[origSize]; + int finalSize = 0; + for (int i = 0 ; i < origSize; i++ ) { + String s = locales[i]; + int len = s.length(); + if (len == 5) { + String language = s.substring(0, 2); + String country = s.substring(3, 5); + Locale l = new Locale(language, country); + + if (finalSize == 0) { + preprocess[finalSize++] = + new Loc(toTitleCase(l.getDisplayName(l)), 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].locale.getLanguage().equals( + language)) { + preprocess[finalSize-1].label = toTitleCase( + preprocess[finalSize-1].locale.getDisplayName()); + preprocess[finalSize++] = + new Loc(toTitleCase(l.getDisplayName()), l); + } else { + String displayName; + if (s.equals("zz_ZZ")) { + } else { + displayName = toTitleCase(l.getDisplayName(l)); + preprocess[finalSize++] = new Loc(displayName, l); + } + } + } + } + } + for (int i = 0; i < finalSize ; i++) { + uniqueLocales.add(preprocess[i]); + } + return uniqueLocales; + } + + private static String toTitleCase(String s) { + if (s.length() == 0) { + return s; + } + + return Character.toUpperCase(s.charAt(0)) + s.substring(1); + } + +} diff --git a/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/src/com/android/inputmethod/latin/KeyboardSwitcher.java index c7f90946d..03b008e34 100644 --- a/src/com/android/inputmethod/latin/KeyboardSwitcher.java +++ b/src/com/android/inputmethod/latin/KeyboardSwitcher.java @@ -17,8 +17,14 @@ package com.android.inputmethod.latin; import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.inputmethodservice.InputMethodService; + public class KeyboardSwitcher { public static final int MODE_TEXT = 1; @@ -42,7 +48,8 @@ public class KeyboardSwitcher { private static final int SYMBOLS_MODE_STATE_SYMBOL = 2; LatinKeyboardView mInputView; - LatinIME mContext; + Context mContext; + InputMethodService mInputMethodService; private KeyboardId mSymbolsId; private KeyboardId mSymbolsShiftedId; @@ -58,12 +65,26 @@ public class KeyboardSwitcher { private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; private int mLastDisplayWidth; + private Locale mInputLocale; + private boolean mEnableMultipleLanguages; - KeyboardSwitcher(LatinIME context) { + KeyboardSwitcher(Context context, InputMethodService ims) { mContext = context; mKeyboards = new HashMap(); mSymbolsId = new KeyboardId(R.xml.kbd_symbols); mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift); + mInputMethodService = ims; + } + + /** + * Sets the input locale, when there are multiple locales for input. + * If no locale switching is required, then the locale should be set to null. + * @param locale the current input locale, or null for default locale with no locale + * button. + */ + void setInputLocale(Locale locale, boolean enableMultipleLanguages) { + mInputLocale = locale; + mEnableMultipleLanguages = enableMultipleLanguages; } void setInputView(LatinKeyboardView inputView) { @@ -75,7 +96,7 @@ public class KeyboardSwitcher { // Configuration change is coming after the keyboard gets recreated. So don't rely on that. // If keyboards have already been made, check if we have a screen width change and // create the keyboard layouts again at the correct orientation - int displayWidth = mContext.getMaxWidth(); + int displayWidth = mInputMethodService.getMaxWidth(); if (displayWidth == mLastDisplayWidth) return; mLastDisplayWidth = displayWidth; if (!forceCreate) mKeyboards.clear(); @@ -136,6 +157,11 @@ public class KeyboardSwitcher { } mCurrentId = id; + if (mEnableMultipleLanguages) { + keyboard.setLanguage(mInputLocale); + } else { + keyboard.setLanguage(null); + } mInputView.setKeyboard(keyboard); keyboard.setShifted(false); keyboard.setShiftLocked(keyboard.isShiftLocked()); @@ -145,6 +171,11 @@ public class KeyboardSwitcher { private LatinKeyboard getKeyboard(KeyboardId id) { if (!mKeyboards.containsKey(id)) { + Resources orig = mContext.getResources(); + Configuration conf = orig.getConfiguration(); + Locale saveLocale = conf.locale; + conf.locale = mInputLocale; + orig.updateConfiguration(conf, null); LatinKeyboard keyboard = new LatinKeyboard( mContext, id.mXml, id.mMode); if (id.mMode == KEYBOARDMODE_NORMAL @@ -158,6 +189,9 @@ public class KeyboardSwitcher { keyboard.enableShiftLock(); } mKeyboards.put(id, keyboard); + + conf.locale = saveLocale; + orig.updateConfiguration(conf, null); } return mKeyboards.get(id); } diff --git a/src/com/android/inputmethod/latin/LatinIME.java b/src/com/android/inputmethod/latin/LatinIME.java index d72710e5b..1a08b2c0a 100644 --- a/src/com/android/inputmethod/latin/LatinIME.java +++ b/src/com/android/inputmethod/latin/LatinIME.java @@ -16,14 +16,24 @@ package com.android.inputmethod.latin; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import android.app.AlertDialog; +import android.backup.BackupManager; import android.content.BroadcastReceiver; import android.content.Context; +import android.content.ContextWrapper; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; import android.content.res.Configuration; +import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.inputmethodservice.Keyboard; import android.inputmethodservice.KeyboardView; @@ -49,16 +59,13 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; - /** * Input method implementation for Qwerty'ish keyboard. */ public class LatinIME extends InputMethodService - implements KeyboardView.OnKeyboardActionListener { + implements KeyboardView.OnKeyboardActionListener, + SharedPreferences.OnSharedPreferenceChangeListener { + static final boolean DEBUG = false; static final boolean TRACE = false; @@ -68,6 +75,8 @@ public class LatinIME extends InputMethodService private static final String PREF_QUICK_FIXES = "quick_fixes"; private static final String PREF_SHOW_SUGGESTIONS = "show_suggestions"; private static final String PREF_AUTO_COMPLETE = "auto_complete"; + public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; + public static final String PREF_INPUT_LANGUAGE = "input_language"; private static final int MSG_UPDATE_SUGGESTIONS = 0; private static final int MSG_START_TUTORIAL = 1; @@ -105,7 +114,9 @@ public class LatinIME extends InputMethodService private UserDictionary mUserDictionary; private ContactsDictionary mContactsDictionary; private ExpandableDictionary mAutoDictionary; - + + Resources mResources; + private String mLocale; private StringBuilder mComposing = new StringBuilder(); @@ -144,7 +155,12 @@ public class LatinIME extends InputMethodService private String mWordSeparators; private String mSentenceSeparators; - + private int mCurrentInputLocale = 0; + private String mInputLanguage; + private String[] mSelectedLanguageArray; + private String mSelectedLanguagesList; + private boolean mRefreshKeyboardRequired; + Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -173,32 +189,61 @@ public class LatinIME extends InputMethodService @Override public void onCreate() { super.onCreate(); //setStatusIcon(R.drawable.ime_qwerty); - mKeyboardSwitcher = new KeyboardSwitcher(this); - final Configuration conf = getResources().getConfiguration(); - initSuggest(conf.locale.toString()); + mKeyboardSwitcher = new KeyboardSwitcher(this, this); + mResources = getResources(); + final Configuration conf = mResources.getConfiguration(); + mInputLanguage = getPersistedInputLanguage(); + mSelectedLanguagesList = getSelectedInputLanguages(); + boolean enableMultipleLanguages = mSelectedLanguagesList != null + && mSelectedLanguagesList.split(",").length > 1; + if (mInputLanguage == null) { + mInputLanguage = conf.locale.toString(); + } + initSuggest(mInputLanguage); + mKeyboardSwitcher.setInputLocale(conf.locale, enableMultipleLanguages); mOrientation = conf.orientation; - mVibrateDuration = getResources().getInteger(R.integer.vibrate_duration_ms); + mVibrateDuration = mResources.getInteger(R.integer.vibrate_duration_ms); // register to receive ringer mode changes for silent mode IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION); registerReceiver(mReceiver, filter); + PreferenceManager.getDefaultSharedPreferences(this) + .registerOnSharedPreferenceChangeListener(this); } - + private void initSuggest(String locale) { mLocale = locale; + + Resources orig = getResources(); + Configuration conf = orig.getConfiguration(); + Locale saveLocale = conf.locale; + conf.locale = new Locale(locale); + orig.updateConfiguration(conf, orig.getDisplayMetrics()); + if (mSuggest != null) { + mSuggest.close(); + } mSuggest = new Suggest(this, R.raw.main); mSuggest.setCorrectionMode(mCorrectionMode); mUserDictionary = new UserDictionary(this); - mContactsDictionary = new ContactsDictionary(this); - mAutoDictionary = new AutoDictionary(this); + if (mContactsDictionary == null) { + mContactsDictionary = new ContactsDictionary(this); + } + // TODO: Save and restore the dictionary for the current input language. + if (mAutoDictionary == null) { + mAutoDictionary = new AutoDictionary(this); + } mSuggest.setUserDictionary(mUserDictionary); mSuggest.setContactsDictionary(mContactsDictionary); mSuggest.setAutoDictionary(mAutoDictionary); - mWordSeparators = getResources().getString(R.string.word_separators); - mSentenceSeparators = getResources().getString(R.string.sentence_separators); + + mWordSeparators = mResources.getString(R.string.word_separators); + mSentenceSeparators = mResources.getString(R.string.sentence_separators); + + conf.locale = saveLocale; + orig.updateConfiguration(conf, orig.getDisplayMetrics()); } - + @Override public void onDestroy() { mUserDictionary.close(); mContactsDictionary.close(); @@ -216,10 +261,7 @@ public class LatinIME extends InputMethodService commitTyped(getCurrentInputConnection()); mOrientation = conf.orientation; } - if (mKeyboardSwitcher == null) { - mKeyboardSwitcher = new KeyboardSwitcher(this); - } - mKeyboardSwitcher.makeKeyboards(true); + reloadKeyboards(); super.onConfigurationChanged(conf); } @@ -253,6 +295,11 @@ public class LatinIME extends InputMethodService return; } + if (mRefreshKeyboardRequired) { + mRefreshKeyboardRequired = false; + toggleLanguage(true); + } + mKeyboardSwitcher.makeKeyboards(false); TextEntryState.newSession(this); @@ -499,6 +546,15 @@ public class LatinIME extends InputMethodService return super.onKeyUp(keyCode, event); } + private void reloadKeyboards() { + if (mKeyboardSwitcher == null) { + mKeyboardSwitcher = new KeyboardSwitcher(this, this); + } + mKeyboardSwitcher.setInputLocale(new Locale(mInputLanguage), + getSelectedInputLanguages() != null); + mKeyboardSwitcher.makeKeyboards(true); + } + private void commitTyped(InputConnection inputConnection) { if (mPredicting) { mPredicting = false; @@ -601,6 +657,9 @@ public class LatinIME extends InputMethodService case LatinKeyboardView.KEYCODE_OPTIONS: showOptionsMenu(); break; + case LatinKeyboardView.KEYCODE_F1: + toggleLanguage(false); + break; case LatinKeyboardView.KEYCODE_SHIFT_LONGPRESS: if (mCapsLock) { handleShift(); @@ -674,7 +733,6 @@ public class LatinIME extends InputMethodService private void handleShift() { mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE); - Keyboard currentKeyboard = mInputView.getKeyboard(); if (mKeyboardSwitcher.isAlphabetMode()) { // Alphabet keyboard checkToggleCapsLock(); @@ -966,9 +1024,32 @@ public class LatinIME extends InputMethodService } } } - + + private void toggleLanguage(boolean reset) { + final String [] languages = mSelectedLanguageArray; + if (reset) mCurrentInputLocale = -1; + mCurrentInputLocale = (mCurrentInputLocale + 1) + % (languages != null ? languages.length : 1); + mInputLanguage = languages != null ? languages[mCurrentInputLocale] : + getResources().getConfiguration().locale.getLanguage(); + int currentKeyboardMode = mKeyboardSwitcher.getKeyboardMode(); + reloadKeyboards(); + mKeyboardSwitcher.makeKeyboards(true); + mKeyboardSwitcher.setKeyboardMode(currentKeyboardMode, 0); + initSuggest(mInputLanguage); + persistInputLanguage(mInputLanguage); + updateShiftKeyState(getCurrentInputEditorInfo()); + } + + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (PREF_SELECTED_LANGUAGES.equals(key)) { + updateSelectedLanguages(sharedPreferences.getString(key, null)); + mRefreshKeyboardRequired = true; + } + } + public void swipeLeft() { - //handleBackspace(); } public void swipeDown() { @@ -1089,11 +1170,38 @@ public class LatinIME extends InputMethodService if (AutoText.getSize(mInputView) < 1) mQuickFixes = true; mShowSuggestions = sp.getBoolean(PREF_SHOW_SUGGESTIONS, true) & mQuickFixes; boolean autoComplete = sp.getBoolean(PREF_AUTO_COMPLETE, - getResources().getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions; + mResources.getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions; mAutoCorrectOn = mSuggest != null && (autoComplete || mQuickFixes); mCorrectionMode = autoComplete ? Suggest.CORRECTION_FULL : (mQuickFixes ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE); + String languageList = sp.getString(PREF_SELECTED_LANGUAGES, null); + updateSelectedLanguages(languageList); + } + + private void updateSelectedLanguages(String languageList) { + if (languageList != null && languageList.length() > 1) { + mSelectedLanguageArray = languageList.split(","); + } else { + mSelectedLanguageArray = null; + } + } + + private String getPersistedInputLanguage() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + return sp.getString(PREF_INPUT_LANGUAGE, null); + } + + private void persistInputLanguage(String inputLanguage) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + Editor editor = sp.edit(); + editor.putString(PREF_INPUT_LANGUAGE, inputLanguage); + editor.commit(); + } + + private String getSelectedInputLanguages() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + return sp.getString(PREF_SELECTED_LANGUAGES, null); } private void showOptionsMenu() { @@ -1120,7 +1228,7 @@ public class LatinIME extends InputMethodService } } }); - builder.setTitle(getResources().getString(R.string.english_ime_name)); + builder.setTitle(mResources.getString(R.string.english_ime_name)); mOptionsDialog = builder.create(); Window window = mOptionsDialog.getWindow(); WindowManager.LayoutParams lp = window.getAttributes(); @@ -1139,7 +1247,7 @@ public class LatinIME extends InputMethodService updateShiftKeyState(getCurrentInputEditorInfo()); } - + @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) { super.dump(fd, fout, args); diff --git a/src/com/android/inputmethod/latin/LatinKeyboard.java b/src/com/android/inputmethod/latin/LatinKeyboard.java index e68e01ded..05913122f 100644 --- a/src/com/android/inputmethod/latin/LatinKeyboard.java +++ b/src/com/android/inputmethod/latin/LatinKeyboard.java @@ -16,9 +16,17 @@ package com.android.inputmethod.latin; +import java.util.Locale; + import android.content.Context; import android.content.res.Resources; import android.content.res.XmlResourceParser; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.Paint.Align; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.inputmethodservice.Keyboard; import android.view.inputmethod.EditorInfo; @@ -29,8 +37,14 @@ public class LatinKeyboard extends Keyboard { private Drawable mShiftLockPreviewIcon; private Drawable mOldShiftIcon; private Drawable mOldShiftPreviewIcon; + private Drawable mSpaceIcon; private Key mShiftKey; private Key mEnterKey; + private Key mF1Key; + private Key mSpaceKey; + private Locale mLocale; + private Resources mRes; + private int mMode; private int mExtensionResId; @@ -48,12 +62,15 @@ public class LatinKeyboard extends Keyboard { public LatinKeyboard(Context context, int xmlLayoutResId, int mode) { super(context, xmlLayoutResId, mode); - Resources res = context.getResources(); + final Resources res = context.getResources(); + mMode = mode; + mRes = res; mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked); mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked); mShiftLockPreviewIcon.setBounds(0, 0, mShiftLockPreviewIcon.getIntrinsicWidth(), mShiftLockPreviewIcon.getIntrinsicHeight()); + mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space); sSpacebarVerticalCorrection = res.getDimensionPixelOffset( R.dimen.spacebar_vertical_correction); } @@ -67,12 +84,20 @@ public class LatinKeyboard extends Keyboard { protected Key createKeyFromXml(Resources res, Row parent, int x, int y, XmlResourceParser parser) { Key key = new LatinKey(res, parent, x, y, parser); - if (key.codes[0] == 10) { + switch (key.codes[0]) { + case 10: mEnterKey = key; + break; + case LatinKeyboardView.KEYCODE_F1: + mF1Key = key; + break; + case 32: + mSpaceKey = key; + break; } return key; } - + void setImeOptions(Resources res, int mode, int options) { if (mEnterKey != null) { // Reset some of the rarely used attributes. @@ -183,7 +208,7 @@ public class LatinKeyboard extends Keyboard { } return shiftChanged; } - + @Override public boolean isShifted() { if (mShiftKey != null) { @@ -201,6 +226,62 @@ public class LatinKeyboard extends Keyboard { return mExtensionResId; } + private void setF1Key() { + if (mF1Key == null) return; // No function key on this keyboard + if (mLocale != null) { + // Create the graphic for spacebar + mF1Key.label = null; + mF1Key.icon = mRes.getDrawable(R.drawable.sym_keyboard_globe); + Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(buffer); + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextSize(22); + paint.setTextAlign(Align.CENTER); + // Draw a drop shadow for the text + paint.setShadowLayer(1f, 0, 0, 0xFF000000); + paint.setColor(0x80C0C0C0); + canvas.drawText(mLocale.getDisplayLanguage(mLocale), + buffer.getWidth() / 2, - paint.ascent() + 2, paint); + int x = (buffer.getWidth() - mSpaceIcon.getIntrinsicWidth()) / 2; + int y = buffer.getHeight() - mSpaceIcon.getIntrinsicHeight(); + mSpaceIcon.setBounds(x, y, + x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight()); + mSpaceIcon.draw(canvas); + mSpaceKey.icon = new BitmapDrawable(mRes, buffer); + } else { + mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space); + switch (mMode) { + case KeyboardSwitcher.KEYBOARDMODE_NORMAL: + case KeyboardSwitcher.KEYBOARDMODE_IM: + mF1Key.label = ","; + mF1Key.codes = new int[] { ',' }; + mF1Key.icon = null; + mF1Key.iconPreview = null; + break; + case KeyboardSwitcher.KEYBOARDMODE_EMAIL: + case KeyboardSwitcher.KEYBOARDMODE_URL: + mF1Key.label = mRes.getString(R.string.popular_domain_0); + mF1Key.codes = new int[] { '.' }; + mF1Key.text = mF1Key.label; + mF1Key.icon = null; + mF1Key.iconPreview = null; + mF1Key.popupResId = R.xml.popup_domains; + break; + } + } + } + + public void setLanguage(Locale locale) { + if (mLocale != null && mLocale.equals(locale)) return; + mLocale = locale; + setF1Key(); + if (mF1Key != null) { + } + } + static class LatinKey extends Keyboard.Key { private boolean mShiftLockEnabled; diff --git a/src/com/android/inputmethod/latin/LatinKeyboardView.java b/src/com/android/inputmethod/latin/LatinKeyboardView.java index ecbd1adfb..163d824e0 100644 --- a/src/com/android/inputmethod/latin/LatinKeyboardView.java +++ b/src/com/android/inputmethod/latin/LatinKeyboardView.java @@ -35,6 +35,7 @@ public class LatinKeyboardView extends KeyboardView { static final int KEYCODE_OPTIONS = -100; static final int KEYCODE_SHIFT_LONGPRESS = -101; + static final int KEYCODE_F1 = -102; private Keyboard mPhoneKeyboard; diff --git a/src/com/android/inputmethod/latin/Suggest.java b/src/com/android/inputmethod/latin/Suggest.java index c025566b7..cc9a39cb7 100755 --- a/src/com/android/inputmethod/latin/Suggest.java +++ b/src/com/android/inputmethod/latin/Suggest.java @@ -336,4 +336,10 @@ public class Suggest implements Dictionary.WordCallback { } mSuggestions.clear(); } + + public void close() { + if (mMainDict != null) { + mMainDict.close(); + } + } }