KeyboardSwitcher.java revision 4199e29a7d796191d3e13ef07e6e80e91834fe7a
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka *
4923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * use this file except in compliance with the License. You may obtain a copy of
6923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License at
7571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka *
8923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0
9571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * License for the specific language governing permissions and limitations under
14923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokapackage com.android.inputmethod.keyboard;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
19571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
20571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
21571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
225cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaokaimport com.android.inputmethod.latin.Settings;
23571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeSwitcher;
245cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
255a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
268b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaokaimport android.content.Context;
27979f8690967ff5409fe18f5085858ccdb8e0ccf1satokimport android.content.SharedPreferences;
2836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasaniimport android.content.res.Resources;
298b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaokaimport android.util.Log;
30979f8690967ff5409fe18f5085858ccdb8e0ccf1satokimport android.view.InflateException;
314b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaokaimport android.view.inputmethod.EditorInfo;
3279efbed76f638be298493107fa2d0cd1b5eb529esatokimport android.view.inputmethod.InputMethodManager;
3336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
34681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaokaimport java.lang.ref.SoftReference;
3512659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.HashMap;
3612659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.Locale;
3712659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka
38979f8690967ff5409fe18f5085858ccdb8e0ccf1satokpublic class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
398b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private static final String TAG = "KeyboardSwitcher";
408b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private static final boolean DEBUG = false;
41f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public static final boolean DEBUG_STATE = false;
42923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4305ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka    private static String sConfigDefaultKeyboardThemeId;
44a327485e595c9f7676989097c830ff452085d4c9satok    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
4505ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka    private static final int[] KEYBOARD_THEMES = {
4675fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka        R.layout.input_basic,
4775fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka        R.layout.input_basic_highcontrast,
4875fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka        R.layout.input_stone_normal,
4975fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka        R.layout.input_stone_bold,
5075fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka        R.layout.input_gingerbread,
5105ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        R.layout.input_honeycomb,
5275fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka    };
53979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
540ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private SubtypeSwitcher mSubtypeSwitcher;
5527d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    private SharedPreferences mPrefs;
560ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
573a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    private LatinKeyboardView mInputView;
580ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private LatinIME mInputMethodService;
5931adfa78e2edae188edb05e869f9f68798857582satok
60f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
61f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
62889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
63353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private KeyboardId mSymbolsId;
64353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private KeyboardId mSymbolsShiftedId;
65353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
66353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private KeyboardId mCurrentId;
678b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
688b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
69466741d8a78965b8509bf527344f289e50873092Mike LeBeau
704b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka    private EditorInfo mAttribute;
71353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private boolean mIsSymbols;
721b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
7341feaaadb758a8b31d3e436063b4b5faed104d4dsatok     * what user actually typed. */
741b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private boolean mIsAutoCorrectionActive;
7504448c2978a81b8c479b254e0f40bce128da8f7bTadashi G. Takaoka    private boolean mVoiceKeyEnabled;
76507495efd57074994fdc2fda78db9d5345f4a3a8Tadashi G. Takaoka    private boolean mVoiceButtonOnPrimary;
779e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
789e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0;
799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1;
809e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2;
819e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    // The following states are used only on the distinct multi-touch panel devices.
829e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3;
839e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4;
849e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
85923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
864199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    // Indicates whether or not we have the settings key in option of settings
874199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    private boolean mSettingsKeyEnabledInSettings;
881508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa    private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
89503797ae0e55c74068470d237fb47c4da13ec4fbTadashi G. Takaoka    private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW =
90503797ae0e55c74068470d237fb47c4da13ec4fbTadashi G. Takaoka            R.string.settings_key_mode_always_show;
9117fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa    // NOTE: No need to have SETTINGS_KEY_MODE_ALWAYS_HIDE here because it's not being referred to
9217fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa    // in the source code now.
9317fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa    // Default is SETTINGS_KEY_MODE_AUTO.
9417fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa    private static final int DEFAULT_SETTINGS_KEY_MODE = SETTINGS_KEY_MODE_AUTO;
951508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa
96979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    private int mLayoutId;
97979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
980ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
990ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
1000ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    public static KeyboardSwitcher getInstance() {
1010ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        return sInstance;
1020ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
1030ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
1040ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private KeyboardSwitcher() {
105e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        // Intentional empty constructor for singleton.
1060ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
1070ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
10827d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    public static void init(LatinIME ims, SharedPreferences prefs) {
1090ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        sInstance.mInputMethodService = ims;
11027d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa        sInstance.mPrefs = prefs;
1110ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        sInstance.mSubtypeSwitcher = SubtypeSwitcher.getInstance();
112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
11305ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        try {
11405ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka            sConfigDefaultKeyboardThemeId = ims.getString(
11505ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka                    R.string.config_default_keyboard_theme_id);
11605ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka            sInstance.mLayoutId = Integer.valueOf(
11705ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka                    prefs.getString(PREF_KEYBOARD_LAYOUT, sConfigDefaultKeyboardThemeId));
11805ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        } catch (NumberFormatException e) {
11905ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka            sConfigDefaultKeyboardThemeId = "0";
12005ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka            sInstance.mLayoutId = 0;
12105ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        }
1220ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        prefs.registerOnSharedPreferenceChangeListener(sInstance);
123979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
124979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
125cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka    public void loadKeyboard(EditorInfo attribute, boolean voiceKeyEnabled,
126507495efd57074994fdc2fda78db9d5345f4a3a8Tadashi G. Takaoka            boolean voiceButtonOnPrimary) {
1279e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
128979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        try {
129cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            loadKeyboardInternal(attribute, voiceKeyEnabled, voiceButtonOnPrimary, false);
130979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } catch (RuntimeException e) {
1314b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka            // Get KeyboardId to record which keyboard has been failed to load.
132cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka            final KeyboardId id = getKeyboardId(attribute, false);
1334b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka            Log.w(TAG, "loading keyboard failed: " + id, e);
1344b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka            LatinImeLogger.logOnException(id.toString(), e);
135979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
136353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
137353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
138cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka    private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
139507495efd57074994fdc2fda78db9d5345f4a3a8Tadashi G. Takaoka            boolean voiceButtonOnPrimary, boolean isSymbols) {
1406516d0fdfcbaa4eb809a8a69bd876293043a68a4Amith Yamasani        if (mInputView == null) return;
1418b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
1424b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka        mAttribute = attribute;
14304448c2978a81b8c479b254e0f40bce128da8f7bTadashi G. Takaoka        mVoiceKeyEnabled = voiceButtonEnabled;
1448b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        mVoiceButtonOnPrimary = voiceButtonOnPrimary;
145c5bb4591b67b165f007e29bd9424d319ac3c7c28Brandon Ballinger        mIsSymbols = isSymbols;
1468b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        // Update the settings key state because number of enabled IMEs could have been changed
1474199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService);
148cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        final KeyboardId id = getKeyboardId(attribute, isSymbols);
149466741d8a78965b8509bf527344f289e50873092Mike LeBeau
1508bec4aa912c193135bebacfc75dc15f06c5dce6eTadashi G. Takaoka        final Keyboard oldKeyboard = mInputView.getKeyboard();
1518bec4aa912c193135bebacfc75dc15f06c5dce6eTadashi G. Takaoka        if (oldKeyboard != null && oldKeyboard.mId.equals(id))
1528bec4aa912c193135bebacfc75dc15f06c5dce6eTadashi G. Takaoka            return;
1538bec4aa912c193135bebacfc75dc15f06c5dce6eTadashi G. Takaoka
1544199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        makeSymbolsKeyboardIds(id.mMode, attribute);
155353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger        mCurrentId = id;
1568bec4aa912c193135bebacfc75dc15f06c5dce6eTadashi G. Takaoka        mInputView.setPreviewEnabled(mInputMethodService.getPopupOn());
157050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        setKeyboard(getKeyboard(id));
158050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    }
159050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka
160050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    private void setKeyboard(final Keyboard newKeyboard) {
161050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        final Keyboard oldKeyboard = mInputView.getKeyboard();
162050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        mInputView.setKeyboard(newKeyboard);
163050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        final boolean localeChanged = (oldKeyboard == null)
164050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka                || !newKeyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
165050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
166353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
167353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
168353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private LatinKeyboard getKeyboard(KeyboardId id) {
1698b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
170681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
171681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        if (keyboard == null) {
1720ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok            final Locale savedLocale =  mSubtypeSwitcher.changeSystemLocale(
1730ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok                    mSubtypeSwitcher.getInputLocale());
1748b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
175b0465116bd3786174ccd0034c8a165ebc723b60fTadashi G. Takaoka            keyboard = new LatinKeyboard(mInputMethodService, id);
176358e485b14938fbcb5af5be75aa29f2b73674100Amith Yamasani
177353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger            if (id.mEnableShiftLock) {
178353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger                keyboard.enableShiftLock();
179353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger            }
1808b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
1818b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
1828b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            if (DEBUG)
1838b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
1848b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
18536fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani
1860ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok            mSubtypeSwitcher.changeSystemLocale(savedLocale);
1878b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        } else if (DEBUG) {
1888b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
189353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger        }
1908b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
1911b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
1928b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        keyboard.setShifted(false);
193de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // If the cached keyboard had been switched to another keyboard while the language was
194de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
1955cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka        // we should reset the text fade factor. It is also applicable to shortcut key.
196de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        keyboard.setSpacebarTextFadeFactor(0.0f, null);
1975cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka        keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutAvailable(), null);
198681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        return keyboard;
199353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
200353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
2014199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    private boolean hasVoiceKey(boolean isSymbols) {
2024199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
2034199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
2044199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
2054199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    private boolean hasSettingsKey(EditorInfo attribute) {
2064199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        return mSettingsKeyEnabledInSettings
2074199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka            && !Utils.inPrivateImeOptions(mInputMethodService.getPackageName(),
2084199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka                    LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
2094199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
2104199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
211cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka    private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
212cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        final int mode = Utils.getKeyboardMode(attribute);
2138b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final boolean hasVoiceKey = hasVoiceKey(isSymbols);
21459c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        final int charColorId = getColorScheme();
21559c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        final int xmlId;
216e354a85ef44e13999aaefd735cef7f659090f6e8Ken Wakasa        final boolean enableShiftLock;
217e354a85ef44e13999aaefd735cef7f659090f6e8Ken Wakasa
218e354a85ef44e13999aaefd735cef7f659090f6e8Ken Wakasa        if (isSymbols) {
2195a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            if (mode == KeyboardId.MODE_PHONE) {
2202c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_phone_symbols;
2215a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            } else if (mode == KeyboardId.MODE_NUMBER) {
2222c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
2232c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_number;
2242c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            } else {
2252c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_symbols;
2262c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            }
227e354a85ef44e13999aaefd735cef7f659090f6e8Ken Wakasa            enableShiftLock = false;
2282c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka        } else {
2295a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            if (mode == KeyboardId.MODE_PHONE) {
2302c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_phone;
2312c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                enableShiftLock = false;
2325a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka            } else if (mode == KeyboardId.MODE_NUMBER) {
2332c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_number;
2342c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                enableShiftLock = false;
2352c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            } else {
2362c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_qwerty;
2372c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                enableShiftLock = true;
2382c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            }
239923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
2404199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final boolean hasSettingsKey = hasSettingsKey(attribute);
2419b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        final Resources res = mInputMethodService.getResources();
2429b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        final int orientation = res.getConfiguration().orientation;
2430ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        final Locale locale = mSubtypeSwitcher.getInputLocale();
2449b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        return new KeyboardId(
2454b13b4f94215368c6387b2564bdaf2cbcbe4c130Tadashi G. Takaoka                res.getResourceEntryName(xmlId), xmlId, charColorId, locale, orientation, mode,
2464199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka                attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, enableShiftLock);
2474199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
2484199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
2494199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    private void makeSymbolsKeyboardIds(final int mode, EditorInfo attribute) {
2504199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final Locale locale = mSubtypeSwitcher.getInputLocale();
2514199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final Resources res = mInputMethodService.getResources();
2524199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final int orientation = res.getConfiguration().orientation;
2534199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final int colorScheme = getColorScheme();
2544199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final boolean hasVoiceKey = mVoiceKeyEnabled && !mVoiceButtonOnPrimary;
2554199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final boolean hasSettingsKey = hasSettingsKey(attribute);
2564199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // Note: This comment is only applied for phone number keyboard layout.
2574199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
2584199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // between "phone keyboard" and "phone symbols keyboard".  But on xlarge device,
2594199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // "@integer/key_shift" key code is used for that purpose in order to properly display
2604199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
2614199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
2624199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // respectively here for xlarge device's layout switching.
2634199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols;
2644199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        final String xmlName = res.getResourceEntryName(xmlId);
2654199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        mSymbolsId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode,
2664199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka                attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, true);
2674199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift;
2684199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        mSymbolsShiftedId = new KeyboardId(xmlName, xmlId, colorScheme, locale, orientation, mode,
2694199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka                attribute, hasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, true);
270923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
271923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2723a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public int getKeyboardMode() {
273cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
274923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
2751679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
2763a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public boolean isAlphabetMode() {
277571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return mCurrentId != null && mCurrentId.isAlphabetKeyboard();
278923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
279923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
2801679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isInputViewShown() {
2811679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka        return mInputView != null && mInputView.isShown();
2821679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
2831679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
2841679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isKeyboardAvailable() {
285b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (mInputView != null)
286050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            return mInputView.getKeyboard() != null;
287b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
288b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
289b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
290050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    public LatinKeyboard getLatinKeyboard() {
291050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        if (mInputView != null) {
292050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            final Keyboard keyboard = mInputView.getKeyboard();
293050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            if (keyboard instanceof LatinKeyboard)
294050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka                return (LatinKeyboard)keyboard;
295050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        }
296b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return null;
2971679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
2981679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
2991679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public void keyReleased() {
300b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
301b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
3021679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka            latinKeyboard.keyReleased();
3031679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3041679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
305f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
306b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
307b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
308f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isShiftedOrShiftLocked();
309b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
3101679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3111679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
3121679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isShiftLocked() {
313b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
314b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
315b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return latinKeyboard.isShiftLocked();
316b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
3171679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3181679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
319f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isAutomaticTemporaryUpperCase() {
320f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
321f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
322f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isAutomaticTemporaryUpperCase();
323f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
324f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
325f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
326f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
327f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
328f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
329f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCase();
330f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
331f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
332f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
333d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    private boolean isManualTemporaryUpperCaseFromAuto() {
334d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
335d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        if (latinKeyboard != null)
336d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCaseFromAuto();
337d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        return false;
338d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    }
339d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka
340f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setManualTemporaryUpperCase(boolean shifted) {
341b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
3420d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka        if (latinKeyboard != null) {
3430d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On non-distinct multi touch panel device, we should also turn off the shift locked
3440d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state when shift key is pressed to go to normal mode.
3450d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On the other hand, on distinct multi touch panel device, turning off the shift locked
3460d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state with shift key pressing is handled by onReleaseShift().
3470d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
3480d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka                latinKeyboard.setShiftLocked(false);
3490d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
3500d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            if (latinKeyboard.setShifted(shifted)) {
3510d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka                mInputView.invalidateAllKeys();
3520d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
353979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
354979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
355979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
356b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void setShiftLocked(boolean shiftLocked) {
357b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
358b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
359b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            mInputView.invalidateAllKeys();
360b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
361979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
362979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
3630d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
3640d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Toggle keyboard shift state triggered by user touch event.
3650d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
366b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleShift() {
367b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
368f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
369f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleShift:"
370f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
371f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
372b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
373f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            setManualTemporaryUpperCase(!isShiftedOrShiftLocked());
374b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
375b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            toggleShiftInSymbol();
376b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
377b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
378b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
379b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleCapsLock() {
380b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
381f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
382f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleCapsLock:"
383f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
384f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
385b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
386b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            if (isShiftLocked()) {
387f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is long pressed while caps lock state, we will toggle back to normal
388f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // state. And mark as if shift key is released.
389f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setShiftLocked(false);
390f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                mShiftKeyState.onRelease();
391b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            } else {
392b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                setShiftLocked(true);
393b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
394b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
395889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
396889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
397f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setAutomaticTemporaryUpperCase() {
398f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
399f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null) {
400f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            latinKeyboard.setAutomaticTemporaryUpperCase();
401f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            mInputView.invalidateAllKeys();
402f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        }
403f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
404f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
4050d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
4060d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Update keyboard shift state triggered by connected EditText status change.
4070d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
408b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void updateShiftState() {
40945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final ShiftKeyState shiftKeyState = mShiftKeyState;
410f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
411f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "updateShiftState:"
412f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()
413f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
41445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState);
41545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (isAlphabetMode()) {
41645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            if (!isShiftLocked() && !shiftKeyState.isIgnoring()) {
41745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) {
41845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    // Only when shift key is releasing, automatic temporary upper case will be set.
41945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setAutomaticTemporaryUpperCase();
42045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                } else {
42145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setManualTemporaryUpperCase(shiftKeyState.isMomentary());
42245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                }
423f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
42445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        } else {
42545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // In symbol keyboard mode, we should clear shift key state because only alphabet
42645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // keyboard has shift key.
42745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            shiftKeyState.onRelease();
428b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
429889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
430889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
431b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void changeKeyboardMode() {
432f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
433f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "changeKeyboardMode:"
434f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
435f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
436b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        toggleKeyboardMode();
437b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isShiftLocked() && isAlphabetMode())
438b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            setShiftLocked(true);
439b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        updateShiftState();
440889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
441889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
442b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void onPressShift() {
443b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
444b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
445f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
446f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
447f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onPressShift:"
448f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
449f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState);
450f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (isAlphabetMode()) {
451f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (isShiftLocked()) {
452f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while caps lock state, we will treat this state as shifted
453f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // caps lock state and mark as if shift key pressed while normal state.
454f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
4556769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                setManualTemporaryUpperCase(true);
456f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isAutomaticTemporaryUpperCase()) {
457f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while automatic temporary upper case, we have to move to
458f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // manual temporary upper case.
4596769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                shiftKeyState.onPress();
460f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setManualTemporaryUpperCase(true);
461f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked()) {
462f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In manual upper case state, we just record shift key has been pressing while
463f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // shifted state.
464f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPressOnShifted();
465f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else {
466f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In base layout, chording or manual temporary upper case mode is started.
467f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
4686769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                toggleShift();
469f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
470b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
471f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            // In symbol mode, just toggle symbol and symbol more keyboard.
472f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            shiftKeyState.onPress();
4736769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka            toggleShift();
474b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
475b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
476b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
477b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void onReleaseShift() {
478b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
479b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
480f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
481f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
482f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onReleaseShift:"
483f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
484f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState);
485b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
486f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (shiftKeyState.isMomentary()) {
487f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // After chording input while normal state.
488f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleShift();
489f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftLocked() && !shiftKeyState.isIgnoring()) {
490f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while caps lock state.
491f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleCapsLock();
492f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()) {
493f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while shifted state.
494b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                toggleShift();
495d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka            } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()) {
496d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // Shift has been pressed without chording while manual temporary upper case
497d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // transited from automatic temporary upper case.
498d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                toggleShift();
499b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
500b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
501f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        shiftKeyState.onRelease();
502889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
503889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
5046c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onPressSymbol() {
505f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
5069e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onPressSymbol:"
507f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
508f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
509b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        changeKeyboardMode();
5106c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onPress();
5119e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
5126c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
5136c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
5146c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onReleaseSymbol() {
515f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
5169e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onReleaseSymbol:"
517f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
518f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
5199e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user chords the mode change key and
5209e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        // other key, then released the mode change key.
5219e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING)
522b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            changeKeyboardMode();
5236c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onRelease();
5246c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
5256c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
5266c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onOtherKeyPressed() {
52745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (DEBUG_STATE)
52845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            Log.d(TAG, "onOtherKeyPressed:"
52945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
53045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState
53145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
532f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        mShiftKeyState.onOtherKeyPressed();
5336c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onOtherKeyPressed();
5346c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
5356c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
5365f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    public void onCancelInput() {
5375f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user cancels sliding input.
5385f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1)
5395f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            changeKeyboardMode();
5405f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    }
5415f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka
542b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void toggleShiftInSymbol() {
5437e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        if (isAlphabetMode())
5447e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka            return;
5458b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final LatinKeyboard keyboard;
5467e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
547353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger            mCurrentId = mSymbolsShiftedId;
5488b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            keyboard = getKeyboard(mCurrentId);
54912659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
55012659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // enable the indicator, we need to call enableShiftLock() and setShiftLocked(true).
55112659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // Thus we can keep the ALT key's Key.on value true while LatinKey.onRelease() is
55212659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // called.
5538b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            keyboard.setShiftLocked(true);
5547e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        } else {
555353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger            mCurrentId = mSymbolsId;
5568b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            keyboard = getKeyboard(mCurrentId);
55712659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
55812659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka            // indicator, we need to call enableShiftLock() and setShiftLocked(false).
5598b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            keyboard.setShifted(false);
560923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
561050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        setKeyboard(keyboard);
562923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
563923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
5649e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    public boolean isInMomentaryAutoModeSwitchState() {
5659e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
5669e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
5679e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
568cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isVibrateAndSoundFeedbackRequired() {
569cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka        return mInputView == null || !mInputView.isInSlidingKeyInput();
570cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
571cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
5729e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private int getPointerCount() {
5739e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        return mInputView == null ? 0 : mInputView.getPointerCount();
5749e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
5759e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
576f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void toggleKeyboardMode() {
577cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
578e354a85ef44e13999aaefd735cef7f659090f6e8Ken Wakasa        if (mIsSymbols) {
5799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
580b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        } else {
5819e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
582b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
583b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    }
584b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
585c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
586c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka        return mInputView != null && mInputView.hasDistinctMultitouch();
587c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    }
588c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka
589b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    /**
5909e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka     * Updates state machine to figure out when to automatically snap back to the previous mode.
591b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani     */
592b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void onKey(int key) {
5939e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        if (DEBUG_STATE)
5949e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onKey: code=" + key + " autoModeSwitchState=" + mAutoModeSwitchState
5959e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    + " pointers=" + getPointerCount());
5969e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        switch (mAutoModeSwitchState) {
5979e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        case AUTO_MODE_SWITCH_STATE_MOMENTARY:
5989e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Only distinct multi touch devices can be in this state.
5995f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // On non-distinct multi touch devices, mode change key is handled by
6005f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
6015f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link LatinIME#onRelease}. So, on such devices, {@link #mAutoModeSwitchState} starts
6025f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // from {@link #AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN}, or
6035f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link #AUTO_MODE_SWITCH_STATE_ALPHA}, not from
6045f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link #AUTO_MODE_SWITCH_STATE_MOMENTARY}.
6059e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            if (key == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
6069e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Detected only the mode change key has been pressed, and then released.
6079e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                if (mIsSymbols) {
6089e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
6099e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                } else {
6109e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
6119e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                }
6129e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else if (getPointerCount() == 1) {
6139e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the mode change key
6149e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // and slid to other key, then released the finger.
6155f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // If the user cancels the sliding input, snapping back to the previous keyboard
6165f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // mode is handled by {@link #onCancelInput}.
6179e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                changeKeyboardMode();
6189e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else {
6199e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
6209e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // previous mode in {@link onReleaseSymbol} when the mode change key is released.
6219e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING;
6229e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            }
6239e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            break;
6249e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
6255ef421b58afa7bc58be40ed9331ce04998efbf56Tadashi G. Takaoka            if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key >= 0) {
6269e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL;
627b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
628b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
6299e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        case AUTO_MODE_SWITCH_STATE_SYMBOL:
6309e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Snap back to alpha keyboard mode if user types one or more non-space/enter
6319e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // characters followed by a space/enter.
632571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka            if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) {
633b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                changeKeyboardMode();
634b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
635b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
636b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
637923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
638979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
639979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public LatinKeyboardView getInputView() {
640979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        return mInputView;
641979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
642979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
6438d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    public LatinKeyboardView onCreateInputView() {
6448d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        createInputViewInternal(mLayoutId, true);
6458d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        return mInputView;
646979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
647979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
6488d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    private void createInputViewInternal(int newLayout, boolean forceReset) {
649e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        int layoutId = newLayout;
650e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        if (mLayoutId != layoutId || mInputView == null || forceReset) {
651979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (mInputView != null) {
652979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mInputView.closing();
653979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
65405ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka            if (KEYBOARD_THEMES.length <= layoutId) {
65505ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka                layoutId = Integer.valueOf(sConfigDefaultKeyboardThemeId);
656979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
657979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
6589502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka            Utils.GCUtils.getInstance().reset();
659979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            boolean tryGC = true;
6609502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka            for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
661979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                try {
662979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
66305ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka                            ).inflate(KEYBOARD_THEMES[layoutId], null);
664979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    tryGC = false;
665979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                } catch (OutOfMemoryError e) {
6667f0befe1f0e346ec6468f229f337eda32e19f6d8Tadashi G. Takaoka                    Log.w(TAG, "load keyboard failed: " + e);
6679502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
668e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                            mLayoutId + "," + layoutId, e);
669979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                } catch (InflateException e) {
6707f0befe1f0e346ec6468f229f337eda32e19f6d8Tadashi G. Takaoka                    Log.w(TAG, "load keyboard failed: " + e);
6719502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                    tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
672e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka                            mLayoutId + "," + layoutId, e);
673979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
674979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
675979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            mInputView.setOnKeyboardActionListener(mInputMethodService);
676e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka            mLayoutId = layoutId;
677979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
6788d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    }
6798d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka
6808d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    private void postSetInputView() {
681979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mInputMethodService.mHandler.post(new Runnable() {
6828d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            @Override
683979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            public void run() {
684979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                if (mInputView != null) {
685979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                    mInputMethodService.setInputView(mInputView);
686979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
687979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mInputMethodService.updateInputViewShown();
6888d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            }
6898d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        });
690979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
691979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
6928d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    @Override
693979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
694979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
6958b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            final int layoutId = Integer.valueOf(
69605ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka                    sharedPreferences.getString(key, sConfigDefaultKeyboardThemeId));
6978d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            createInputViewInternal(layoutId, false);
6988d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            postSetInputView();
6999502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka        } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
7004199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka            mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences,
7014199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka                    mInputMethodService);
7028d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            createInputViewInternal(mLayoutId, true);
7038d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            postSetInputView();
704979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
705979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
706979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
70759c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka    private int getColorScheme() {
70859c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        return (mInputView != null)
7095a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka                ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE;
710979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
711979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
7121b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
7131b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        if (isAutoCorrection != mIsAutoCorrectionActive) {
71441feaaadb758a8b31d3e436063b4b5faed104d4dsatok            LatinKeyboardView keyboardView = getInputView();
7151b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mIsAutoCorrectionActive = isAutoCorrection;
71641feaaadb758a8b31d3e436063b4b5faed104d4dsatok            keyboardView.invalidateKey(((LatinKeyboard) keyboardView.getKeyboard())
7171b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka                    .onAutoCorrectionStateChanged(isAutoCorrection));
71841feaaadb758a8b31d3e436063b4b5faed104d4dsatok        }
71941feaaadb758a8b31d3e436063b4b5faed104d4dsatok    }
7201508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa
7218b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private static boolean getSettingsKeyMode(SharedPreferences prefs, Context context) {
7228b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        Resources resources = context.getResources();
723503797ae0e55c74068470d237fb47c4da13ec4fbTadashi G. Takaoka        final boolean showSettingsKeyOption = resources.getBoolean(
724503797ae0e55c74068470d237fb47c4da13ec4fbTadashi G. Takaoka                R.bool.config_enable_show_settings_key_option);
72517fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa        if (showSettingsKeyOption) {
7269502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka            final String settingsKeyMode = prefs.getString(Settings.PREF_SETTINGS_KEY,
72717fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa                    resources.getString(DEFAULT_SETTINGS_KEY_MODE));
72817fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa            // We show the settings key when 1) SETTINGS_KEY_MODE_ALWAYS_SHOW or
72917fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa            // 2) SETTINGS_KEY_MODE_AUTO and there are two or more enabled IMEs on the system
73017fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa            if (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_ALWAYS_SHOW))
73117fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa                    || (settingsKeyMode.equals(resources.getString(SETTINGS_KEY_MODE_AUTO))
7329502cc177cc53678c9ddcc01d4d046f69220e13bTadashi G. Takaoka                            && Utils.hasMultipleEnabledIMEsOrSubtypes(
73379efbed76f638be298493107fa2d0cd1b5eb529esatok                                    ((InputMethodManager) context.getSystemService(
73479efbed76f638be298493107fa2d0cd1b5eb529esatok                                            Context.INPUT_METHOD_SERVICE))))) {
7358b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                return true;
73617fcd719de9a0ddcf9fd712481b28038419eec4eKen Wakasa            }
7374199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka            return false;
7381508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa        }
7394199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        // If the show settings key option is disabled, we always try showing the settings key.
7404199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka        return true;
7411508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa    }
742923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
743