• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
getInstance()142     public static KeyboardSwitcher getInstance() {
143         return sInstance;
144     }
145 
KeyboardSwitcher()146     private KeyboardSwitcher() {
147         // Intentional empty constructor for singleton.
148     }
149 
init(LatinIME ims)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      */
setLanguageSwitcher(LanguageSwitcher languageSwitcher)169     public void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
170         mLanguageSwitcher = languageSwitcher;
171         mInputLocale = mLanguageSwitcher.getInputLocale();
172     }
173 
makeSymbolsId(boolean hasVoice)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 
makeSymbolsShiftedId(boolean hasVoice)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 
makeKeyboards(boolean forceCreate)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 
KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice)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 
KeyboardId(int xml, boolean hasVoice)224         public KeyboardId(int xml, boolean hasVoice) {
225             this(xml, 0, false, hasVoice);
226         }
227 
228         @Override
equals(Object other)229         public boolean equals(Object other) {
230             return other instanceof KeyboardId && equals((KeyboardId) other);
231         }
232 
equals(KeyboardId other)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
hashCode()241         public int hashCode() {
242             return mHashCode;
243         }
244     }
245 
setVoiceMode(boolean enableVoice, boolean voiceOnPrimary)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 
hasVoiceButton(boolean isSymbols)255     private boolean hasVoiceButton(boolean isSymbols) {
256         return mHasVoice && (isSymbols != mVoiceOnPrimary);
257     }
258 
setKeyboardMode(int mode, int imeOptions, boolean enableVoice)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 
setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols)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 
getKeyboard(KeyboardId id)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 
getKeyboardId(int mode, int imeOptions, boolean isSymbols)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 
getKeyboardMode()371     public int getKeyboardMode() {
372         return mMode;
373     }
374 
isAlphabetMode()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 
setShifted(boolean shifted)388     public void setShifted(boolean shifted) {
389         if (mInputView != null) {
390             mInputView.setShifted(shifted);
391         }
392     }
393 
setShiftLocked(boolean shiftLocked)394     public void setShiftLocked(boolean shiftLocked) {
395         if (mInputView != null) {
396             mInputView.setShiftLocked(shiftLocked);
397         }
398     }
399 
toggleShift()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 
onCancelInput()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 
toggleSymbols()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 
hasDistinctMultitouch()442     public boolean hasDistinctMultitouch() {
443         return mInputView != null && mInputView.hasDistinctMultitouch();
444     }
445 
setAutoModeSwitchStateMomentary()446     public void setAutoModeSwitchStateMomentary() {
447         mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
448     }
449 
isInMomentaryAutoModeSwitchState()450     public boolean isInMomentaryAutoModeSwitchState() {
451         return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
452     }
453 
isInChordingAutoModeSwitchState()454     public boolean isInChordingAutoModeSwitchState() {
455         return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING;
456     }
457 
isVibrateAndSoundFeedbackRequired()458     public boolean isVibrateAndSoundFeedbackRequired() {
459         return mInputView != null && !mInputView.isInSlidingKeyInput();
460     }
461 
getPointerCount()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      */
onKey(int key)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 
getInputView()514     public LatinKeyboardView getInputView() {
515         return mInputView;
516     }
517 
recreateInputView()518     public void recreateInputView() {
519         changeLatinKeyboardView(mLayoutId, true);
520     }
521 
changeLatinKeyboardView(int newLayout, boolean forceReset)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 
onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)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 
isBlackSym()568     public boolean isBlackSym () {
569         if (mInputView != null && mInputView.getSymbolColorScheme() == 1) {
570             return true;
571         }
572         return false;
573     }
574 
getCharColorId()575     private int getCharColorId () {
576         if (isBlackSym()) {
577             return CHAR_THEME_COLOR_BLACK;
578         } else {
579             return CHAR_THEME_COLOR_WHITE;
580         }
581     }
582 
onAutoCompletionStateChanged(boolean isAutoCompletion)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 
updateSettingsKeyState(SharedPreferences prefs)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