diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/KeyboardSwitcher.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/KeyboardSwitcher.java | 809 |
1 files changed, 518 insertions, 291 deletions
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java index a7b695eb3..00b6f0a43 100644 --- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java @@ -16,11 +16,13 @@ package com.android.inputmethod.latin; +import android.content.Context; import android.content.SharedPreferences; -import android.content.res.Configuration; import android.content.res.Resources; -import android.preference.PreferenceManager; +import android.util.Log; import android.view.InflateException; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; import java.lang.ref.SoftReference; import java.util.Arrays; @@ -28,187 +30,169 @@ import java.util.HashMap; import java.util.Locale; public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener { - - public static final int MODE_NONE = 0; - public static final int MODE_TEXT = 1; - public static final int MODE_SYMBOLS = 2; - public static final int MODE_PHONE = 3; - public static final int MODE_URL = 4; - public static final int MODE_EMAIL = 5; - public static final int MODE_IM = 6; - public static final int MODE_WEB = 7; - - // Main keyboard layouts without the settings key - public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal; - public static final int KEYBOARDMODE_URL = R.id.mode_url; - public static final int KEYBOARDMODE_EMAIL = R.id.mode_email; - public static final int KEYBOARDMODE_IM = R.id.mode_im; - public static final int KEYBOARDMODE_WEB = R.id.mode_webentry; - // Main keyboard layouts with the settings key - public static final int KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY = - R.id.mode_normal_with_settings_key; - public static final int KEYBOARDMODE_URL_WITH_SETTINGS_KEY = - R.id.mode_url_with_settings_key; - public static final int KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY = - R.id.mode_email_with_settings_key; - public static final int KEYBOARDMODE_IM_WITH_SETTINGS_KEY = - R.id.mode_im_with_settings_key; - public static final int KEYBOARDMODE_WEB_WITH_SETTINGS_KEY = - R.id.mode_webentry_with_settings_key; - - // Symbols keyboard layout without the settings key - public static final int KEYBOARDMODE_SYMBOLS = R.id.mode_symbols; - // Symbols keyboard layout with the settings key - public static final int KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY = - R.id.mode_symbols_with_settings_key; - - public static final String DEFAULT_LAYOUT_ID = "4"; + private static final String TAG = "KeyboardSwitcher"; + private static final boolean DEBUG = false; + public static final boolean DEBUG_STATE = false; + + public static final int MODE_TEXT = 0; + 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; + + // Changing DEFAULT_LAYOUT_ID also requires prefs_for_debug.xml to be matched with. + public static final String DEFAULT_LAYOUT_ID = "5"; public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902"; private static final int[] THEMES = new int [] { - R.layout.input_basic, R.layout.input_basic_highcontrast, R.layout.input_stone_normal, - R.layout.input_stone_bold, R.layout.input_gingerbread}; - - // Ids for each characters' color in the keyboard - private static final int CHAR_THEME_COLOR_WHITE = 0; - private static final int CHAR_THEME_COLOR_BLACK = 1; - - // Tables which contains resource ids for each character theme color - private static final int[] KBD_PHONE = new int[] {R.xml.kbd_phone, R.xml.kbd_phone_black}; - private static final int[] KBD_PHONE_SYMBOLS = new int[] { - R.xml.kbd_phone_symbols, R.xml.kbd_phone_symbols_black}; - private static final int[] KBD_SYMBOLS = new int[] { - R.xml.kbd_symbols, R.xml.kbd_symbols_black}; - private static final int[] KBD_SYMBOLS_SHIFT = new int[] { - R.xml.kbd_symbols_shift, R.xml.kbd_symbols_shift_black}; - private static final int[] KBD_QWERTY = new int[] {R.xml.kbd_qwerty, R.xml.kbd_qwerty_black}; + 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, // DEFAULT_LAYOUT_ID + }; private static final int SYMBOLS_MODE_STATE_NONE = 0; private static final int SYMBOLS_MODE_STATE_BEGIN = 1; private static final int SYMBOLS_MODE_STATE_SYMBOL = 2; + private SubtypeSwitcher mSubtypeSwitcher; + private SharedPreferences mPrefs; + private LatinKeyboardView mInputView; - private static final int[] ALPHABET_MODES = { - KEYBOARDMODE_NORMAL, - KEYBOARDMODE_URL, - KEYBOARDMODE_EMAIL, - KEYBOARDMODE_IM, - KEYBOARDMODE_WEB, - KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY, - KEYBOARDMODE_URL_WITH_SETTINGS_KEY, - KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY, - KEYBOARDMODE_IM_WITH_SETTINGS_KEY, - KEYBOARDMODE_WEB_WITH_SETTINGS_KEY }; - - private final LatinIME mInputMethodService; + private LatinIME mInputMethodService; + + private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); + private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); private KeyboardId mSymbolsId; private KeyboardId mSymbolsShiftedId; private KeyboardId mCurrentId; - private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards; + private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = + new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); - private int mMode = MODE_NONE; /** One of the MODE_XXX values */ + private int mMode = MODE_TEXT; /* default value */ private int mImeOptions; private boolean mIsSymbols; /** mIsAutoCompletionActive indicates that auto completed word will be input instead of * what user actually typed. */ private boolean mIsAutoCompletionActive; - private boolean mHasVoice; - private boolean mVoiceOnPrimary; - private boolean mPreferSymbols; + private boolean mVoiceKeyEnabled; + private boolean mVoiceButtonOnPrimary; private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; // Indicates whether or not we have the settings key private boolean mHasSettingsKey; private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto; - private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = R.string.settings_key_mode_always_show; + private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = + R.string.settings_key_mode_always_show; // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to // in the source code now. // Default is SETTINGS_KEY_MODE_AUTO. private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO; - private int mLastDisplayWidth; - private LanguageSwitcher mLanguageSwitcher; - private Locale mInputLocale; - private int mLayoutId; - public KeyboardSwitcher(LatinIME ims) { - mInputMethodService = ims; - - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims); - mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); - updateSettingsKeyState(prefs); - prefs.registerOnSharedPreferenceChangeListener(this); + private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); - mKeyboards = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); - mSymbolsId = makeSymbolsId(false); - mSymbolsShiftedId = makeSymbolsShiftedId(false); + public static KeyboardSwitcher getInstance() { + return sInstance; } - /** - * 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. - */ - public void setLanguageSwitcher(LanguageSwitcher languageSwitcher) { - mLanguageSwitcher = languageSwitcher; - mInputLocale = mLanguageSwitcher.getInputLocale(); + private KeyboardSwitcher() { } - private KeyboardId makeSymbolsId(boolean hasVoice) { - return new KeyboardId(KBD_SYMBOLS[getCharColorId()], mHasSettingsKey ? - KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS, - false, hasVoice); - } + public static void init(LatinIME ims, SharedPreferences prefs) { + sInstance.mInputMethodService = ims; + sInstance.mPrefs = prefs; + sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance(); - private KeyboardId makeSymbolsShiftedId(boolean hasVoice) { - return new KeyboardId(KBD_SYMBOLS_SHIFT[getCharColorId()], mHasSettingsKey ? - KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS, - false, hasVoice); + sInstance.mLayoutId = Integer.valueOf( + prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); + prefs.registerOnSharedPreferenceChangeListener(sInstance); } - public void makeKeyboards(boolean forceCreate) { - mSymbolsId = makeSymbolsId(mHasVoice && !mVoiceOnPrimary); - mSymbolsShiftedId = makeSymbolsShiftedId(mHasVoice && !mVoiceOnPrimary); - - if (forceCreate) mKeyboards.clear(); - // 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 = mInputMethodService.getMaxWidth(); - if (displayWidth == mLastDisplayWidth) return; - mLastDisplayWidth = displayWidth; - if (!forceCreate) mKeyboards.clear(); + private void makeSymbolsKeyboardIds() { + final Locale locale = mSubtypeSwitcher.getInputLocale(); + final int orientation = mInputMethodService.getResources().getConfiguration().orientation; + final int mode = mMode; + final int colorScheme = getColorScheme(); + final boolean hasSettingsKey = mHasSettingsKey; + final boolean voiceKeyEnabled = mVoiceKeyEnabled; + final boolean hasVoiceKey = voiceKeyEnabled && !mVoiceButtonOnPrimary; + final int imeOptions = mImeOptions; + // 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 = new KeyboardId(locale, orientation, mode, + mode == MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols, + colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); + mSymbolsShiftedId = new KeyboardId(locale, orientation, mode, + mode == MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift, + colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); } /** * Represents the parameters necessary to construct a new LatinKeyboard, * which also serve as a unique identifier for each keyboard type. */ - private static class KeyboardId { - // TODO: should have locale and portrait/landscape orientation? - public final int mXml; - public final int mKeyboardMode; /** A KEYBOARDMODE_XXX value */ + public static class KeyboardId { + public final Locale mLocale; + public final int mOrientation; + public final int mMode; + public final int mXmlId; + public final int mColorScheme; + public final boolean mHasSettingsKey; + public final boolean mVoiceKeyEnabled; + public final boolean mHasVoiceKey; + public final int mImeOptions; public final boolean mEnableShiftLock; - public final boolean mHasVoice; private final int mHashCode; - public KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice) { - this.mXml = xml; - this.mKeyboardMode = mode; + public KeyboardId(Locale locale, int orientation, int mode, + int xmlId, int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled, + boolean hasVoiceKey, int imeOptions, boolean enableShiftLock) { + this.mLocale = locale; + this.mOrientation = orientation; + this.mMode = mode; + this.mXmlId = xmlId; + this.mColorScheme = colorScheme; + this.mHasSettingsKey = hasSettingsKey; + this.mVoiceKeyEnabled = voiceKeyEnabled; + this.mHasVoiceKey = hasVoiceKey; + // We are interested only in IME_MASK_ACTION enum value and IME_FLAG_NO_ENTER_ACTION. + this.mImeOptions = imeOptions + & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); this.mEnableShiftLock = enableShiftLock; - this.mHasVoice = hasVoice; this.mHashCode = Arrays.hashCode(new Object[] { - xml, mode, enableShiftLock, hasVoice + locale, + orientation, + mode, + xmlId, + colorScheme, + hasSettingsKey, + voiceKeyEnabled, + hasVoiceKey, + imeOptions, + enableShiftLock, }); } - public KeyboardId(int xml, boolean hasVoice) { - this(xml, 0, false, hasVoice); + public int getXmlId() { + return mXmlId; + } + + public boolean isAlphabetMode() { + return mXmlId == R.xml.kbd_qwerty; } @Override @@ -217,202 +201,438 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } private boolean equals(KeyboardId other) { - return other.mXml == this.mXml - && other.mKeyboardMode == this.mKeyboardMode - && other.mEnableShiftLock == this.mEnableShiftLock - && other.mHasVoice == this.mHasVoice; + return other.mLocale.equals(this.mLocale) + && other.mOrientation == this.mOrientation + && other.mMode == this.mMode + && other.mXmlId == this.mXmlId + && other.mColorScheme == this.mColorScheme + && other.mHasSettingsKey == this.mHasSettingsKey + && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled + && other.mHasVoiceKey == this.mHasVoiceKey + && other.mImeOptions == this.mImeOptions + && other.mEnableShiftLock == this.mEnableShiftLock; } @Override public int hashCode() { return mHashCode; } - } - public void setVoiceMode(boolean enableVoice, boolean voiceOnPrimary) { - if (enableVoice != mHasVoice || voiceOnPrimary != mVoiceOnPrimary) { - mKeyboards.clear(); + @Override + public String toString() { + return String.format("[%s %s %5s imeOptions=0x%08x xml=0x%08x %s%s%s%s%s]", + mLocale, + (mOrientation == 1 ? "port" : "land"), + modeName(mMode), + mImeOptions, + mXmlId, + colorSchemeName(mColorScheme), + (mHasSettingsKey ? " hasSettingsKey" : ""), + (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""), + (mHasVoiceKey ? " hasVoiceKey" : ""), + (mEnableShiftLock ? " enableShiftLock" : "")); + } + + private static String modeName(int mode) { + switch (mode) { + case MODE_TEXT: return "text"; + 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"; + } + return null; + } + + private static String colorSchemeName(int colorScheme) { + switch (colorScheme) { + case BaseKeyboardView.COLOR_SCHEME_WHITE: return "white"; + case BaseKeyboardView.COLOR_SCHEME_BLACK: return "black"; + } + return null; } - mHasVoice = enableVoice; - mVoiceOnPrimary = voiceOnPrimary; - setKeyboardMode(mMode, mImeOptions, mHasVoice, mIsSymbols); } - private boolean hasVoiceButton(boolean isSymbols) { - return mHasVoice && (isSymbols != mVoiceOnPrimary); + private boolean hasVoiceKey(boolean isSymbols) { + return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary); } - public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) { + public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled, + boolean voiceButtonOnPrimary) { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; - mPreferSymbols = mode == MODE_SYMBOLS; - if (mode == MODE_SYMBOLS) { - mode = MODE_TEXT; - } try { - setKeyboardMode(mode, imeOptions, enableVoice, mPreferSymbols); + loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, + false); } catch (RuntimeException e) { - LatinImeLogger.logOnException(mode + "," + imeOptions + "," + mPreferSymbols, e); + Log.w(TAG, e); + LatinImeLogger.logOnException(mode + "," + imeOptions, e); } } - private void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) { + private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled, + boolean voiceButtonOnPrimary, boolean isSymbols) { if (mInputView == null) return; + mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); + mMode = mode; mImeOptions = imeOptions; - if (enableVoice != mHasVoice) { - // TODO clean up this unnecessary recursive call. - setVoiceMode(enableVoice, mVoiceOnPrimary); - } + mVoiceKeyEnabled = voiceButtonEnabled; + mVoiceButtonOnPrimary = voiceButtonOnPrimary; mIsSymbols = isSymbols; + // Update the settings key state because number of enabled IMEs could have been changed + mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService); + makeSymbolsKeyboardIds(); - mInputView.setPreviewEnabled(mInputMethodService.getPopupOn()); KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols); - LatinKeyboard keyboard = null; - keyboard = getKeyboard(id); - - if (mode == MODE_PHONE) { - mInputView.setPhoneKeyboard(keyboard); - } + LatinKeyboard keyboard = getKeyboard(id); mCurrentId = id; mInputView.setKeyboard(keyboard); - keyboard.setShifted(false); - keyboard.setShiftLocked(keyboard.isShiftLocked()); - keyboard.setImeOptions(mInputMethodService.getResources(), mMode, imeOptions); - keyboard.setColorOfSymbolIcons(mIsAutoCompletionActive, isBlackSym()); - // Update the settings key state because number of enabled IMEs could have been changed - updateSettingsKeyState(PreferenceManager.getDefaultSharedPreferences(mInputMethodService)); } private LatinKeyboard getKeyboard(KeyboardId id) { - SoftReference<LatinKeyboard> ref = mKeyboards.get(id); + final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id); LatinKeyboard keyboard = (ref == null) ? null : ref.get(); if (keyboard == null) { - Resources orig = mInputMethodService.getResources(); - Configuration conf = orig.getConfiguration(); - Locale saveLocale = conf.locale; - conf.locale = mInputLocale; - orig.updateConfiguration(conf, null); - keyboard = new LatinKeyboard(mInputMethodService, id.mXml, id.mKeyboardMode); - keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols - || id.mXml == R.xml.kbd_symbols_black), mHasVoice); - keyboard.setLanguageSwitcher(mLanguageSwitcher, mIsAutoCompletionActive, isBlackSym()); + final Locale savedLocale = mSubtypeSwitcher.changeSystemLocale( + mSubtypeSwitcher.getInputLocale()); + + keyboard = new LatinKeyboard(mInputMethodService, id); if (id.mEnableShiftLock) { keyboard.enableShiftLock(); } - mKeyboards.put(id, new SoftReference<LatinKeyboard>(keyboard)); - conf.locale = saveLocale; - orig.updateConfiguration(conf, null); + mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard)); + if (DEBUG) + Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " + + ((ref == null) ? "LOAD" : "GCed") + " id=" + id); + + mSubtypeSwitcher.changeSystemLocale(savedLocale); + } else if (DEBUG) { + Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id); } + + keyboard.onAutoCompletionStateChanged(mIsAutoCompletionActive); + keyboard.setShifted(false); return keyboard; } private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) { - boolean hasVoice = hasVoiceButton(isSymbols); - int charColorId = getCharColorId(); - // TODO: generalize for any KeyboardId - int keyboardRowsResId = KBD_QWERTY[charColorId]; + final boolean hasVoiceKey = hasVoiceKey(isSymbols); + final int charColorId = getColorScheme(); + final int xmlId; + final boolean enableShiftLock; + if (isSymbols) { if (mode == MODE_PHONE) { - return new KeyboardId(KBD_PHONE_SYMBOLS[charColorId], hasVoice); + xmlId = R.xml.kbd_phone_symbols; + } else if (mode == MODE_NUMBER) { + // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key. + xmlId = R.xml.kbd_number; } else { - return new KeyboardId(KBD_SYMBOLS[charColorId], mHasSettingsKey ? - KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS, - false, hasVoice); + xmlId = R.xml.kbd_symbols; + } + enableShiftLock = false; + } else { + if (mode == MODE_PHONE) { + xmlId = R.xml.kbd_phone; + enableShiftLock = false; + } else if (mode == MODE_NUMBER) { + xmlId = R.xml.kbd_number; + enableShiftLock = false; + } else { + xmlId = R.xml.kbd_qwerty; + enableShiftLock = true; } } - switch (mode) { - case MODE_NONE: - LatinImeLogger.logOnWarning( - "getKeyboardId:" + mode + "," + imeOptions + "," + isSymbols); - /* fall through */ - case MODE_TEXT: - return new KeyboardId(keyboardRowsResId, mHasSettingsKey ? - KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY : KEYBOARDMODE_NORMAL, - true, hasVoice); - case MODE_SYMBOLS: - return new KeyboardId(KBD_SYMBOLS[charColorId], mHasSettingsKey ? - KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS, - false, hasVoice); - case MODE_PHONE: - return new KeyboardId(KBD_PHONE[charColorId], hasVoice); - case MODE_URL: - return new KeyboardId(keyboardRowsResId, mHasSettingsKey ? - KEYBOARDMODE_URL_WITH_SETTINGS_KEY : KEYBOARDMODE_URL, true, hasVoice); - case MODE_EMAIL: - return new KeyboardId(keyboardRowsResId, mHasSettingsKey ? - KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY : KEYBOARDMODE_EMAIL, true, hasVoice); - case MODE_IM: - return new KeyboardId(keyboardRowsResId, mHasSettingsKey ? - KEYBOARDMODE_IM_WITH_SETTINGS_KEY : KEYBOARDMODE_IM, true, hasVoice); - case MODE_WEB: - return new KeyboardId(keyboardRowsResId, mHasSettingsKey ? - KEYBOARDMODE_WEB_WITH_SETTINGS_KEY : KEYBOARDMODE_WEB, true, hasVoice); - } - return null; + final int orientation = mInputMethodService.getResources().getConfiguration().orientation; + final Locale locale = mSubtypeSwitcher.getInputLocale(); + return new KeyboardId(locale, orientation, mode, xmlId, charColorId, + mHasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, imeOptions, enableShiftLock); } public int getKeyboardMode() { return mMode; } - + public boolean isAlphabetMode() { - if (mCurrentId == null) { - return false; + return mCurrentId != null && mCurrentId.isAlphabetMode(); + } + + public boolean isInputViewShown() { + return mInputView != null && mInputView.isShown(); + } + + public boolean isKeyboardAvailable() { + if (mInputView != null) + return mInputView.getLatinKeyboard() != null; + return false; + } + + private LatinKeyboard getLatinKeyboard() { + if (mInputView != null) + return mInputView.getLatinKeyboard(); + return null; + } + + public void setPreferredLetters(int[] frequencies) { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + latinKeyboard.setPreferredLetters(frequencies); + } + + public void keyReleased() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + latinKeyboard.keyReleased(); + } + + public boolean isShiftedOrShiftLocked() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isShiftedOrShiftLocked(); + return false; + } + + public boolean isShiftLocked() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isShiftLocked(); + return false; + } + + public boolean isAutomaticTemporaryUpperCase() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isAutomaticTemporaryUpperCase(); + return false; + } + + public boolean isManualTemporaryUpperCase() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) + return latinKeyboard.isManualTemporaryUpperCase(); + return false; + } + + private void setManualTemporaryUpperCase(boolean shifted) { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) { + // On non-distinct multi touch panel device, we should also turn off the shift locked + // state when shift key is pressed to go to normal mode. + // On the other hand, on distinct multi touch panel device, turning off the shift locked + // state with shift key pressing is handled by onReleaseShift(). + if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) { + latinKeyboard.setShiftLocked(false); + } + if (latinKeyboard.setShifted(shifted)) { + mInputView.invalidateAllKeys(); + } } - int currentMode = mCurrentId.mKeyboardMode; - for (Integer mode : ALPHABET_MODES) { - if (currentMode == mode) { - return true; + } + + private void setShiftLocked(boolean shiftLocked) { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) { + mInputView.invalidateAllKeys(); + } + } + + /** + * Toggle keyboard shift state triggered by user touch event. + */ + public void toggleShift() { + mInputMethodService.mHandler.cancelUpdateShiftState(); + if (DEBUG_STATE) + Log.d(TAG, "toggleShift:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + mShiftKeyState); + if (isAlphabetMode()) { + setManualTemporaryUpperCase(!isShiftedOrShiftLocked()); + } else { + toggleShiftInSymbol(); + } + } + + public void toggleCapsLock() { + mInputMethodService.mHandler.cancelUpdateShiftState(); + if (DEBUG_STATE) + Log.d(TAG, "toggleCapsLock:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + mShiftKeyState); + if (isAlphabetMode()) { + if (isShiftLocked()) { + // Shift key is long pressed while caps lock state, we will toggle back to normal + // state. And mark as if shift key is released. + setShiftLocked(false); + mShiftKeyState.onRelease(); + } else { + setShiftLocked(true); } } - return false; } - public void setShifted(boolean shifted) { - if (mInputView != null) { - mInputView.setShifted(shifted); + private void setAutomaticTemporaryUpperCase() { + LatinKeyboard latinKeyboard = getLatinKeyboard(); + if (latinKeyboard != null) { + latinKeyboard.setAutomaticTemporaryUpperCase(); + mInputView.invalidateAllKeys(); } } - public void setShiftLocked(boolean shiftLocked) { - if (mInputView != null) { - mInputView.setShiftLocked(shiftLocked); + /** + * Update keyboard shift state triggered by connected EditText status change. + */ + public void updateShiftState() { + final ShiftKeyState shiftKeyState = mShiftKeyState; + if (DEBUG_STATE) + Log.d(TAG, "updateShiftState:" + + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState() + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + shiftKeyState); + if (isAlphabetMode()) { + if (!isShiftLocked() && !shiftKeyState.isIgnoring()) { + if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) { + // Only when shift key is releasing, automatic temporary upper case will be set. + setAutomaticTemporaryUpperCase(); + } else { + setManualTemporaryUpperCase(shiftKeyState.isMomentary()); + } + } + } else { + // In symbol keyboard mode, we should clear shift key state because only alphabet + // keyboard has shift key. + shiftKeyState.onRelease(); } } - public void toggleShift() { + public void changeKeyboardMode() { + if (DEBUG_STATE) + Log.d(TAG, "changeKeyboardMode:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + mShiftKeyState); + toggleKeyboardMode(); + if (isShiftLocked() && isAlphabetMode()) + setShiftLocked(true); + updateShiftState(); + } + + public void onPressShift() { + if (!isKeyboardAvailable()) + return; + ShiftKeyState shiftKeyState = mShiftKeyState; + if (DEBUG_STATE) + Log.d(TAG, "onPressShift:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + shiftKeyState); + if (isAlphabetMode()) { + if (isShiftLocked()) { + // Shift key is pressed while caps lock state, we will treat this state as shifted + // caps lock state and mark as if shift key pressed while normal state. + shiftKeyState.onPress(); + setManualTemporaryUpperCase(true); + } else if (isAutomaticTemporaryUpperCase()) { + // Shift key is pressed while automatic temporary upper case, we have to move to + // manual temporary upper case. + shiftKeyState.onPress(); + setManualTemporaryUpperCase(true); + } else if (isShiftedOrShiftLocked()) { + // In manual upper case state, we just record shift key has been pressing while + // shifted state. + shiftKeyState.onPressOnShifted(); + } else { + // In base layout, chording or manual temporary upper case mode is started. + shiftKeyState.onPress(); + toggleShift(); + } + } else { + // In symbol mode, just toggle symbol and symbol more keyboard. + shiftKeyState.onPress(); + toggleShift(); + } + } + + public void onReleaseShift() { + if (!isKeyboardAvailable()) + return; + ShiftKeyState shiftKeyState = mShiftKeyState; + if (DEBUG_STATE) + Log.d(TAG, "onReleaseShift:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + shiftKeyState); + if (isAlphabetMode()) { + if (shiftKeyState.isMomentary()) { + // After chording input while normal state. + toggleShift(); + } else if (isShiftLocked() && !shiftKeyState.isIgnoring()) { + // Shift has been pressed without chording while caps lock state. + toggleCapsLock(); + } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) { + // Shift has been pressed without chording while shifted state. + toggleShift(); + } + } + shiftKeyState.onRelease(); + } + + public void onPressSymbol() { + if (DEBUG_STATE) + Log.d(TAG, "onReleaseShift:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " symbolKeyState=" + mSymbolKeyState); + changeKeyboardMode(); + mSymbolKeyState.onPress(); + } + + public void onReleaseSymbol() { + if (DEBUG_STATE) + Log.d(TAG, "onReleaseShift:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " symbolKeyState=" + mSymbolKeyState); + if (mSymbolKeyState.isMomentary()) + changeKeyboardMode(); + mSymbolKeyState.onRelease(); + } + + public void onOtherKeyPressed() { + if (DEBUG_STATE) + Log.d(TAG, "onOtherKeyPressed:" + + " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + + " shiftKeyState=" + mShiftKeyState + + " symbolKeyState=" + mSymbolKeyState); + mShiftKeyState.onOtherKeyPressed(); + mSymbolKeyState.onOtherKeyPressed(); + } + + private void toggleShiftInSymbol() { if (isAlphabetMode()) return; + final LatinKeyboard keyboard; if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) { - LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId); mCurrentId = mSymbolsShiftedId; - mInputView.setKeyboard(symbolsShiftedKeyboard); + keyboard = getKeyboard(mCurrentId); // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true). // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is // called. - symbolsShiftedKeyboard.enableShiftLock(); - symbolsShiftedKeyboard.setShiftLocked(true); - symbolsShiftedKeyboard.setImeOptions(mInputMethodService.getResources(), - mMode, mImeOptions); + keyboard.setShiftLocked(true); } else { - LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId); mCurrentId = mSymbolsId; - mInputView.setKeyboard(symbolsKeyboard); + keyboard = getKeyboard(mCurrentId); // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the // indicator, we need to call enableShiftLock() and setShiftLocked(false). - symbolsKeyboard.enableShiftLock(); - symbolsKeyboard.setShifted(false); - symbolsKeyboard.setImeOptions(mInputMethodService.getResources(), mMode, mImeOptions); + keyboard.setShifted(false); } + mInputView.setKeyboard(keyboard); } - public void toggleSymbols() { - setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols); - if (mIsSymbols && !mPreferSymbols) { + private void toggleKeyboardMode() { + loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary, + !mIsSymbols); + if (mIsSymbols) { mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN; } else { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; @@ -425,33 +645,34 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha /** * Updates state machine to figure out when to automatically switch back to alpha mode. - * Returns true if the keyboard needs to switch back */ - public boolean onKey(int key) { - // Switch back to alpha mode if user types one or more non-space/enter characters - // followed by a space/enter + public void onKey(int key) { + // Switch back to alpha mode if user types one or more non-space/enter + // characters followed by a space/enter switch (mSymbolsModeState) { - case SYMBOLS_MODE_STATE_BEGIN: - if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) { - mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; - } - break; - case SYMBOLS_MODE_STATE_SYMBOL: - if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true; - break; + case SYMBOLS_MODE_STATE_BEGIN: + if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) { + mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; + } + break; + case SYMBOLS_MODE_STATE_SYMBOL: + if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) { + changeKeyboardMode(); + } + break; } - return false; } public LatinKeyboardView getInputView() { return mInputView; } - public void recreateInputView() { - changeLatinKeyboardView(mLayoutId, true); + public LatinKeyboardView onCreateInputView() { + createInputViewInternal(mLayoutId, true); + return mInputView; } - private void changeLatinKeyboardView(int newLayout, boolean forceReset) { + private void createInputViewInternal(int newLayout, boolean forceReset) { if (mLayoutId != newLayout || mInputView == null || forceReset) { if (mInputView != null) { mInputView.closing(); @@ -468,9 +689,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha ).inflate(THEMES[newLayout], null); tryGC = false; } catch (OutOfMemoryError e) { + Log.w(TAG, "load keyboard failed: " + e); tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait( mLayoutId + "," + newLayout, e); } catch (InflateException e) { + Log.w(TAG, "load keyboard failed: " + e); tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait( mLayoutId + "," + newLayout, e); } @@ -478,38 +701,37 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mInputView.setOnKeyboardActionListener(mInputMethodService); mLayoutId = newLayout; } + } + + private void postSetInputView() { mInputMethodService.mHandler.post(new Runnable() { + @Override public void run() { if (mInputView != null) { mInputMethodService.setInputView(mInputView); } mInputMethodService.updateInputViewShown(); - }}); + } + }); } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (PREF_KEYBOARD_LAYOUT.equals(key)) { - changeLatinKeyboardView( - Integer.valueOf(sharedPreferences.getString(key, DEFAULT_LAYOUT_ID)), false); + final int layoutId = Integer.valueOf( + sharedPreferences.getString(key, DEFAULT_LAYOUT_ID)); + createInputViewInternal(layoutId, false); + postSetInputView(); } else if (LatinIMESettings.PREF_SETTINGS_KEY.equals(key)) { - updateSettingsKeyState(sharedPreferences); - recreateInputView(); - } - } - - public boolean isBlackSym () { - if (mInputView != null && mInputView.getSymbolColorScheme() == 1) { - return true; + mHasSettingsKey = getSettingsKeyMode(sharedPreferences, mInputMethodService); + createInputViewInternal(mLayoutId, true); + postSetInputView(); } - return false; } - private int getCharColorId () { - if (isBlackSym()) { - return CHAR_THEME_COLOR_BLACK; - } else { - return CHAR_THEME_COLOR_WHITE; - } + private int getColorScheme() { + return (mInputView != null) + ? mInputView.getColorScheme() : BaseKeyboardView.COLOR_SCHEME_WHITE; } public void onAutoCompletionStateChanged(boolean isAutoCompletion) { @@ -521,18 +743,23 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - private void updateSettingsKeyState(SharedPreferences prefs) { - Resources resources = mInputMethodService.getResources(); - final String settingsKeyMode = prefs.getString(LatinIMESettings.PREF_SETTINGS_KEY, - resources.getString(DEFAULT_SETTINGS_KEY_MODE)); - // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or - // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system - if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW)) - || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO)) - && LatinIMEUtil.hasMultipleEnabledIMEs(mInputMethodService))) { - mHasSettingsKey = true; - } else { - mHasSettingsKey = false; + private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) { + Resources resources = context.getResources(); + final boolean showSettingsKeyOption = resources.getBoolean( + R.bool.config_enable_show_settings_key_option); + if (showSettingsKeyOption) { + final String settingsKeyMode = prefs.getString(LatinIMESettings.PREF_SETTINGS_KEY, + resources.getString(DEFAULT_SETTINGS_KEY_MODE)); + // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or + // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system + if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW)) + || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO)) + && LatinIMEUtil.hasMultipleEnabledIMEsOrSubtypes( + ((InputMethodManager) context.getSystemService( + Context.INPUT_METHOD_SERVICE))))) { + return true; + } } + return false; } } |