Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import android.content.SharedPreferences;
     20 import android.content.res.Configuration;
     21 import android.content.res.Resources;
     22 import android.preference.PreferenceManager;
     23 import android.view.InflateException;
     24 
     25 import java.lang.ref.SoftReference;
     26 import java.util.Arrays;
     27 import java.util.HashMap;
     28 import java.util.Locale;
     29 
     30 public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
     31 
     32     public static final int MODE_NONE = 0;
     33     public static final int MODE_TEXT = 1;
     34     public static final int MODE_SYMBOLS = 2;
     35     public static final int MODE_PHONE = 3;
     36     public static final int MODE_URL = 4;
     37     public static final int MODE_EMAIL = 5;
     38     public static final int MODE_IM = 6;
     39     public static final int MODE_WEB = 7;
     40 
     41     // Main keyboard layouts without the settings key
     42     public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal;
     43     public static final int KEYBOARDMODE_URL = R.id.mode_url;
     44     public static final int KEYBOARDMODE_EMAIL = R.id.mode_email;
     45     public static final int KEYBOARDMODE_IM = R.id.mode_im;
     46     public static final int KEYBOARDMODE_WEB = R.id.mode_webentry;
     47     // Main keyboard layouts with the settings key
     48     public static final int KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY =
     49             R.id.mode_normal_with_settings_key;
     50     public static final int KEYBOARDMODE_URL_WITH_SETTINGS_KEY =
     51             R.id.mode_url_with_settings_key;
     52     public static final int KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY =
     53             R.id.mode_email_with_settings_key;
     54     public static final int KEYBOARDMODE_IM_WITH_SETTINGS_KEY =
     55             R.id.mode_im_with_settings_key;
     56     public static final int KEYBOARDMODE_WEB_WITH_SETTINGS_KEY =
     57             R.id.mode_webentry_with_settings_key;
     58 
     59     // Symbols keyboard layout without the settings key
     60     public static final int KEYBOARDMODE_SYMBOLS = R.id.mode_symbols;
     61     // Symbols keyboard layout with the settings key
     62     public static final int KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY =
     63             R.id.mode_symbols_with_settings_key;
     64 
     65     public static final String DEFAULT_LAYOUT_ID = "4";
     66     public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
     67     private static final int[] THEMES = new int [] {
     68         R.layout.input_basic, R.layout.input_basic_highcontrast, R.layout.input_stone_normal,
     69         R.layout.input_stone_bold, R.layout.input_gingerbread};
     70 
     71     // Ids for each characters' color in the keyboard
     72     private static final int CHAR_THEME_COLOR_WHITE = 0;
     73     private static final int CHAR_THEME_COLOR_BLACK = 1;
     74 
     75     // Tables which contains resource ids for each character theme color
     76     private static final int[] KBD_PHONE = new int[] {R.xml.kbd_phone, R.xml.kbd_phone_black};
     77     private static final int[] KBD_PHONE_SYMBOLS = new int[] {
     78         R.xml.kbd_phone_symbols, R.xml.kbd_phone_symbols_black};
     79     private static final int[] KBD_SYMBOLS = new int[] {
     80         R.xml.kbd_symbols, R.xml.kbd_symbols_black};
     81     private static final int[] KBD_SYMBOLS_SHIFT = new int[] {
     82         R.xml.kbd_symbols_shift, R.xml.kbd_symbols_shift_black};
     83     private static final int[] KBD_QWERTY = new int[] {R.xml.kbd_qwerty, R.xml.kbd_qwerty_black};
     84 
     85     private LatinKeyboardView mInputView;
     86     private static final int[] ALPHABET_MODES = {
     87         KEYBOARDMODE_NORMAL,
     88         KEYBOARDMODE_URL,
     89         KEYBOARDMODE_EMAIL,
     90         KEYBOARDMODE_IM,
     91         KEYBOARDMODE_WEB,
     92         KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY,
     93         KEYBOARDMODE_URL_WITH_SETTINGS_KEY,
     94         KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY,
     95         KEYBOARDMODE_IM_WITH_SETTINGS_KEY,
     96         KEYBOARDMODE_WEB_WITH_SETTINGS_KEY };
     97 
     98     private LatinIME mInputMethodService;
     99 
    100     private KeyboardId mSymbolsId;
    101     private KeyboardId mSymbolsShiftedId;
    102 
    103     private KeyboardId mCurrentId;
    104     private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards =
    105             new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
    106 
    107     private int mMode = MODE_NONE; /** One of the MODE_XXX values */
    108     private int mImeOptions;
    109     private boolean mIsSymbols;
    110     /** mIsAutoCompletionActive indicates that auto completed word will be input instead of
    111      * what user actually typed. */
    112     private boolean mIsAutoCompletionActive;
    113     private boolean mHasVoice;
    114     private boolean mVoiceOnPrimary;
    115     private boolean mPreferSymbols;
    116 
    117     private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0;
    118     private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1;
    119     private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2;
    120     // The following states are used only on the distinct multi-touch panel devices.
    121     private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3;
    122     private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4;
    123     private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
    124 
    125     // Indicates whether or not we have the settings key
    126     private boolean mHasSettingsKey;
    127     private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
    128     private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW = R.string.settings_key_mode_always_show;
    129     // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to
    130     // in the source code now.
    131     // Default is SETTINGS_KEY_MODE_AUTO.
    132     private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
    133 
    134     private int mLastDisplayWidth;
    135     private LanguageSwitcher mLanguageSwitcher;
    136     private Locale mInputLocale;
    137 
    138     private int mLayoutId;
    139 
    140     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
    141 
    142     public static KeyboardSwitcher getInstance() {
    143         return sInstance;
    144     }
    145 
    146     private KeyboardSwitcher() {
    147         // Intentional empty constructor for singleton.
    148     }
    149 
    150     public static void init(LatinIME ims) {
    151         sInstance.mInputMethodService = ims;
    152 
    153         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims);
    154         sInstance.mLayoutId = Integer.valueOf(
    155                 prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID));
    156         sInstance.updateSettingsKeyState(prefs);
    157         prefs.registerOnSharedPreferenceChangeListener(sInstance);
    158 
    159         sInstance.mSymbolsId = sInstance.makeSymbolsId(false);
    160         sInstance.mSymbolsShiftedId = sInstance.makeSymbolsShiftedId(false);
    161     }
    162 
    163     /**
    164      * Sets the input locale, when there are multiple locales for input.
    165      * If no locale switching is required, then the locale should be set to null.
    166      * @param locale the current input locale, or null for default locale with no locale
    167      * button.
    168      */
    169     public void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
    170         mLanguageSwitcher = languageSwitcher;
    171         mInputLocale = mLanguageSwitcher.getInputLocale();
    172     }
    173 
    174     private KeyboardId makeSymbolsId(boolean hasVoice) {
    175         return new KeyboardId(KBD_SYMBOLS[getCharColorId()], mHasSettingsKey ?
    176                 KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS,
    177                 false, hasVoice);
    178     }
    179 
    180     private KeyboardId makeSymbolsShiftedId(boolean hasVoice) {
    181         return new KeyboardId(KBD_SYMBOLS_SHIFT[getCharColorId()], mHasSettingsKey ?
    182                 KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS,
    183                 false, hasVoice);
    184     }
    185 
    186     public void makeKeyboards(boolean forceCreate) {
    187         mSymbolsId = makeSymbolsId(mHasVoice && !mVoiceOnPrimary);
    188         mSymbolsShiftedId = makeSymbolsShiftedId(mHasVoice && !mVoiceOnPrimary);
    189 
    190         if (forceCreate) mKeyboards.clear();
    191         // Configuration change is coming after the keyboard gets recreated. So don't rely on that.
    192         // If keyboards have already been made, check if we have a screen width change and
    193         // create the keyboard layouts again at the correct orientation
    194         int displayWidth = mInputMethodService.getMaxWidth();
    195         if (displayWidth == mLastDisplayWidth) return;
    196         mLastDisplayWidth = displayWidth;
    197         if (!forceCreate) mKeyboards.clear();
    198     }
    199 
    200     /**
    201      * Represents the parameters necessary to construct a new LatinKeyboard,
    202      * which also serve as a unique identifier for each keyboard type.
    203      */
    204     private static class KeyboardId {
    205         // TODO: should have locale and portrait/landscape orientation?
    206         public final int mXml;
    207         public final int mKeyboardMode; /** A KEYBOARDMODE_XXX value */
    208         public final boolean mEnableShiftLock;
    209         public final boolean mHasVoice;
    210 
    211         private final int mHashCode;
    212 
    213         public KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice) {
    214             this.mXml = xml;
    215             this.mKeyboardMode = mode;
    216             this.mEnableShiftLock = enableShiftLock;
    217             this.mHasVoice = hasVoice;
    218 
    219             this.mHashCode = Arrays.hashCode(new Object[] {
    220                xml, mode, enableShiftLock, hasVoice
    221             });
    222         }
    223 
    224         public KeyboardId(int xml, boolean hasVoice) {
    225             this(xml, 0, false, hasVoice);
    226         }
    227 
    228         @Override
    229         public boolean equals(Object other) {
    230             return other instanceof KeyboardId && equals((KeyboardId) other);
    231         }
    232 
    233         private boolean equals(KeyboardId other) {
    234             return other.mXml == this.mXml
    235                 && other.mKeyboardMode == this.mKeyboardMode
    236                 && other.mEnableShiftLock == this.mEnableShiftLock
    237                 && other.mHasVoice == this.mHasVoice;
    238         }
    239 
    240         @Override
    241         public int hashCode() {
    242             return mHashCode;
    243         }
    244     }
    245 
    246     public void setVoiceMode(boolean enableVoice, boolean voiceOnPrimary) {
    247         if (enableVoice != mHasVoice || voiceOnPrimary != mVoiceOnPrimary) {
    248             mKeyboards.clear();
    249         }
    250         mHasVoice = enableVoice;
    251         mVoiceOnPrimary = voiceOnPrimary;
    252         setKeyboardMode(mMode, mImeOptions, mHasVoice, mIsSymbols);
    253     }
    254 
    255     private boolean hasVoiceButton(boolean isSymbols) {
    256         return mHasVoice && (isSymbols != mVoiceOnPrimary);
    257     }
    258 
    259     public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) {
    260         mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
    261         mPreferSymbols = mode == MODE_SYMBOLS;
    262         if (mode == MODE_SYMBOLS) {
    263             mode = MODE_TEXT;
    264         }
    265         try {
    266             setKeyboardMode(mode, imeOptions, enableVoice, mPreferSymbols);
    267         } catch (RuntimeException e) {
    268             LatinImeLogger.logOnException(mode + "," + imeOptions + "," + mPreferSymbols, e);
    269         }
    270     }
    271 
    272     private void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) {
    273         if (mInputView == null) return;
    274         mMode = mode;
    275         mImeOptions = imeOptions;
    276         if (enableVoice != mHasVoice) {
    277             // TODO clean up this unnecessary recursive call.
    278             setVoiceMode(enableVoice, mVoiceOnPrimary);
    279         }
    280         mIsSymbols = isSymbols;
    281 
    282         mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
    283         KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
    284         LatinKeyboard keyboard = null;
    285         keyboard = getKeyboard(id);
    286 
    287         if (mode == MODE_PHONE) {
    288             mInputView.setPhoneKeyboard(keyboard);
    289         }
    290 
    291         mCurrentId = id;
    292         mInputView.setKeyboard(keyboard);
    293         keyboard.setShifted(false);
    294         keyboard.setShiftLocked(keyboard.isShiftLocked());
    295         keyboard.setImeOptions(mInputMethodService.getResources(), mMode, imeOptions);
    296         keyboard.setColorOfSymbolIcons(mIsAutoCompletionActive, isBlackSym());
    297         // Update the settings key state because number of enabled IMEs could have been changed
    298         updateSettingsKeyState(PreferenceManager.getDefaultSharedPreferences(mInputMethodService));
    299     }
    300 
    301     private LatinKeyboard getKeyboard(KeyboardId id) {
    302         SoftReference<LatinKeyboard> ref = mKeyboards.get(id);
    303         LatinKeyboard keyboard = (ref == null) ? null : ref.get();
    304         if (keyboard == null) {
    305             Resources orig = mInputMethodService.getResources();
    306             Configuration conf = orig.getConfiguration();
    307             Locale saveLocale = conf.locale;
    308             conf.locale = mInputLocale;
    309             orig.updateConfiguration(conf, null);
    310             keyboard = new LatinKeyboard(mInputMethodService, id.mXml, id.mKeyboardMode);
    311             keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols
    312                     || id.mXml == R.xml.kbd_symbols_black), mHasVoice);
    313             keyboard.setLanguageSwitcher(mLanguageSwitcher, mIsAutoCompletionActive, isBlackSym());
    314 
    315             if (id.mEnableShiftLock) {
    316                 keyboard.enableShiftLock();
    317             }
    318             mKeyboards.put(id, new SoftReference<LatinKeyboard>(keyboard));
    319 
    320             conf.locale = saveLocale;
    321             orig.updateConfiguration(conf, null);
    322         }
    323         return keyboard;
    324     }
    325 
    326     private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
    327         boolean hasVoice = hasVoiceButton(isSymbols);
    328         int charColorId = getCharColorId();
    329         // TODO: generalize for any KeyboardId
    330         int keyboardRowsResId = KBD_QWERTY[charColorId];
    331         if (isSymbols) {
    332             if (mode == MODE_PHONE) {
    333                 return new KeyboardId(KBD_PHONE_SYMBOLS[charColorId], hasVoice);
    334             } else {
    335                 return new KeyboardId(KBD_SYMBOLS[charColorId], mHasSettingsKey ?
    336                         KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS,
    337                         false, hasVoice);
    338             }
    339         }
    340         switch (mode) {
    341             case MODE_NONE:
    342                 LatinImeLogger.logOnWarning(
    343                         "getKeyboardId:" + mode + "," + imeOptions + "," + isSymbols);
    344                 /* fall through */
    345             case MODE_TEXT:
    346                 return new KeyboardId(keyboardRowsResId, mHasSettingsKey ?
    347                         KEYBOARDMODE_NORMAL_WITH_SETTINGS_KEY : KEYBOARDMODE_NORMAL,
    348                         true, hasVoice);
    349             case MODE_SYMBOLS:
    350                 return new KeyboardId(KBD_SYMBOLS[charColorId], mHasSettingsKey ?
    351                         KEYBOARDMODE_SYMBOLS_WITH_SETTINGS_KEY : KEYBOARDMODE_SYMBOLS,
    352                         false, hasVoice);
    353             case MODE_PHONE:
    354                 return new KeyboardId(KBD_PHONE[charColorId], hasVoice);
    355             case MODE_URL:
    356                 return new KeyboardId(keyboardRowsResId, mHasSettingsKey ?
    357                         KEYBOARDMODE_URL_WITH_SETTINGS_KEY : KEYBOARDMODE_URL, true, hasVoice);
    358             case MODE_EMAIL:
    359                 return new KeyboardId(keyboardRowsResId, mHasSettingsKey ?
    360                         KEYBOARDMODE_EMAIL_WITH_SETTINGS_KEY : KEYBOARDMODE_EMAIL, true, hasVoice);
    361             case MODE_IM:
    362                 return new KeyboardId(keyboardRowsResId, mHasSettingsKey ?
    363                         KEYBOARDMODE_IM_WITH_SETTINGS_KEY : KEYBOARDMODE_IM, true, hasVoice);
    364             case MODE_WEB:
    365                 return new KeyboardId(keyboardRowsResId, mHasSettingsKey ?
    366                         KEYBOARDMODE_WEB_WITH_SETTINGS_KEY : KEYBOARDMODE_WEB, true, hasVoice);
    367         }
    368         return null;
    369     }
    370 
    371     public int getKeyboardMode() {
    372         return mMode;
    373     }
    374 
    375     public boolean isAlphabetMode() {
    376         if (mCurrentId == null) {
    377             return false;
    378         }
    379         int currentMode = mCurrentId.mKeyboardMode;
    380         for (Integer mode : ALPHABET_MODES) {
    381             if (currentMode == mode) {
    382                 return true;
    383             }
    384         }
    385         return false;
    386     }
    387 
    388     public void setShifted(boolean shifted) {
    389         if (mInputView != null) {
    390             mInputView.setShifted(shifted);
    391         }
    392     }
    393 
    394     public void setShiftLocked(boolean shiftLocked) {
    395         if (mInputView != null) {
    396             mInputView.setShiftLocked(shiftLocked);
    397         }
    398     }
    399 
    400     public void toggleShift() {
    401         if (isAlphabetMode())
    402             return;
    403         if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
    404             LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
    405             mCurrentId = mSymbolsShiftedId;
    406             mInputView.setKeyboard(symbolsShiftedKeyboard);
    407             // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
    408             // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true).
    409             // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is
    410             // called.
    411             symbolsShiftedKeyboard.enableShiftLock();
    412             symbolsShiftedKeyboard.setShiftLocked(true);
    413             symbolsShiftedKeyboard.setImeOptions(mInputMethodService.getResources(),
    414                     mMode, mImeOptions);
    415         } else {
    416             LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
    417             mCurrentId = mSymbolsId;
    418             mInputView.setKeyboard(symbolsKeyboard);
    419             // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
    420             // indicator, we need to call enableShiftLock() and setShiftLocked(false).
    421             symbolsKeyboard.enableShiftLock();
    422             symbolsKeyboard.setShifted(false);
    423             symbolsKeyboard.setImeOptions(mInputMethodService.getResources(), mMode, mImeOptions);
    424         }
    425     }
    426 
    427     public void onCancelInput() {
    428         // Snap back to the previous keyboard mode if the user cancels sliding input.
    429         if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1)
    430             mInputMethodService.changeKeyboardMode();
    431     }
    432 
    433     public void toggleSymbols() {
    434         setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols);
    435         if (mIsSymbols && !mPreferSymbols) {
    436             mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
    437         } else {
    438             mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
    439         }
    440     }
    441 
    442     public boolean hasDistinctMultitouch() {
    443         return mInputView != null && mInputView.hasDistinctMultitouch();
    444     }
    445 
    446     public void setAutoModeSwitchStateMomentary() {
    447         mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
    448     }
    449 
    450     public boolean isInMomentaryAutoModeSwitchState() {
    451         return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
    452     }
    453 
    454     public boolean isInChordingAutoModeSwitchState() {
    455         return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING;
    456     }
    457 
    458     public boolean isVibrateAndSoundFeedbackRequired() {
    459         return mInputView != null && !mInputView.isInSlidingKeyInput();
    460     }
    461 
    462     private int getPointerCount() {
    463         return mInputView == null ? 0 : mInputView.getPointerCount();
    464     }
    465 
    466     /**
    467      * Updates state machine to figure out when to automatically snap back to the previous mode.
    468      */
    469     public void onKey(int key) {
    470         // Switch back to alpha mode if user types one or more non-space/enter characters
    471         // followed by a space/enter
    472         switch (mAutoModeSwitchState) {
    473         case AUTO_MODE_SWITCH_STATE_MOMENTARY:
    474             // Only distinct multi touch devices can be in this state.
    475             // On non-distinct multi touch devices, mode change key is handled by {@link onKey},
    476             // not by {@link onPress} and {@link onRelease}. So, on such devices,
    477             // {@link mAutoModeSwitchState} starts from {@link AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN},
    478             // or {@link AUTO_MODE_SWITCH_STATE_ALPHA}, not from
    479             // {@link AUTO_MODE_SWITCH_STATE_MOMENTARY}.
    480             if (key == LatinKeyboard.KEYCODE_MODE_CHANGE) {
    481                 // Detected only the mode change key has been pressed, and then released.
    482                 if (mIsSymbols) {
    483                     mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
    484                 } else {
    485                     mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
    486                 }
    487             } else if (getPointerCount() == 1) {
    488                 // Snap back to the previous keyboard mode if the user pressed the mode change key
    489                 // and slid to other key, then released the finger.
    490                 // If the user cancels the sliding input, snapping back to the previous keyboard
    491                 // mode is handled by {@link #onCancelInput}.
    492                 mInputMethodService.changeKeyboardMode();
    493             } else {
    494                 // Chording input is being started. The keyboard mode will be snapped back to the
    495                 // previous mode in {@link onReleaseSymbol} when the mode change key is released.
    496                 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING;
    497             }
    498             break;
    499         case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
    500             if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key >= 0) {
    501                 mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL;
    502             }
    503             break;
    504         case AUTO_MODE_SWITCH_STATE_SYMBOL:
    505             // Snap back to alpha keyboard mode if user types one or more non-space/enter
    506             // characters followed by a space/enter.
    507             if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) {
    508                 mInputMethodService.changeKeyboardMode();
    509             }
    510             break;
    511         }
    512     }
    513 
    514     public LatinKeyboardView getInputView() {
    515         return mInputView;
    516     }
    517 
    518     public void recreateInputView() {
    519         changeLatinKeyboardView(mLayoutId, true);
    520     }
    521 
    522     private void changeLatinKeyboardView(int newLayout, boolean forceReset) {
    523         if (mLayoutId != newLayout || mInputView == null || forceReset) {
    524             if (mInputView != null) {
    525                 mInputView.closing();
    526             }
    527             if (THEMES.length <= newLayout) {
    528                 newLayout = Integer.valueOf(DEFAULT_LAYOUT_ID);
    529             }
    530 
    531             LatinIMEUtil.GCUtils.getInstance().reset();
    532             boolean tryGC = true;
    533             for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
    534                 try {
    535                     mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
    536                             ).inflate(THEMES[newLayout], null);
    537                     tryGC = false;
    538                 } catch (OutOfMemoryError e) {
    539                     tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
    540                             mLayoutId + "," + newLayout, e);
    541                 } catch (InflateException e) {
    542                     tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
    543                             mLayoutId + "," + newLayout, e);
    544                 }
    545             }
    546             mInputView.setOnKeyboardActionListener(mInputMethodService);
    547             mLayoutId = newLayout;
    548         }
    549         mInputMethodService.mHandler.post(new Runnable() {
    550             public void run() {
    551                 if (mInputView != null) {
    552                     mInputMethodService.setInputView(mInputView);
    553                 }
    554                 mInputMethodService.updateInputViewShown();
    555             }});
    556     }
    557 
    558     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    559         if (PREF_KEYBOARD_LAYOUT.equals(key)) {
    560             changeLatinKeyboardView(
    561                     Integer.valueOf(sharedPreferences.getString(key, DEFAULT_LAYOUT_ID)), false);
    562         } else if (LatinIMESettings.PREF_SETTINGS_KEY.equals(key)) {
    563             updateSettingsKeyState(sharedPreferences);
    564             recreateInputView();
    565         }
    566     }
    567 
    568     public boolean isBlackSym () {
    569         if (mInputView != null && mInputView.getSymbolColorScheme() == 1) {
    570             return true;
    571         }
    572         return false;
    573     }
    574 
    575     private int getCharColorId () {
    576         if (isBlackSym()) {
    577             return CHAR_THEME_COLOR_BLACK;
    578         } else {
    579             return CHAR_THEME_COLOR_WHITE;
    580         }
    581     }
    582 
    583     public void onAutoCompletionStateChanged(boolean isAutoCompletion) {
    584         if (isAutoCompletion != mIsAutoCompletionActive) {
    585             LatinKeyboardView keyboardView = getInputView();
    586             mIsAutoCompletionActive = isAutoCompletion;
    587             keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard())
    588                     .onAutoCompletionStateChanged(isAutoCompletion));
    589         }
    590     }
    591 
    592     private void updateSettingsKeyState(SharedPreferences prefs) {
    593         Resources resources = mInputMethodService.getResources();
    594         final String settingsKeyMode = prefs.getString(LatinIMESettings.PREF_SETTINGS_KEY,
    595                 resources.getString(DEFAULT_SETTINGS_KEY_MODE));
    596         // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or
    597         // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system
    598         if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
    599                 || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO))
    600                         && LatinIMEUtil.hasMultipleEnabledIMEs(mInputMethodService))) {
    601             mHasSettingsKey = true;
    602         } else {
    603             mHasSettingsKey = false;
    604         }
    605     }
    606 }
    607