KeyboardSwitcher.java revision af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6
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
19c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.content.Context;
20c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.content.SharedPreferences;
21364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaokaimport android.content.res.Configuration;
22c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.content.res.Resources;
23b39f612d9791877bf0136fa7af9ae054092b9eabTadashi G. Takaokaimport android.util.DisplayMetrics;
24c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.util.Log;
2513a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaokaimport android.view.ContextThemeWrapper;
26c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.view.InflateException;
27c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.view.LayoutInflater;
28c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.view.View;
29c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaokaimport android.view.inputmethod.EditorInfo;
30c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka
315ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
3272934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.ModifierKeyState;
3372934bd5967d0127f71fd4d66158b18b4e6ceefeTadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.ShiftKeyState;
34571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinIME;
35571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.LatinImeLogger;
36571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.R;
375cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaokaimport com.android.inputmethod.latin.Settings;
38571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaokaimport com.android.inputmethod.latin.SubtypeSwitcher;
395cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaokaimport com.android.inputmethod.latin.Utils;
405a309f57155fb95667c2ccdda730eaf175de8876Tadashi G. Takaoka
41681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaokaimport java.lang.ref.SoftReference;
4212659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.HashMap;
4312659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.Locale;
4412659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka
45979f8690967ff5409fe18f5085858ccdb8e0ccf1satokpublic class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
46c25e07d21261583d0eeca5aa0fb6e3ffe93dc603Tadashi G. Takaoka    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
47c25e07d21261583d0eeca5aa0fb6e3ffe93dc603Tadashi G. Takaoka    private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
48f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public static final boolean DEBUG_STATE = false;
49923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
50a327485e595c9f7676989097c830ff452085d4c9satok    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
5105ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka    private static final int[] KEYBOARD_THEMES = {
5213a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme,
5313a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_HighContrast,
5413a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Stone,
5513a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Stone_Bold,
5613a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Gingerbread,
579116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka        R.style.KeyboardTheme_IceCreamSandwich,
5875fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka    };
59979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
600ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private SubtypeSwitcher mSubtypeSwitcher;
6127d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    private SharedPreferences mPrefs;
620ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
6386e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka    private View mCurrentInputView;
64c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    private LatinKeyboardView mKeyboardView;
650ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private LatinIME mInputMethodService;
660a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka    private String mPackageName;
670a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka    private Resources mResources;
6831adfa78e2edae188edb05e869f9f68798857582satok
69717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // TODO: Combine these key state objects with auto mode switch state.
70f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
71f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
72889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
7360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mMainKeyboardId;
7460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mSymbolsKeyboardId;
7560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mSymbolsShiftedKeyboardId;
76353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
77353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private KeyboardId mCurrentId;
788b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
798b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
80466741d8a78965b8509bf527344f289e50873092Mike LeBeau
81055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState();
82055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
831b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
8441feaaadb758a8b31d3e436063b4b5faed104d4dsatok     * what user actually typed. */
851b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private boolean mIsAutoCorrectionActive;
869e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
87717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
88717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // and ModifierKeyState.
89717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_ALPHA = 0;
90717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_SYMBOL_BEGIN = 1;
91717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_SYMBOL = 2;
929e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    // The following states are used only on the distinct multi-touch panel devices.
93717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
94717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
95717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_CHORDING_ALPHA = 5;
96717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
97717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private int mSwitchState = SWITCH_STATE_ALPHA;
98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
995a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private int mThemeIndex = -1;
1005a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private Context mThemeContext;
101979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1020ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
1030ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
104055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public class KeyboardLayoutState {
105055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsValid;
106055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsAlphabetMode;
107055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsShiftLocked;
108055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsShifted;
109055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
110055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public boolean isValid() {
111055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            return mIsValid;
112055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
113055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
114055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public void save() {
11594ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka            if (mCurrentId == null) {
11694ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka                return;
11794ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka            }
118055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsAlphabetMode = isAlphabetMode();
11938b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            if (mIsAlphabetMode) {
12038b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShiftLocked = isShiftLocked();
12138b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked();
12238b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            } else {
12338b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShiftLocked = false;
12494ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka                mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId);
12538b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            }
126055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = true;
127055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
128055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
129055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public KeyboardId getKeyboardId() {
130055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return mMainKeyboardId;
131055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
132055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
133055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mMainKeyboardId;
134055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            } else {
135055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId;
136055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
137055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
138055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
139055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public void restore() {
140055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return;
141055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = false;
142055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
143055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
144055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isAlphabetMode = isAlphabetMode();
145055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShiftLocked = isAlphabetMode && isShiftLocked();
146055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked();
147055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                if (mIsShiftLocked != isShiftLocked) {
148055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    toggleCapsLock();
149055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                } else if (mIsShifted != isShifted) {
150055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onPressShift(false);
151055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onReleaseShift(false);
152055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                }
153055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
154055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
155055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
156055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
1570ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    public static KeyboardSwitcher getInstance() {
1580ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        return sInstance;
1590ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
1600ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
1610ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private KeyboardSwitcher() {
162e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        // Intentional empty constructor for singleton.
1630ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
1640ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
16527d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    public static void init(LatinIME ims, SharedPreferences prefs) {
166364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        sInstance.initInternal(ims, prefs);
167364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    }
168364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
169364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    private void initInternal(LatinIME ims, SharedPreferences prefs) {
170364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mInputMethodService = ims;
171364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPackageName = ims.getPackageName();
172364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mResources = ims.getResources();
173364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPrefs = prefs;
174364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
175364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
176364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        prefs.registerOnSharedPreferenceChangeListener(this);
1775a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
178979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1795a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
1805a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String defaultThemeId = context.getString(R.string.config_default_keyboard_theme_id);
1815a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String themeId = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultThemeId);
18205ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        try {
1835a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            final int themeIndex = Integer.valueOf(themeId);
1845a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            if (themeIndex >= 0 && themeIndex < KEYBOARD_THEMES.length)
1855a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                return themeIndex;
18605ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        } catch (NumberFormatException e) {
1875a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            // Format error, keyboard theme is default to 0.
1885a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        }
1895a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        Log.w(TAG, "Illegal keyboard theme in preference: " + themeId + ", default to 0");
1905a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        return 0;
1915a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
1925a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka
1935a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private void setContextThemeWrapper(Context context, int themeIndex) {
1945a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        if (mThemeIndex != themeIndex) {
1955a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeIndex = themeIndex;
1965a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]);
1975a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mKeyboardCache.clear();
19805ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        }
199979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
200979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2012ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) {
202979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        try {
2032ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
2042ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
2052ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
206055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
207979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } catch (RuntimeException e) {
20860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
20960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
211353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
212353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
213055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public KeyboardLayoutState getKeyboardState() {
214055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return mSavedKeyboardState;
215055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
216055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
217055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public void onFinishInputView() {
218055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mIsAutoCorrectionActive = false;
219055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
220055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
221c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    public void onHideWindow() {
222c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mIsAutoCorrectionActive = false;
223c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    }
224c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka
225055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void setKeyboard(final Keyboard keyboard) {
226c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
227055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardView.setKeyboard(keyboard);
228055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mCurrentId = keyboard.mId;
229055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mSwitchState = getSwitchState(mCurrentId);
230055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        updateShiftLockState(keyboard);
231c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        mKeyboardView.setKeyPreviewPopupEnabled(
2320a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
2330a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
234050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        final boolean localeChanged = (oldKeyboard == null)
235055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
236050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
237286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        updateShiftState();
238353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
239353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
240055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private int getSwitchState(KeyboardId id) {
241055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
242055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
243055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
244055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void updateShiftLockState(Keyboard keyboard) {
245055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
246055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
247055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
248055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // that takes care of the current keyboard having such ALT key or not.
249055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(keyboard.hasShiftLockKey());
250055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
251055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
252055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // indicator, we need to call setShiftLocked(false).
253055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(false);
254055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
255055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
256055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
257353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private LatinKeyboard getKeyboard(KeyboardId id) {
2588b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
259681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
260681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        if (keyboard == null) {
261af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka            final Locale savedLocale = Utils.setSystemLocale(mResources, id.mLocale);
2628da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            try {
2638da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
2648da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            } finally {
2658da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                Utils.setSystemLocale(mResources, savedLocale);
2668da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
2678b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
2688da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
2698da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            if (DEBUG_CACHE) {
2708b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
2718b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
2728da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
273e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        } else if (DEBUG_CACHE) {
2748b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
275353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger        }
2768b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
2771b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
27873c02390d31bbf32b78763db66b04004666fb5a3Tadashi G. Takaoka        keyboard.setShiftLocked(false);
2798b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        keyboard.setShifted(false);
280de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // If the cached keyboard had been switched to another keyboard while the language was
281de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
2825cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka        // we should reset the text fade factor. It is also applicable to shortcut key.
283de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        keyboard.setSpacebarTextFadeFactor(0.0f, null);
2844503e2ea9853c1573f60903d8639d82e39e07c56Tadashi G. Takaoka        keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null);
285681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        return keyboard;
286353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
287353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
2882ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
2892ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final boolean isShift, Settings.Values settingsValues) {
2902ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int mode = Utils.getKeyboardMode(editorInfo);
29159c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        final int xmlId;
29260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        switch (mode) {
29360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_PHONE:
29460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
29560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
29660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_NUMBER:
29760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = R.xml.kbd_number;
29860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
29960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        default:
30060ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            if (isSymbols) {
30160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
3022c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            } else {
3032c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_qwerty;
3042c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            }
30560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
30760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka
308cee174b8ccb47ccddc8a8a7e88a9c617f9b5e766Ken Wakasa        final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
30973e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean noMicrophone = Utils.inPrivateImeOptions(
31073e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
31173e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                || Utils.inPrivateImeOptions(
31273e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                        null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
31373e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
31473e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                && !noMicrophone;
3152ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
3160a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka        final boolean noSettingsKey = Utils.inPrivateImeOptions(
3172ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
3182ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
3192ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
320c3afe2c244c14ed0f8a355fe608b3402e3656330Tadashi G. Takaoka        final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
321af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka        final boolean forceAscii = Utils.inPrivateImeOptions(
322af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
323af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka        final boolean asciiCapable = mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(
324af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
325af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka        final Locale locale = (forceAscii && !asciiCapable)
326af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                ? Locale.US : mSubtypeSwitcher.getInputLocale();
3272ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final Configuration conf = mResources.getConfiguration();
328b39f612d9791877bf0136fa7af9ae054092b9eabTadashi G. Takaoka        final DisplayMetrics dm = mResources.getDisplayMetrics();
3290a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka
3309b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        return new KeyboardId(
331af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                mResources.getResourceEntryName(xmlId), xmlId, locale, conf.orientation,
332af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                dm.widthPixels, mode, editorInfo, hasSettingsKey, f2KeyMode, noSettingsKey,
333af52c0ea04c6563feaa6ea7dbac5dd87c2a48cc6Tadashi G. Takaoka                voiceKeyEnabled, hasShortcutKey);
3344199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
3354199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
3363a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public int getKeyboardMode() {
337cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
338923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
3391679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
3403a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public boolean isAlphabetMode() {
341571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return mCurrentId != null && mCurrentId.isAlphabetKeyboard();
342923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
343923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
3441679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isInputViewShown() {
34586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        return mCurrentInputView != null && mCurrentInputView.isShown();
3461679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3471679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
3481679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isKeyboardAvailable() {
349c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null)
350c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            return mKeyboardView.getKeyboard() != null;
351b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
352b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
353b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
354050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    public LatinKeyboard getLatinKeyboard() {
355c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null) {
356c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            final Keyboard keyboard = mKeyboardView.getKeyboard();
357050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            if (keyboard instanceof LatinKeyboard)
358050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka                return (LatinKeyboard)keyboard;
359050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        }
360b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return null;
3611679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3621679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
363f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
364b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
365b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
366f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isShiftedOrShiftLocked();
367b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
3681679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3691679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
3701679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isShiftLocked() {
371b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
372b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
373b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return latinKeyboard.isShiftLocked();
374b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
3751679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
3761679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
377f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isAutomaticTemporaryUpperCase() {
378f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
379f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
380f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isAutomaticTemporaryUpperCase();
381f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
382f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
383f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
384f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
385f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
386f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
387f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCase();
388f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
389f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
390f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
391d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    private boolean isManualTemporaryUpperCaseFromAuto() {
392d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
393d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        if (latinKeyboard != null)
394d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCaseFromAuto();
395d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        return false;
396d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    }
397d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka
398f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setManualTemporaryUpperCase(boolean shifted) {
399b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
4000d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka        if (latinKeyboard != null) {
4010d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On non-distinct multi touch panel device, we should also turn off the shift locked
4020d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state when shift key is pressed to go to normal mode.
4030d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On the other hand, on distinct multi touch panel device, turning off the shift locked
4040d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state with shift key pressing is handled by onReleaseShift().
405101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
4060d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka                latinKeyboard.setShiftLocked(false);
4070d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
4080d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            if (latinKeyboard.setShifted(shifted)) {
409c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.invalidateAllKeys();
4100d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
411979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
412979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
413979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
414b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void setShiftLocked(boolean shiftLocked) {
415b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
416b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
417c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            mKeyboardView.invalidateAllKeys();
418b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
419979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
420979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
4210d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
4220d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Toggle keyboard shift state triggered by user touch event.
4230d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
424b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleShift() {
425b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
426f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
427f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleShift:"
428f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
429f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
430b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
431f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            setManualTemporaryUpperCase(!isShiftedOrShiftLocked());
432b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
433b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            toggleShiftInSymbol();
434b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
435b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
436b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
437b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleCapsLock() {
438b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
439f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
440f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleCapsLock:"
441f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
442f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
443b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
444b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            if (isShiftLocked()) {
445f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is long pressed while caps lock state, we will toggle back to normal
446f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // state. And mark as if shift key is released.
447f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setShiftLocked(false);
448f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                mShiftKeyState.onRelease();
449b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            } else {
450b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                setShiftLocked(true);
451b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
452b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
453889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
454889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
455f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setAutomaticTemporaryUpperCase() {
456286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        if (mKeyboardView == null) return;
457286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        final Keyboard keyboard = mKeyboardView.getKeyboard();
458e56e88beffe7afee3b41da4697304e1f4c031787Ken Wakasa        if (keyboard == null) return;
459e56e88beffe7afee3b41da4697304e1f4c031787Ken Wakasa        keyboard.setAutomaticTemporaryUpperCase();
460286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        mKeyboardView.invalidateAllKeys();
461f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
462f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
4630d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
4640d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Update keyboard shift state triggered by connected EditText status change.
4650d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
466b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void updateShiftState() {
46745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final ShiftKeyState shiftKeyState = mShiftKeyState;
468f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
469f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "updateShiftState:"
470f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()
471f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
472286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " shiftKeyState=" + shiftKeyState
473286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " isAlphabetMode=" + isAlphabetMode()
474286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " isShiftLocked=" + isShiftLocked());
47545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (isAlphabetMode()) {
47645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            if (!isShiftLocked() && !shiftKeyState.isIgnoring()) {
47745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) {
47845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    // Only when shift key is releasing, automatic temporary upper case will be set.
47945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setAutomaticTemporaryUpperCase();
48045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                } else {
48145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setManualTemporaryUpperCase(shiftKeyState.isMomentary());
48245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                }
483f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
48445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        } else {
48545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // In symbol keyboard mode, we should clear shift key state because only alphabet
48645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // keyboard has shift key.
48745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            shiftKeyState.onRelease();
488b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
489889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
490889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
491b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void changeKeyboardMode() {
492f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
493f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "changeKeyboardMode:"
494f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
495f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
496b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        toggleKeyboardMode();
497b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isShiftLocked() && isAlphabetMode())
498b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            setShiftLocked(true);
499b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        updateShiftState();
500889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
501889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
502e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onPressShift(boolean withSliding) {
503b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
504b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
505f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
506f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
507f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onPressShift:"
508f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
509e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
510f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (isAlphabetMode()) {
511f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (isShiftLocked()) {
512f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while caps lock state, we will treat this state as shifted
513f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // caps lock state and mark as if shift key pressed while normal state.
514f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
5156769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                setManualTemporaryUpperCase(true);
516f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isAutomaticTemporaryUpperCase()) {
517f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while automatic temporary upper case, we have to move to
518f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // manual temporary upper case.
5196769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                shiftKeyState.onPress();
520f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setManualTemporaryUpperCase(true);
521f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked()) {
522f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In manual upper case state, we just record shift key has been pressing while
523f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // shifted state.
524f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPressOnShifted();
525f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else {
526f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In base layout, chording or manual temporary upper case mode is started.
527f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
5286769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                toggleShift();
529f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
530b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
531f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            // In symbol mode, just toggle symbol and symbol more keyboard.
532f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            shiftKeyState.onPress();
5336769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka            toggleShift();
534717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
535b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
536b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
537b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
538e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onReleaseShift(boolean withSliding) {
539b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
540b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
541f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
542f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
543f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onReleaseShift:"
544f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
545e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
546b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
547f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (shiftKeyState.isMomentary()) {
548f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // After chording input while normal state.
549f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleShift();
550e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftLocked() && !shiftKeyState.isIgnoring() && !withSliding) {
551f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while caps lock state.
552f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleCapsLock();
553e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // To be able to turn off caps lock by "double tap" on shift key, we should ignore
554e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // the second tap of the "double tap" from now for a while because we just have
555e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // already turned off caps lock above.
556c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.startIgnoringDoubleTap();
557e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()
558e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
559f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while shifted state.
560b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                toggleShift();
561e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()
562e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
563d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // Shift has been pressed without chording while manual temporary upper case
564d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // transited from automatic temporary upper case.
565d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                toggleShift();
566b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
567717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        } else {
568717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // In symbol mode, snap back to the previous keyboard mode if the user chords the shift
569717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // key and another key, then releases the shift key.
570717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) {
571717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
572717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
573b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
574f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        shiftKeyState.onRelease();
575889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
576889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
5776c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onPressSymbol() {
578f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
5799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onPressSymbol:"
580f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
581f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
582b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        changeKeyboardMode();
5836c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onPress();
584717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
5856c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
5866c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
5876c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onReleaseSymbol() {
588f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
5899e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onReleaseSymbol:"
590f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
591f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
5929e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user chords the mode change key and
593717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // another key, then releases the mode change key.
594717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) {
595b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            changeKeyboardMode();
596717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
5976c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onRelease();
5986c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
5996c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6006c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onOtherKeyPressed() {
60145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (DEBUG_STATE)
60245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            Log.d(TAG, "onOtherKeyPressed:"
60345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
60445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState
60545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
606f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        mShiftKeyState.onOtherKeyPressed();
6076c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onOtherKeyPressed();
6086c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6096c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6105f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    public void onCancelInput() {
6115f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user cancels sliding input.
612717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (getPointerCount() == 1) {
613717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
614717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                changeKeyboardMode();
615717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
616717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
617717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
618717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
6195f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    }
6205f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka
621b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void toggleShiftInSymbol() {
6227e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        if (isAlphabetMode())
6237e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka            return;
6248b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final LatinKeyboard keyboard;
62560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mSymbolsKeyboardId)
62660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
62760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
6287e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        } else {
62960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsKeyboardId);
630923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
631050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        setKeyboard(keyboard);
632923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
633923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
634717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public boolean isInMomentarySwitchState() {
635717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
636717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
6379e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
6389e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
639cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isVibrateAndSoundFeedbackRequired() {
640c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput();
641cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
642cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
6439e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private int getPointerCount() {
644c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount();
6459e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
6469e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
647f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void toggleKeyboardMode() {
64860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mMainKeyboardId)) {
64960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mSymbolsKeyboardId));
650b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        } else {
65160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mMainKeyboardId));
652b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
653b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    }
654b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
655c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
656c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch();
657c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    }
658c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka
659717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isSpaceCharacter(int c) {
660717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
661717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
662717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
663717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isQuoteCharacter(int c) {
664717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // Apostrophe, quotation mark.
6650730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard        if (c == Keyboard.CODE_SINGLE_QUOTE || c == Keyboard.CODE_DOUBLE_QUOTE)
666717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
667717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2018: Left single quotation mark
668717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2019: Right single quotation mark
669717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201a: Single low-9 quotation mark
670717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201b: Single high-reversed-9 quotation mark
671717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201c: Left double quotation mark
672717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201d: Right double quotation mark
673717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201e: Double low-9 quotation mark
674717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201f: Double high-reversed-9 quotation mark
675717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c >= '\u2018' && c <= '\u201f')
676717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
677717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00ab: Left-pointing double angle quotation mark
678717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00bb: Right-pointing double angle quotation mark
679717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c == '\u00ab' || c == '\u00bb')
680717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
681717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return false;
682717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
683717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
684b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    /**
6859e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka     * Updates state machine to figure out when to automatically snap back to the previous mode.
686b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani     */
687717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public void onKey(int code) {
6889e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        if (DEBUG_STATE)
689717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState
6909e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    + " pointers=" + getPointerCount());
691717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        switch (mSwitchState) {
692717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
6939e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Only distinct multi touch devices can be in this state.
6945f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // On non-distinct multi touch devices, mode change key is handled by
6955f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
696717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts
697717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from
698717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link #SWITCH_STATE_MOMENTARY}.
699717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
7009e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Detected only the mode change key has been pressed, and then released.
70160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                if (mCurrentId.equals(mMainKeyboardId)) {
702717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                    mSwitchState = SWITCH_STATE_ALPHA;
70360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                } else {
70460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
7059e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                }
7069e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else if (getPointerCount() == 1) {
7079e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the mode change key
7089e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // and slid to other key, then released the finger.
7095f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // If the user cancels the sliding input, snapping back to the previous keyboard
7105f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // mode is handled by {@link #onCancelInput}.
7119e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                changeKeyboardMode();
7129e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else {
7139e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
7149e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // previous mode in {@link onReleaseSymbol} when the mode change key is released.
715717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_ALPHA;
716717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
717717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            break;
718717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
719717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SHIFT) {
720717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Detected only the shift key has been pressed on symbol layout, and then released.
721717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
722717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (getPointerCount() == 1) {
723717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the shift key on
724717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // symbol mode and slid to other key, then released the finger.
725717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
726717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
727717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else {
728717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
729717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // previous mode in {@link onReleaseShift} when the shift key is released.
730717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_SYMBOL;
7319e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            }
7329e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            break;
733717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL_BEGIN:
734717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (!isSpaceCharacter(code) && code >= 0) {
735717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
736b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
7370b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // Snap back to alpha keyboard mode immediately if user types a quote character.
7380b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            if (isQuoteCharacter(code)) {
7390b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa                changeKeyboardMode();
7400b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            }
741b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
742717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL:
743717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_CHORDING_SYMBOL:
7449e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Snap back to alpha keyboard mode if user types one or more non-space/enter
7450b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // characters followed by a space/enter or a quote character.
746717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
747b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                changeKeyboardMode();
748b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
749b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
750b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
751923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
752979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
753c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka    public LatinKeyboardView getKeyboardView() {
754c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView;
755979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
756979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
757c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public View onCreateInputView() {
758f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return createInputView(mThemeIndex, true);
759979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
760979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
761f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private View createInputView(final int newThemeIndex, final boolean forceRecreate) {
762f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mCurrentInputView != null && mThemeIndex == newThemeIndex && !forceRecreate)
763f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            return mCurrentInputView;
764f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
765f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mKeyboardView != null) {
766f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            mKeyboardView.closing();
767f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        }
768f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
7695a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final int oldThemeIndex = mThemeIndex;
770f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
771f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        boolean tryGC = true;
772f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
773f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            try {
7745a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                setContextThemeWrapper(mInputMethodService, newThemeIndex);
7755a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
77613a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka                        R.layout.input_view, null);
777f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                tryGC = false;
778f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
779f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
7805a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
7815a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
782f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (InflateException e) {
783f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
7845a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
7855a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
786979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
787979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
788f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
789c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka        mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
7905a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboardView.setKeyboardActionListener(mInputMethodService);
7915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
7925ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // This always needs to be set since the accessibility state can
7935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // potentially change without the input view being re-created.
7945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        AccessibleKeyboardViewProxy.setView(mKeyboardView);
7955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
796f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return mCurrentInputView;
7978d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    }
7988d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka
799f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private void postSetInputView(final View newInputView) {
800979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mInputMethodService.mHandler.post(new Runnable() {
8018d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            @Override
802979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            public void run() {
803f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                if (newInputView != null) {
804f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                    mInputMethodService.setInputView(newInputView);
805979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
806979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mInputMethodService.updateInputViewShown();
8078d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            }
8088d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        });
809979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
810979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
8118d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    @Override
812979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
813979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
8142ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
8152ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            postSetInputView(createInputView(themeIndex, false));
8162ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) {
817f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            postSetInputView(createInputView(mThemeIndex, true));
818979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
819979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
820979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
8211b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
8225e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka        if (mIsAutoCorrectionActive != isAutoCorrection) {
8231b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mIsAutoCorrectionActive = isAutoCorrection;
8245e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            final LatinKeyboard keyboard = getLatinKeyboard();
8255e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) {
8265e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection);
827c84bc3460d2fb386a1db2a2c8b135b746fa706cdTadashi G. Takaoka                final LatinKeyboardView keyboardView = getKeyboardView();
8285e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                if (keyboardView != null)
8295e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                    keyboardView.invalidateKey(invalidatedKey);
8305e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            }
83141feaaadb758a8b31d3e436063b4b5faed104d4dsatok        }
83241feaaadb758a8b31d3e436063b4b5faed104d4dsatok    }
8331508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa
8342ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
8352ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (noSettingsKey) {
8362ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // Never shows the Settings key
837cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka            return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
838cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka        }
8392ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa
8402ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (settingsKeyEnabled) {
8412ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SETTINGS;
8422ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else {
8432ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // It should be alright to fall back to the Settings key on 7-inch layouts
8442ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // even when the Settings key is not explicitly enabled.
8452ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
8462ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        }
847cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka    }
848923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
849