KeyboardSwitcher.java revision cee174b8ccb47ccddc8a8a7e88a9c617f9b5e766
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;
23364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaokaimport android.inputmethodservice.InputMethodService;
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;
42364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaokaimport java.util.Arrays;
4312659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.HashMap;
4412659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaokaimport java.util.Locale;
4512659d4c0ce04aaf3d8479e44f9230881b964000Tadashi G. Takaoka
46979f8690967ff5409fe18f5085858ccdb8e0ccf1satokpublic class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
47c25e07d21261583d0eeca5aa0fb6e3ffe93dc603Tadashi G. Takaoka    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
48c25e07d21261583d0eeca5aa0fb6e3ffe93dc603Tadashi G. Takaoka    private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
49f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public static final boolean DEBUG_STATE = false;
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
51a327485e595c9f7676989097c830ff452085d4c9satok    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902";
5205ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka    private static final int[] KEYBOARD_THEMES = {
5313a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme,
5413a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_HighContrast,
5513a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Stone,
5613a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Stone_Bold,
5713a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka        R.style.KeyboardTheme_Gingerbread,
589116bf18f9c83084f9d451e2e709eff32db27d36Tadashi G. Takaoka        R.style.KeyboardTheme_IceCreamSandwich,
5975fde6489039c09056fb5e64d39630ece5ad57cfTadashi G. Takaoka    };
60979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
610ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private SubtypeSwitcher mSubtypeSwitcher;
6227d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    private SharedPreferences mPrefs;
630ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
6486e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka    private View mCurrentInputView;
65c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    private LatinKeyboardView mKeyboardView;
660ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private LatinIME mInputMethodService;
670a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka    private String mPackageName;
680a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka    private Resources mResources;
6931adfa78e2edae188edb05e869f9f68798857582satok
70717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // TODO: Combine these key state objects with auto mode switch state.
71f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
72f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
73889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
7460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mMainKeyboardId;
7560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mSymbolsKeyboardId;
7660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka    private KeyboardId mSymbolsShiftedKeyboardId;
77353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
78353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private KeyboardId mCurrentId;
798b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka    private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
808b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
81364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    // TODO: Remove this cache object when {@link DisplayMetrics} has actual window width excluding
82364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    // system navigation bar.
83364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    private WindowWidthCache mWindowWidthCache;
84466741d8a78965b8509bf527344f289e50873092Mike LeBeau
85055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState();
86055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
871b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
8841feaaadb758a8b31d3e436063b4b5faed104d4dsatok     * what user actually typed. */
891b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    private boolean mIsAutoCorrectionActive;
909e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
91717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
92717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    // and ModifierKeyState.
93717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_ALPHA = 0;
94717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_SYMBOL_BEGIN = 1;
95717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_SYMBOL = 2;
969e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    // The following states are used only on the distinct multi-touch panel devices.
97717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3;
98717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4;
99717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_CHORDING_ALPHA = 5;
100717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
101717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private int mSwitchState = SWITCH_STATE_ALPHA;
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
1035a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private int mThemeIndex = -1;
1045a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private Context mThemeContext;
105979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1060ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
1070ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
108364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    private static class WindowWidthCache {
109364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        private final InputMethodService mService;
110364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        private final Resources mResources;
111364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        private final boolean mIsRegistered[] = new boolean[Configuration.ORIENTATION_SQUARE + 1];
112364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        private final int mWidth[] = new int[Configuration.ORIENTATION_SQUARE + 1];
113364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
114364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        public WindowWidthCache(InputMethodService service) {
115364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            mService = service;
116364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            mResources = service.getResources();
117364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
118364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            Arrays.fill(mIsRegistered, false);
119364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            Arrays.fill(mWidth, 0);
120364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        }
121364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
122364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        private int getCurrentWindowWidth() {
123364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            return mService.getWindow().getWindow().getDecorView().getWidth();
124364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        }
125364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
126364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        public int getWidth(Configuration conf) {
127364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            final int orientation = conf.orientation;
128364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            try {
129364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                final int width = mWidth[orientation];
130364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                if (mIsRegistered[orientation] || width > 0) {
131364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    // Return registered or cached window width for this orientation.
132364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    return width;
133364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                }
134364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                // Fall through
135364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            } catch (IndexOutOfBoundsException e) {
136364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                Log.w(TAG, "unknwon orientation value " + orientation);
137364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                // Fall through
138364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            }
139364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
140364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            // Return screen width as default window width.
141364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            return mResources.getDisplayMetrics().widthPixels;
142364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        }
143364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
144364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        public int getWidthOnSizeChanged(Configuration conf) {
145364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            final int orientation = conf.orientation;
146364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            try {
147364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                if (mIsRegistered[orientation]) {
148364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    // Return registered window width for this orientation.
149364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    return mWidth[orientation];
150364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                }
151364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
152364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                // Cache the current window width without registering.
153364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                final int width = getCurrentWindowWidth();
154364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                mWidth[orientation] = width;
155364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                return width;
156364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            } catch (IndexOutOfBoundsException e) {
157364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                Log.w(TAG, "unknwon orientation value " + orientation);
158364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                return 0;
159364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            }
160364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        }
161364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
162364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        public void registerWidth() {
163364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            final int orientation = mResources.getConfiguration().orientation;
164364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            try {
165364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                if (!mIsRegistered[orientation]) {
166364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    final int width = getCurrentWindowWidth();
167364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    if (width > 0) {
168364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                        // Register current window width.
169364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                        mWidth[orientation] = width;
170364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                        mIsRegistered[orientation] = true;
171364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                    }
172364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                }
173364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            } catch (IndexOutOfBoundsException e) {
174364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka                Log.w(TAG, "unknwon orientation value " + orientation);
175364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka            }
176364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        }
177364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    }
178364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
179055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public class KeyboardLayoutState {
180055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsValid;
181055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsAlphabetMode;
182055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsShiftLocked;
183055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        private boolean mIsShifted;
184055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
185055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public boolean isValid() {
186055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            return mIsValid;
187055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
188055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
189055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public void save() {
19094ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka            if (mCurrentId == null) {
19194ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka                return;
19294ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka            }
193055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsAlphabetMode = isAlphabetMode();
19438b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            if (mIsAlphabetMode) {
19538b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShiftLocked = isShiftLocked();
19638b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked();
19738b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            } else {
19838b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka                mIsShiftLocked = false;
19994ac3d760b98aa148cc9386e0a4b4a20978db85dTadashi G. Takaoka                mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId);
20038b5605feed7260a6dc43ce7bd88d0f95cf43261Tadashi G. Takaoka            }
201055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = true;
202055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
203055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
204055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public KeyboardId getKeyboardId() {
205055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return mMainKeyboardId;
206055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
207055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
208055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mMainKeyboardId;
209055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            } else {
210055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId;
211055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
212055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
213055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
214055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public void restore() {
215055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return;
216055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = false;
217055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
218055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
219055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isAlphabetMode = isAlphabetMode();
220055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShiftLocked = isAlphabetMode && isShiftLocked();
221055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked();
222055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                if (mIsShiftLocked != isShiftLocked) {
223055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    toggleCapsLock();
224055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                } else if (mIsShifted != isShifted) {
225055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onPressShift(false);
226055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onReleaseShift(false);
227055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                }
228055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
229055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
230055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
231055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
2320ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    public static KeyboardSwitcher getInstance() {
2330ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        return sInstance;
2340ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
2350ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
2360ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private KeyboardSwitcher() {
237e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        // Intentional empty constructor for singleton.
2380ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
2390ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
24027d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    public static void init(LatinIME ims, SharedPreferences prefs) {
241364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        sInstance.initInternal(ims, prefs);
242364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    }
243364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
244364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    private void initInternal(LatinIME ims, SharedPreferences prefs) {
245364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mInputMethodService = ims;
246364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPackageName = ims.getPackageName();
247364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mResources = ims.getResources();
248364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPrefs = prefs;
249364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
250364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mWindowWidthCache = new WindowWidthCache(ims);
251364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
252364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        prefs.registerOnSharedPreferenceChangeListener(this);
2535a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
254979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2555a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
2565a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String defaultThemeId = context.getString(R.string.config_default_keyboard_theme_id);
2575a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String themeId = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultThemeId);
25805ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        try {
2595a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            final int themeIndex = Integer.valueOf(themeId);
2605a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            if (themeIndex >= 0 && themeIndex < KEYBOARD_THEMES.length)
2615a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                return themeIndex;
26205ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        } catch (NumberFormatException e) {
2635a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            // Format error, keyboard theme is default to 0.
2645a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        }
2655a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        Log.w(TAG, "Illegal keyboard theme in preference: " + themeId + ", default to 0");
2665a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        return 0;
2675a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
2685a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka
2695a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private void setContextThemeWrapper(Context context, int themeIndex) {
2705a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        if (mThemeIndex != themeIndex) {
2715a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeIndex = themeIndex;
2725a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]);
2735a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mKeyboardCache.clear();
27405ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        }
275979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
276979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2772ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) {
278979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        try {
2792ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
2802ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
2812ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
282055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
283979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } catch (RuntimeException e) {
28460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
28560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
286979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
287353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
288353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
289055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public KeyboardLayoutState getKeyboardState() {
290055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return mSavedKeyboardState;
291055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
292055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
293055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public void onFinishInputView() {
294055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mIsAutoCorrectionActive = false;
295055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
296055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
297c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    public void onHideWindow() {
298c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mIsAutoCorrectionActive = false;
299c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    }
300c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka
301055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public void registerWindowWidth() {
302055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mWindowWidthCache.registerWidth();
303055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
304055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
30538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    @SuppressWarnings("unused")
30638f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    public void onSizeChanged(int w, int h, int oldw, int oldh) {
307c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // TODO: This hack should be removed when display metric returns a proper width.
308c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // Until then, the behavior of KeyboardSwitcher is suboptimal on a device that has a
309c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // vertical system navigation bar in landscape screen orientation, for instance.
310364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final Configuration conf = mResources.getConfiguration();
311364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final int width = mWindowWidthCache.getWidthOnSizeChanged(conf);
31238f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
31387154c656eaa2b9bf1f93e990a61b9f1c7d48babTadashi G. Takaoka        if (width == 0 || mCurrentId == null)
314e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka            return;
31538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        // Reload keyboard with new width.
316364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(conf.orientation, width);
317055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mInputMethodService.mHandler.postRestoreKeyboardLayout();
318e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        setKeyboard(getKeyboard(newId));
319e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
320e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
321055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void setKeyboard(final Keyboard keyboard) {
322c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
323055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardView.setKeyboard(keyboard);
324055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mCurrentId = keyboard.mId;
325055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mSwitchState = getSwitchState(mCurrentId);
326055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        updateShiftLockState(keyboard);
327c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        mKeyboardView.setKeyPreviewPopupEnabled(
3280a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
3290a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
330050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        final boolean localeChanged = (oldKeyboard == null)
331055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
332050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
333286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        updateShiftState();
334353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
335353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
336055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private int getSwitchState(KeyboardId id) {
337055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
338055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
339055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
340055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void updateShiftLockState(Keyboard keyboard) {
341055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
342055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
343055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
344055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // that takes care of the current keyboard having such ALT key or not.
345055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(keyboard.hasShiftLockKey());
346055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
347055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
348055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // indicator, we need to call setShiftLocked(false).
349055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(false);
350055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
351055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
352055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
353353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private LatinKeyboard getKeyboard(KeyboardId id) {
3548b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
355681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
356681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        if (keyboard == null) {
3570a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka            final Locale savedLocale = Utils.setSystemLocale(
3580a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                    mResources, mSubtypeSwitcher.getInputLocale());
3598da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            try {
3608da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
3618da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            } finally {
3628da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                Utils.setSystemLocale(mResources, savedLocale);
3638da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
3648b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
3658da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
3668da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            if (DEBUG_CACHE) {
3678b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
3688b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
3698da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
370e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        } else if (DEBUG_CACHE) {
3718b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
372353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger        }
3738b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
3741b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
37573c02390d31bbf32b78763db66b04004666fb5a3Tadashi G. Takaoka        keyboard.setShiftLocked(false);
3768b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        keyboard.setShifted(false);
377de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // If the cached keyboard had been switched to another keyboard while the language was
378de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
3795cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka        // we should reset the text fade factor. It is also applicable to shortcut key.
380de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        keyboard.setSpacebarTextFadeFactor(0.0f, null);
3814503e2ea9853c1573f60903d8639d82e39e07c56Tadashi G. Takaoka        keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null);
382681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        return keyboard;
383353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
384353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
3852ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
3862ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final boolean isShift, Settings.Values settingsValues) {
3872ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int mode = Utils.getKeyboardMode(editorInfo);
38859c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        final int xmlId;
38960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        switch (mode) {
39060ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_PHONE:
39160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
39260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
39360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_NUMBER:
39460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = R.xml.kbd_number;
39560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
39660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        default:
39760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            if (isSymbols) {
39860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
3992c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            } else {
4002c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_qwerty;
4012c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            }
40260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
403923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
40460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka
405cee174b8ccb47ccddc8a8a7e88a9c617f9b5e766Ken Wakasa        final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
40673e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean noMicrophone = Utils.inPrivateImeOptions(
40773e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
40873e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                || Utils.inPrivateImeOptions(
40973e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                        null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
41073e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
41173e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                && !noMicrophone;
4122ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
4130a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka        final boolean noSettingsKey = Utils.inPrivateImeOptions(
4142ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
4152ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
4162ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
417c3afe2c244c14ed0f8a355fe608b3402e3656330Tadashi G. Takaoka        final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
4182ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final Configuration conf = mResources.getConfiguration();
4190a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka
4209b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        return new KeyboardId(
4212ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                mResources.getResourceEntryName(xmlId), xmlId, mSubtypeSwitcher.getInputLocale(),
4222ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                conf.orientation, mWindowWidthCache.getWidth(conf), mode, editorInfo,
423c3afe2c244c14ed0f8a355fe608b3402e3656330Tadashi G. Takaoka                hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey);
4244199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
4254199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
4263a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public int getKeyboardMode() {
427cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
428923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
4291679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4303a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public boolean isAlphabetMode() {
431571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return mCurrentId != null && mCurrentId.isAlphabetKeyboard();
432923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
433923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4341679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isInputViewShown() {
43586e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        return mCurrentInputView != null && mCurrentInputView.isShown();
4361679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4371679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4381679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isKeyboardAvailable() {
439c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null)
440c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            return mKeyboardView.getKeyboard() != null;
441b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
442b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
443b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
444050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    public LatinKeyboard getLatinKeyboard() {
445c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null) {
446c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            final Keyboard keyboard = mKeyboardView.getKeyboard();
447050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            if (keyboard instanceof LatinKeyboard)
448050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka                return (LatinKeyboard)keyboard;
449050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        }
450b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return null;
4511679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4521679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
453f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
454b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
455b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
456f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isShiftedOrShiftLocked();
457b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
4581679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4591679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4601679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isShiftLocked() {
461b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
462b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
463b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return latinKeyboard.isShiftLocked();
464b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
4651679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4661679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
467f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isAutomaticTemporaryUpperCase() {
468f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
469f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
470f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isAutomaticTemporaryUpperCase();
471f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
472f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
473f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
474f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
475f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
476f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
477f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCase();
478f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
479f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
480f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
481d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    private boolean isManualTemporaryUpperCaseFromAuto() {
482d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
483d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        if (latinKeyboard != null)
484d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCaseFromAuto();
485d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        return false;
486d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    }
487d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka
488f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setManualTemporaryUpperCase(boolean shifted) {
489b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
4900d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka        if (latinKeyboard != null) {
4910d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On non-distinct multi touch panel device, we should also turn off the shift locked
4920d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state when shift key is pressed to go to normal mode.
4930d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On the other hand, on distinct multi touch panel device, turning off the shift locked
4940d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state with shift key pressing is handled by onReleaseShift().
495101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
4960d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka                latinKeyboard.setShiftLocked(false);
4970d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
4980d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            if (latinKeyboard.setShifted(shifted)) {
499c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.invalidateAllKeys();
5000d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
501979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
502979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
503979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
504b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void setShiftLocked(boolean shiftLocked) {
505b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
506b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
507c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            mKeyboardView.invalidateAllKeys();
508b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
509979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
510979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
5110d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
5120d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Toggle keyboard shift state triggered by user touch event.
5130d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
514b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleShift() {
515b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
516f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
517f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleShift:"
518f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
519f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
520b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
521f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            setManualTemporaryUpperCase(!isShiftedOrShiftLocked());
522b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
523b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            toggleShiftInSymbol();
524b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
525b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
526b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
527b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleCapsLock() {
528b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
529f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
530f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleCapsLock:"
531f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
532f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
533b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
534b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            if (isShiftLocked()) {
535f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is long pressed while caps lock state, we will toggle back to normal
536f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // state. And mark as if shift key is released.
537f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setShiftLocked(false);
538f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                mShiftKeyState.onRelease();
539b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            } else {
540b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                setShiftLocked(true);
541b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
542b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
543889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
544889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
545f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setAutomaticTemporaryUpperCase() {
546286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        if (mKeyboardView == null) return;
547286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        final Keyboard keyboard = mKeyboardView.getKeyboard();
548e56e88beffe7afee3b41da4697304e1f4c031787Ken Wakasa        if (keyboard == null) return;
549e56e88beffe7afee3b41da4697304e1f4c031787Ken Wakasa        keyboard.setAutomaticTemporaryUpperCase();
550286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa        mKeyboardView.invalidateAllKeys();
551f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
552f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
5530d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
5540d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Update keyboard shift state triggered by connected EditText status change.
5550d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
556b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void updateShiftState() {
55745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final ShiftKeyState shiftKeyState = mShiftKeyState;
558f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
559f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "updateShiftState:"
560f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()
561f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
562286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " shiftKeyState=" + shiftKeyState
563286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " isAlphabetMode=" + isAlphabetMode()
564286a5bf1c68c8dd06ece9701a904593af2457586Ken Wakasa                    + " isShiftLocked=" + isShiftLocked());
56545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (isAlphabetMode()) {
56645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            if (!isShiftLocked() && !shiftKeyState.isIgnoring()) {
56745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) {
56845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    // Only when shift key is releasing, automatic temporary upper case will be set.
56945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setAutomaticTemporaryUpperCase();
57045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                } else {
57145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setManualTemporaryUpperCase(shiftKeyState.isMomentary());
57245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                }
573f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
57445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        } else {
57545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // In symbol keyboard mode, we should clear shift key state because only alphabet
57645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // keyboard has shift key.
57745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            shiftKeyState.onRelease();
578b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
579889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
580889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
581b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void changeKeyboardMode() {
582f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
583f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "changeKeyboardMode:"
584f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
585f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
586b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        toggleKeyboardMode();
587b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isShiftLocked() && isAlphabetMode())
588b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            setShiftLocked(true);
589b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        updateShiftState();
590889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
591889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
592e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onPressShift(boolean withSliding) {
593b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
594b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
595f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
596f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
597f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onPressShift:"
598f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
599e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
600f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (isAlphabetMode()) {
601f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (isShiftLocked()) {
602f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while caps lock state, we will treat this state as shifted
603f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // caps lock state and mark as if shift key pressed while normal state.
604f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
6056769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                setManualTemporaryUpperCase(true);
606f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isAutomaticTemporaryUpperCase()) {
607f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while automatic temporary upper case, we have to move to
608f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // manual temporary upper case.
6096769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                shiftKeyState.onPress();
610f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setManualTemporaryUpperCase(true);
611f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked()) {
612f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In manual upper case state, we just record shift key has been pressing while
613f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // shifted state.
614f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPressOnShifted();
615f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else {
616f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In base layout, chording or manual temporary upper case mode is started.
617f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
6186769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                toggleShift();
619f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
620b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
621f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            // In symbol mode, just toggle symbol and symbol more keyboard.
622f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            shiftKeyState.onPress();
6236769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka            toggleShift();
624717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
625b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
626b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
627b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
628e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onReleaseShift(boolean withSliding) {
629b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
630b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
631f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
632f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
633f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onReleaseShift:"
634f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
635e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
636b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
637f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (shiftKeyState.isMomentary()) {
638f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // After chording input while normal state.
639f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleShift();
640e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftLocked() && !shiftKeyState.isIgnoring() && !withSliding) {
641f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while caps lock state.
642f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleCapsLock();
643e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // To be able to turn off caps lock by "double tap" on shift key, we should ignore
644e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // the second tap of the "double tap" from now for a while because we just have
645e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // already turned off caps lock above.
646c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.startIgnoringDoubleTap();
647e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()
648e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
649f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while shifted state.
650b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                toggleShift();
651e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()
652e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
653d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // Shift has been pressed without chording while manual temporary upper case
654d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // transited from automatic temporary upper case.
655d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                toggleShift();
656b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
657717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        } else {
658717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // In symbol mode, snap back to the previous keyboard mode if the user chords the shift
659717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // key and another key, then releases the shift key.
660717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) {
661717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
662717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
663b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
664f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        shiftKeyState.onRelease();
665889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
666889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
6676c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onPressSymbol() {
668f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
6699e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onPressSymbol:"
670f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
671f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
672b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        changeKeyboardMode();
6736c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onPress();
674717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
6756c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6766c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6776c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onReleaseSymbol() {
678f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
6799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onReleaseSymbol:"
680f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
681f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
6829e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user chords the mode change key and
683717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // another key, then releases the mode change key.
684717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) {
685b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            changeKeyboardMode();
686717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
6876c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onRelease();
6886c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6896c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6906c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onOtherKeyPressed() {
69145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (DEBUG_STATE)
69245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            Log.d(TAG, "onOtherKeyPressed:"
69345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
69445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState
69545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
696f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        mShiftKeyState.onOtherKeyPressed();
6976c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onOtherKeyPressed();
6986c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6996c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
7005f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    public void onCancelInput() {
7015f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user cancels sliding input.
702717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (getPointerCount() == 1) {
703717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
704717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                changeKeyboardMode();
705717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
706717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
707717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
708717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
7095f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    }
7105f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka
711b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void toggleShiftInSymbol() {
7127e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        if (isAlphabetMode())
7137e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka            return;
7148b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final LatinKeyboard keyboard;
71560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mSymbolsKeyboardId)
71660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
71760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
7187e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        } else {
71960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsKeyboardId);
720923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
721050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        setKeyboard(keyboard);
722923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
723923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
724717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public boolean isInMomentarySwitchState() {
725717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
726717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
7279e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
7289e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
729cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isVibrateAndSoundFeedbackRequired() {
730c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput();
731cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
732cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
7339e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private int getPointerCount() {
734c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount();
7359e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
7369e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
737f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void toggleKeyboardMode() {
73860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mMainKeyboardId)) {
73960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mSymbolsKeyboardId));
740b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        } else {
74160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mMainKeyboardId));
742b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
743b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    }
744b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
745c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
746c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch();
747c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    }
748c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka
749717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isSpaceCharacter(int c) {
750717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
751717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
752717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
753717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isQuoteCharacter(int c) {
754717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // Apostrophe, quotation mark.
7550730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard        if (c == Keyboard.CODE_SINGLE_QUOTE || c == Keyboard.CODE_DOUBLE_QUOTE)
756717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
757717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2018: Left single quotation mark
758717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2019: Right single quotation mark
759717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201a: Single low-9 quotation mark
760717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201b: Single high-reversed-9 quotation mark
761717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201c: Left double quotation mark
762717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201d: Right double quotation mark
763717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201e: Double low-9 quotation mark
764717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201f: Double high-reversed-9 quotation mark
765717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c >= '\u2018' && c <= '\u201f')
766717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
767717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00ab: Left-pointing double angle quotation mark
768717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00bb: Right-pointing double angle quotation mark
769717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c == '\u00ab' || c == '\u00bb')
770717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
771717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return false;
772717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
773717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
774b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    /**
7759e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka     * Updates state machine to figure out when to automatically snap back to the previous mode.
776b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani     */
777717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public void onKey(int code) {
7789e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        if (DEBUG_STATE)
779717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState
7809e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    + " pointers=" + getPointerCount());
781717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        switch (mSwitchState) {
782717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
7839e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Only distinct multi touch devices can be in this state.
7845f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // On non-distinct multi touch devices, mode change key is handled by
7855f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
786717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts
787717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from
788717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link #SWITCH_STATE_MOMENTARY}.
789717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
7909e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Detected only the mode change key has been pressed, and then released.
79160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                if (mCurrentId.equals(mMainKeyboardId)) {
792717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                    mSwitchState = SWITCH_STATE_ALPHA;
79360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                } else {
79460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
7959e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                }
7969e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else if (getPointerCount() == 1) {
7979e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the mode change key
7989e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // and slid to other key, then released the finger.
7995f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // If the user cancels the sliding input, snapping back to the previous keyboard
8005f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // mode is handled by {@link #onCancelInput}.
8019e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                changeKeyboardMode();
8029e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else {
8039e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
8049e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // previous mode in {@link onReleaseSymbol} when the mode change key is released.
805717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_ALPHA;
806717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
807717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            break;
808717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
809717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SHIFT) {
810717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Detected only the shift key has been pressed on symbol layout, and then released.
811717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
812717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (getPointerCount() == 1) {
813717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the shift key on
814717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // symbol mode and slid to other key, then released the finger.
815717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
816717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
817717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else {
818717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
819717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // previous mode in {@link onReleaseShift} when the shift key is released.
820717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_SYMBOL;
8219e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            }
8229e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            break;
823717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL_BEGIN:
824717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (!isSpaceCharacter(code) && code >= 0) {
825717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
826b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
8270b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // Snap back to alpha keyboard mode immediately if user types a quote character.
8280b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            if (isQuoteCharacter(code)) {
8290b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa                changeKeyboardMode();
8300b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            }
831b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
832717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL:
833717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_CHORDING_SYMBOL:
8349e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Snap back to alpha keyboard mode if user types one or more non-space/enter
8350b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // characters followed by a space/enter or a quote character.
836717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
837b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                changeKeyboardMode();
838b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
839b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
840b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
841923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
842979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
843c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public LatinKeyboardView getKeyboardView() {
844c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView;
845979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
846979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
847c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public View onCreateInputView() {
848f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return createInputView(mThemeIndex, true);
849979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
850979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
851f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private View createInputView(final int newThemeIndex, final boolean forceRecreate) {
852f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mCurrentInputView != null && mThemeIndex == newThemeIndex && !forceRecreate)
853f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            return mCurrentInputView;
854f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
855f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mKeyboardView != null) {
856f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            mKeyboardView.closing();
857f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        }
858f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
8595a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final int oldThemeIndex = mThemeIndex;
860f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
861f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        boolean tryGC = true;
862f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
863f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            try {
8645a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                setContextThemeWrapper(mInputMethodService, newThemeIndex);
8655a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
86613a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka                        R.layout.input_view, null);
867f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                tryGC = false;
868f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
869f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
8705a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
8715a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
872f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (InflateException e) {
873f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
8745a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
8755a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
876979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
877979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
878f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
879070760dc5abdb9a850a94c509eeb9f486515af59Tadashi G. Takaoka        mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
8805a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboardView.setKeyboardActionListener(mInputMethodService);
8815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
8825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // This always needs to be set since the accessibility state can
8835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // potentially change without the input view being re-created.
8845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        AccessibleKeyboardViewProxy.setView(mKeyboardView);
8855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
886f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return mCurrentInputView;
8878d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    }
8888d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka
889f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private void postSetInputView(final View newInputView) {
890979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mInputMethodService.mHandler.post(new Runnable() {
8918d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            @Override
892979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            public void run() {
893f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                if (newInputView != null) {
894f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                    mInputMethodService.setInputView(newInputView);
895979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
896979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mInputMethodService.updateInputViewShown();
8978d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            }
8988d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        });
899979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
900979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
9018d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    @Override
902979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
903979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
9042ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
9052ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            postSetInputView(createInputView(themeIndex, false));
9062ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) {
907f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            postSetInputView(createInputView(mThemeIndex, true));
908979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
909979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
910979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
9111b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
9125e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka        if (mIsAutoCorrectionActive != isAutoCorrection) {
9131b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mIsAutoCorrectionActive = isAutoCorrection;
9145e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            final LatinKeyboard keyboard = getLatinKeyboard();
9155e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) {
9165e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection);
9175e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                final LatinKeyboardView keyboardView = getKeyboardView();
9185e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                if (keyboardView != null)
9195e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                    keyboardView.invalidateKey(invalidatedKey);
9205e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            }
92141feaaadb758a8b31d3e436063b4b5faed104d4dsatok        }
92241feaaadb758a8b31d3e436063b4b5faed104d4dsatok    }
9231508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa
9242ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
9252ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (noSettingsKey) {
9262ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // Never shows the Settings key
927cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka            return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
928cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka        }
9292ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa
9302ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (settingsKeyEnabled) {
9312ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SETTINGS;
9322ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else {
9332ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // It should be alright to fall back to the Settings key on 7-inch layouts
9342ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // even when the Settings key is not explicitly enabled.
9352ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
9362ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        }
937cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka    }
938923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
939