KeyboardSwitcher.java revision 9502cc177cc53678c9ddcc01d4d046f69220e13b
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
17package com.android.inputmethod.keyboard;
18
19import com.android.inputmethod.latin.LatinIME;
20import com.android.inputmethod.latin.Settings;
21import com.android.inputmethod.latin.Utils;
22import com.android.inputmethod.latin.LatinImeLogger;
23import com.android.inputmethod.latin.R;
24import com.android.inputmethod.latin.SubtypeSwitcher;
25
26import android.content.Context;
27import android.content.SharedPreferences;
28import android.content.res.Resources;
29import android.util.Log;
30import android.view.InflateException;
31import android.view.inputmethod.InputMethodManager;
32
33import java.lang.ref.SoftReference;
34import java.util.HashMap;
35import java.util.Locale;
36
37public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
38    private static final String TAG = "KeyboardSwitcher";
39    private static final boolean DEBUG = false;
40    public static final boolean DEBUG_STATE = false;
41
42    // Changing DEFAULT_LAYOUT_ID also requires prefs_for_debug.xml to be matched with.
43    public static final String DEFAULT_LAYOUT_ID = "5";
44    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
45    private static final int[] THEMES = new int [] {
46        R.layout.input_basic,
47        R.layout.input_basic_highcontrast,
48        R.layout.input_stone_normal,
49        R.layout.input_stone_bold,
50        R.layout.input_gingerbread,
51        R.layout.input_honeycomb, // DEFAULT_LAYOUT_ID
52    };
53
54    private static final int SYMBOLS_MODE_STATE_NONE = 0;
55    private static final int SYMBOLS_MODE_STATE_BEGIN = 1;
56    private static final int SYMBOLS_MODE_STATE_SYMBOL = 2;
57
58    private SubtypeSwitcher mSubtypeSwitcher;
59    private SharedPreferences mPrefs;
60
61    private LatinKeyboardView mInputView;
62    private LatinIME mInputMethodService;
63
64    private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
65    private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
66
67    private KeyboardId mSymbolsId;
68    private KeyboardId mSymbolsShiftedId;
69
70    private KeyboardId mCurrentId;
71    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
72            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
73
74    private int mMode = KeyboardId.MODE_TEXT; /* default value */
75    private int mImeOptions;
76    private boolean mIsSymbols;
77    /** mIsAutoCompletionActive indicates that auto completed word will be input instead of
78     * what user actually typed. */
79    private boolean mIsAutoCompletionActive;
80    private boolean mVoiceKeyEnabled;
81    private boolean mVoiceButtonOnPrimary;
82    private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
83
84    // Indicates whether or not we have the settings key
85    private boolean mHasSettingsKey;
86    private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
87    private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW =
88            R.string.settings_key_mode_always_show;
89    // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to
90    // in the source code now.
91    // Default is SETTINGS_KEY_MODE_AUTO.
92    private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
93
94    private int mLayoutId;
95
96    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
97
98    public static KeyboardSwitcher getInstance() {
99        return sInstance;
100    }
101
102    private KeyboardSwitcher() {
103    }
104
105    public static void init(LatinIME ims, SharedPreferences prefs) {
106        sInstance.mInputMethodService = ims;
107        sInstance.mPrefs = prefs;
108        sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance();
109
110        sInstance.mLayoutId = Integer.valueOf(
111                prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID));
112        prefs.registerOnSharedPreferenceChangeListener(sInstance);
113    }
114
115    private void makeSymbolsKeyboardIds() {
116        final Locale locale = mSubtypeSwitcher.getInputLocale();
117        final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
118        final int mode = mMode;
119        final int colorScheme = getColorScheme();
120        final boolean hasSettingsKey = mHasSettingsKey;
121        final boolean voiceKeyEnabled = mVoiceKeyEnabled;
122        final boolean hasVoiceKey = voiceKeyEnabled && !mVoiceButtonOnPrimary;
123        final int imeOptions = mImeOptions;
124        // Note: This comment is only applied for phone number keyboard layout.
125        // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
126        // between "phone keyboard" and "phone symbols keyboard".  But on xlarge device,
127        // "@integer/key_shift" key code is used for that purpose in order to properly display
128        // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
129        // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
130        // respectively here for xlarge device's layout switching.
131        mSymbolsId = new KeyboardId(locale, orientation, mode,
132                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols,
133                colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
134        mSymbolsShiftedId = new KeyboardId(locale, orientation, mode,
135                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift,
136                colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
137    }
138
139    private boolean hasVoiceKey(boolean isSymbols) {
140        return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
141    }
142
143    public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled,
144            boolean voiceButtonOnPrimary) {
145        mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
146        try {
147            loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary,
148                    false);
149        } catch (RuntimeException e) {
150            Log.w(TAG, e);
151            LatinImeLogger.logOnException(mode + "," + imeOptions, e);
152        }
153    }
154
155    private void loadKeyboardInternal(int mode, int imeOptions, boolean voiceButtonEnabled,
156            boolean voiceButtonOnPrimary, boolean isSymbols) {
157        if (mInputView == null) return;
158        mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
159
160        mMode = mode;
161        mImeOptions = imeOptions;
162        mVoiceKeyEnabled = voiceButtonEnabled;
163        mVoiceButtonOnPrimary = voiceButtonOnPrimary;
164        mIsSymbols = isSymbols;
165        // Update the settings key state because number of enabled IMEs could have been changed
166        mHasSettingsKey = getSettingsKeyMode(mPrefs, mInputMethodService);
167        makeSymbolsKeyboardIds();
168
169        KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
170        LatinKeyboard keyboard = getKeyboard(id);
171
172        mCurrentId = id;
173        mInputView.setKeyboard(keyboard);
174    }
175
176    private LatinKeyboard getKeyboard(KeyboardId id) {
177        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
178        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
179        if (keyboard == null) {
180            final Locale savedLocale =  mSubtypeSwitcher.changeSystemLocale(
181                    mSubtypeSwitcher.getInputLocale());
182
183            keyboard = new LatinKeyboard(mInputMethodService, id);
184
185            if (id.mEnableShiftLock) {
186                keyboard.enableShiftLock();
187            }
188
189            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
190            if (DEBUG)
191                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
192                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
193
194            mSubtypeSwitcher.changeSystemLocale(savedLocale);
195        } else if (DEBUG) {
196            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
197        }
198
199        keyboard.onAutoCompletionStateChanged(mIsAutoCompletionActive);
200        keyboard.setShifted(false);
201        return keyboard;
202    }
203
204    private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
205        final boolean hasVoiceKey = hasVoiceKey(isSymbols);
206        final int charColorId = getColorScheme();
207        final int xmlId;
208        final boolean enableShiftLock;
209
210        if (isSymbols) {
211            if (mode == KeyboardId.MODE_PHONE) {
212                xmlId = R.xml.kbd_phone_symbols;
213            } else if (mode == KeyboardId.MODE_NUMBER) {
214                // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
215                xmlId = R.xml.kbd_number;
216            } else {
217                xmlId = R.xml.kbd_symbols;
218            }
219            enableShiftLock = false;
220        } else {
221            if (mode == KeyboardId.MODE_PHONE) {
222                xmlId = R.xml.kbd_phone;
223                enableShiftLock = false;
224            } else if (mode == KeyboardId.MODE_NUMBER) {
225                xmlId = R.xml.kbd_number;
226                enableShiftLock = false;
227            } else {
228                xmlId = R.xml.kbd_qwerty;
229                enableShiftLock = true;
230            }
231        }
232        final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
233        final Locale locale = mSubtypeSwitcher.getInputLocale();
234        return new KeyboardId(locale, orientation, mode, xmlId, charColorId,
235                mHasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, imeOptions, enableShiftLock);
236    }
237
238    public int getKeyboardMode() {
239        return mMode;
240    }
241
242    public boolean isAlphabetMode() {
243        return mCurrentId != null && mCurrentId.isAlphabetKeyboard();
244    }
245
246    public boolean isInputViewShown() {
247        return mInputView != null && mInputView.isShown();
248    }
249
250    public boolean isKeyboardAvailable() {
251        if (mInputView != null)
252            return mInputView.getLatinKeyboard() != null;
253        return false;
254    }
255
256    private LatinKeyboard getLatinKeyboard() {
257        if (mInputView != null)
258            return mInputView.getLatinKeyboard();
259        return null;
260    }
261
262    public void setPreferredLetters(int[] frequencies) {
263        LatinKeyboard latinKeyboard = getLatinKeyboard();
264        if (latinKeyboard != null)
265            latinKeyboard.setPreferredLetters(frequencies);
266    }
267
268    public void keyReleased() {
269        LatinKeyboard latinKeyboard = getLatinKeyboard();
270        if (latinKeyboard != null)
271            latinKeyboard.keyReleased();
272    }
273
274    public boolean isShiftedOrShiftLocked() {
275        LatinKeyboard latinKeyboard = getLatinKeyboard();
276        if (latinKeyboard != null)
277            return latinKeyboard.isShiftedOrShiftLocked();
278        return false;
279    }
280
281    public boolean isShiftLocked() {
282        LatinKeyboard latinKeyboard = getLatinKeyboard();
283        if (latinKeyboard != null)
284            return latinKeyboard.isShiftLocked();
285        return false;
286    }
287
288    public boolean isAutomaticTemporaryUpperCase() {
289        LatinKeyboard latinKeyboard = getLatinKeyboard();
290        if (latinKeyboard != null)
291            return latinKeyboard.isAutomaticTemporaryUpperCase();
292        return false;
293    }
294
295    public boolean isManualTemporaryUpperCase() {
296        LatinKeyboard latinKeyboard = getLatinKeyboard();
297        if (latinKeyboard != null)
298            return latinKeyboard.isManualTemporaryUpperCase();
299        return false;
300    }
301
302    private void setManualTemporaryUpperCase(boolean shifted) {
303        LatinKeyboard latinKeyboard = getLatinKeyboard();
304        if (latinKeyboard != null) {
305            // On non-distinct multi touch panel device, we should also turn off the shift locked
306            // state when shift key is pressed to go to normal mode.
307            // On the other hand, on distinct multi touch panel device, turning off the shift locked
308            // state with shift key pressing is handled by onReleaseShift().
309            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
310                latinKeyboard.setShiftLocked(false);
311            }
312            if (latinKeyboard.setShifted(shifted)) {
313                mInputView.invalidateAllKeys();
314            }
315        }
316    }
317
318    private void setShiftLocked(boolean shiftLocked) {
319        LatinKeyboard latinKeyboard = getLatinKeyboard();
320        if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
321            mInputView.invalidateAllKeys();
322        }
323    }
324
325    /**
326     * Toggle keyboard shift state triggered by user touch event.
327     */
328    public void toggleShift() {
329        mInputMethodService.mHandler.cancelUpdateShiftState();
330        if (DEBUG_STATE)
331            Log.d(TAG, "toggleShift:"
332                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
333                    + " shiftKeyState=" + mShiftKeyState);
334        if (isAlphabetMode()) {
335            setManualTemporaryUpperCase(!isShiftedOrShiftLocked());
336        } else {
337            toggleShiftInSymbol();
338        }
339    }
340
341    public void toggleCapsLock() {
342        mInputMethodService.mHandler.cancelUpdateShiftState();
343        if (DEBUG_STATE)
344            Log.d(TAG, "toggleCapsLock:"
345                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
346                    + " shiftKeyState=" + mShiftKeyState);
347        if (isAlphabetMode()) {
348            if (isShiftLocked()) {
349                // Shift key is long pressed while caps lock state, we will toggle back to normal
350                // state. And mark as if shift key is released.
351                setShiftLocked(false);
352                mShiftKeyState.onRelease();
353            } else {
354                setShiftLocked(true);
355            }
356        }
357    }
358
359    private void setAutomaticTemporaryUpperCase() {
360        LatinKeyboard latinKeyboard = getLatinKeyboard();
361        if (latinKeyboard != null) {
362            latinKeyboard.setAutomaticTemporaryUpperCase();
363            mInputView.invalidateAllKeys();
364        }
365    }
366
367    /**
368     * Update keyboard shift state triggered by connected EditText status change.
369     */
370    public void updateShiftState() {
371        final ShiftKeyState shiftKeyState = mShiftKeyState;
372        if (DEBUG_STATE)
373            Log.d(TAG, "updateShiftState:"
374                    + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()
375                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
376                    + " shiftKeyState=" + shiftKeyState);
377        if (isAlphabetMode()) {
378            if (!isShiftLocked() && !shiftKeyState.isIgnoring()) {
379                if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) {
380                    // Only when shift key is releasing, automatic temporary upper case will be set.
381                    setAutomaticTemporaryUpperCase();
382                } else {
383                    setManualTemporaryUpperCase(shiftKeyState.isMomentary());
384                }
385            }
386        } else {
387            // In symbol keyboard mode, we should clear shift key state because only alphabet
388            // keyboard has shift key.
389            shiftKeyState.onRelease();
390        }
391    }
392
393    public void changeKeyboardMode() {
394        if (DEBUG_STATE)
395            Log.d(TAG, "changeKeyboardMode:"
396                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
397                    + " shiftKeyState=" + mShiftKeyState);
398        toggleKeyboardMode();
399        if (isShiftLocked() && isAlphabetMode())
400            setShiftLocked(true);
401        updateShiftState();
402    }
403
404    public void onPressShift() {
405        if (!isKeyboardAvailable())
406            return;
407        ShiftKeyState shiftKeyState = mShiftKeyState;
408        if (DEBUG_STATE)
409            Log.d(TAG, "onPressShift:"
410                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
411                    + " shiftKeyState=" + shiftKeyState);
412        if (isAlphabetMode()) {
413            if (isShiftLocked()) {
414                // Shift key is pressed while caps lock state, we will treat this state as shifted
415                // caps lock state and mark as if shift key pressed while normal state.
416                shiftKeyState.onPress();
417                setManualTemporaryUpperCase(true);
418            } else if (isAutomaticTemporaryUpperCase()) {
419                // Shift key is pressed while automatic temporary upper case, we have to move to
420                // manual temporary upper case.
421                shiftKeyState.onPress();
422                setManualTemporaryUpperCase(true);
423            } else if (isShiftedOrShiftLocked()) {
424                // In manual upper case state, we just record shift key has been pressing while
425                // shifted state.
426                shiftKeyState.onPressOnShifted();
427            } else {
428                // In base layout, chording or manual temporary upper case mode is started.
429                shiftKeyState.onPress();
430                toggleShift();
431            }
432        } else {
433            // In symbol mode, just toggle symbol and symbol more keyboard.
434            shiftKeyState.onPress();
435            toggleShift();
436        }
437    }
438
439    public void onReleaseShift() {
440        if (!isKeyboardAvailable())
441            return;
442        ShiftKeyState shiftKeyState = mShiftKeyState;
443        if (DEBUG_STATE)
444            Log.d(TAG, "onReleaseShift:"
445                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
446                    + " shiftKeyState=" + shiftKeyState);
447        if (isAlphabetMode()) {
448            if (shiftKeyState.isMomentary()) {
449                // After chording input while normal state.
450                toggleShift();
451            } else if (isShiftLocked() && !shiftKeyState.isIgnoring()) {
452                // Shift has been pressed without chording while caps lock state.
453                toggleCapsLock();
454            } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) {
455                // Shift has been pressed without chording while shifted state.
456                toggleShift();
457            }
458        }
459        shiftKeyState.onRelease();
460    }
461
462    public void onPressSymbol() {
463        if (DEBUG_STATE)
464            Log.d(TAG, "onReleaseShift:"
465                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
466                    + " symbolKeyState=" + mSymbolKeyState);
467        changeKeyboardMode();
468        mSymbolKeyState.onPress();
469    }
470
471    public void onReleaseSymbol() {
472        if (DEBUG_STATE)
473            Log.d(TAG, "onReleaseShift:"
474                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
475                    + " symbolKeyState=" + mSymbolKeyState);
476        if (mSymbolKeyState.isMomentary())
477            changeKeyboardMode();
478        mSymbolKeyState.onRelease();
479    }
480
481    public void onOtherKeyPressed() {
482        if (DEBUG_STATE)
483            Log.d(TAG, "onOtherKeyPressed:"
484                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
485                    + " shiftKeyState=" + mShiftKeyState
486                    + " symbolKeyState=" + mSymbolKeyState);
487        mShiftKeyState.onOtherKeyPressed();
488        mSymbolKeyState.onOtherKeyPressed();
489    }
490
491    private void toggleShiftInSymbol() {
492        if (isAlphabetMode())
493            return;
494        final LatinKeyboard keyboard;
495        if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
496            mCurrentId = mSymbolsShiftedId;
497            keyboard = getKeyboard(mCurrentId);
498            // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
499            // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true).
500            // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is
501            // called.
502            keyboard.setShiftLocked(true);
503        } else {
504            mCurrentId = mSymbolsId;
505            keyboard = getKeyboard(mCurrentId);
506            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
507            // indicator, we need to call enableShiftLock() and setShiftLocked(false).
508            keyboard.setShifted(false);
509        }
510        mInputView.setKeyboard(keyboard);
511    }
512
513    private void toggleKeyboardMode() {
514        loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary,
515                !mIsSymbols);
516        if (mIsSymbols) {
517            mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN;
518        } else {
519            mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
520        }
521    }
522
523    public boolean hasDistinctMultitouch() {
524        return mInputView != null && mInputView.hasDistinctMultitouch();
525    }
526
527    /**
528     * Updates state machine to figure out when to automatically switch back to alpha mode.
529     */
530    public void onKey(int key) {
531        // Switch back to alpha mode if user types one or more non-space/enter
532        // characters followed by a space/enter
533        switch (mSymbolsModeState) {
534        case SYMBOLS_MODE_STATE_BEGIN:
535            if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key > 0) {
536                mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL;
537            }
538            break;
539        case SYMBOLS_MODE_STATE_SYMBOL:
540            if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) {
541                changeKeyboardMode();
542            }
543            break;
544        }
545    }
546
547    public LatinKeyboardView getInputView() {
548        return mInputView;
549    }
550
551    public LatinKeyboardView onCreateInputView() {
552        createInputViewInternal(mLayoutId, true);
553        return mInputView;
554    }
555
556    private void createInputViewInternal(int newLayout, boolean forceReset) {
557        if (mLayoutId != newLayout || mInputView == null || forceReset) {
558            if (mInputView != null) {
559                mInputView.closing();
560            }
561            if (THEMES.length <= newLayout) {
562                newLayout = Integer.valueOf(DEFAULT_LAYOUT_ID);
563            }
564
565            Utils.GCUtils.getInstance().reset();
566            boolean tryGC = true;
567            for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
568                try {
569                    mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
570                            ).inflate(THEMES[newLayout], null);
571                    tryGC = false;
572                } catch (OutOfMemoryError e) {
573                    Log.w(TAG, "load keyboard failed: " + e);
574                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
575                            mLayoutId + "," + newLayout, e);
576                } catch (InflateException e) {
577                    Log.w(TAG, "load keyboard failed: " + e);
578                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
579                            mLayoutId + "," + newLayout, e);
580                }
581            }
582            mInputView.setOnKeyboardActionListener(mInputMethodService);
583            mLayoutId = newLayout;
584        }
585    }
586
587    private void postSetInputView() {
588        mInputMethodService.mHandler.post(new Runnable() {
589            @Override
590            public void run() {
591                if (mInputView != null) {
592                    mInputMethodService.setInputView(mInputView);
593                }
594                mInputMethodService.updateInputViewShown();
595            }
596        });
597    }
598
599    @Override
600    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
601        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
602            final int layoutId = Integer.valueOf(
603                    sharedPreferences.getString(key, DEFAULT_LAYOUT_ID));
604            createInputViewInternal(layoutId, false);
605            postSetInputView();
606        } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
607            mHasSettingsKey = getSettingsKeyMode(sharedPreferences, mInputMethodService);
608            createInputViewInternal(mLayoutId, true);
609            postSetInputView();
610        }
611    }
612
613    private int getColorScheme() {
614        return (mInputView != null)
615                ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE;
616    }
617
618    public void onAutoCompletionStateChanged(boolean isAutoCompletion) {
619        if (isAutoCompletion != mIsAutoCompletionActive) {
620            LatinKeyboardView keyboardView = getInputView();
621            mIsAutoCompletionActive = isAutoCompletion;
622            keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard())
623                    .onAutoCompletionStateChanged(isAutoCompletion));
624        }
625    }
626
627    private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) {
628        Resources resources = context.getResources();
629        final boolean showSettingsKeyOption = resources.getBoolean(
630                R.bool.config_enable_show_settings_key_option);
631        if (showSettingsKeyOption) {
632            final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
633                    resources.getString(DEFAULT_SETTINGS_KEY_MODE));
634            // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or
635            // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system
636            if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
637                    || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO))
638                            && Utils.hasMultipleEnabledIMEsOrSubtypes(
639                                    ((InputMethodManager) context.getSystemService(
640                                            Context.INPUT_METHOD_SERVICE))))) {
641                return true;
642            }
643        }
644        return false;
645    }
646}
647