KeyboardSwitcher.java revision 055054eef3ccd32c6dfd69a3f76bfb7383ea93bb
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() {
190055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsAlphabetMode = isAlphabetMode();
191055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsShiftLocked = mIsAlphabetMode && isShiftLocked();
192055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked();
193055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = true;
194055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
195055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
196055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public KeyboardId getKeyboardId() {
197055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return mMainKeyboardId;
198055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
199055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
200055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mMainKeyboardId;
201055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            } else {
202055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId;
203055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
204055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
205055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
206055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        public void restore() {
207055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (!mIsValid) return;
208055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            mIsValid = false;
209055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
210055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            if (mIsAlphabetMode) {
211055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isAlphabetMode = isAlphabetMode();
212055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShiftLocked = isAlphabetMode && isShiftLocked();
213055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                final boolean isShifted = !isShiftLocked && isShiftedOrShiftLocked();
214055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                if (mIsShiftLocked != isShiftLocked) {
215055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    toggleCapsLock();
216055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                } else if (mIsShifted != isShifted) {
217055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onPressShift(false);
218055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                    onReleaseShift(false);
219055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                }
220055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            }
221055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
222055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
223055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
2240ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    public static KeyboardSwitcher getInstance() {
2250ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok        return sInstance;
2260ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
2270ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
2280ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    private KeyboardSwitcher() {
229e26ef1bccddc942fdaeada3409c8e8ff18a35008Tadashi G. Takaoka        // Intentional empty constructor for singleton.
2300ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok    }
2310ed7191b4d1013a0b9d2fd1f26733dee7364871dsatok
23227d13713bbb291d25a910f97d88a81fdbabddc0eKen Wakasa    public static void init(LatinIME ims, SharedPreferences prefs) {
233364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        sInstance.initInternal(ims, prefs);
234364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    }
235364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka
236364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka    private void initInternal(LatinIME ims, SharedPreferences prefs) {
237364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mInputMethodService = ims;
238364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPackageName = ims.getPackageName();
239364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mResources = ims.getResources();
240364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mPrefs = prefs;
241364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
242364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        mWindowWidthCache = new WindowWidthCache(ims);
243364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
244364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        prefs.registerOnSharedPreferenceChangeListener(this);
2455a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
246979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2475a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private static int getKeyboardThemeIndex(Context context, SharedPreferences prefs) {
2485a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String defaultThemeId = context.getString(R.string.config_default_keyboard_theme_id);
2495a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final String themeId = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultThemeId);
25005ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        try {
2515a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            final int themeIndex = Integer.valueOf(themeId);
2525a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            if (themeIndex >= 0 && themeIndex < KEYBOARD_THEMES.length)
2535a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                return themeIndex;
25405ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        } catch (NumberFormatException e) {
2555a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            // Format error, keyboard theme is default to 0.
2565a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        }
2575a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        Log.w(TAG, "Illegal keyboard theme in preference: " + themeId + ", default to 0");
2585a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        return 0;
2595a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    }
2605a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka
2615a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka    private void setContextThemeWrapper(Context context, int themeIndex) {
2625a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        if (mThemeIndex != themeIndex) {
2635a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeIndex = themeIndex;
2645a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mThemeContext = new ContextThemeWrapper(context, KEYBOARD_THEMES[themeIndex]);
2655a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka            mKeyboardCache.clear();
26605ddb9a5d2b36f519a4de4b50448e258f40d2c0fTadashi G. Takaoka        }
267979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
268979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
2692ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    public void loadKeyboard(EditorInfo editorInfo, Settings.Values settingsValues) {
270979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        try {
2712ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
2722ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
2732ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
274055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId()));
275055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            updateShiftState();
276979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        } catch (RuntimeException e) {
27760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
27860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
279979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
280353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
281353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
282055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public KeyboardLayoutState getKeyboardState() {
283055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return mSavedKeyboardState;
284055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
285055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
286055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public void onFinishInputView() {
287055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mIsAutoCorrectionActive = false;
288055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
289055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
290c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    public void onHideWindow() {
291c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        mIsAutoCorrectionActive = false;
292c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka    }
293c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka
294055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    public void registerWindowWidth() {
295055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mWindowWidthCache.registerWidth();
296055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
297055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
29838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    @SuppressWarnings("unused")
29938f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka    public void onSizeChanged(int w, int h, int oldw, int oldh) {
300c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // TODO: This hack should be removed when display metric returns a proper width.
301c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // Until then, the behavior of KeyboardSwitcher is suboptimal on a device that has a
302c3d175c01ff1956ddb1c2d608d69af1793b4ad8aTadashi G. Takaoka        // vertical system navigation bar in landscape screen orientation, for instance.
303364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final Configuration conf = mResources.getConfiguration();
304364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final int width = mWindowWidthCache.getWidthOnSizeChanged(conf);
30538f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
30687154c656eaa2b9bf1f93e990a61b9f1c7d48babTadashi G. Takaoka        if (width == 0 || mCurrentId == null)
307e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka            return;
30838f55b36c3992a580cf7e20668b1eb72a4eb2431Tadashi G. Takaoka        // Reload keyboard with new width.
309364f1fd1f84cca6c8d396064bfd8bc8fd1efefcbTadashi G. Takaoka        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(conf.orientation, width);
310055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mInputMethodService.mHandler.postRestoreKeyboardLayout();
311e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        setKeyboard(getKeyboard(newId));
312e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka    }
313e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka
314055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void setKeyboard(final Keyboard keyboard) {
315c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
316055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mKeyboardView.setKeyboard(keyboard);
317055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mCurrentId = keyboard.mId;
318055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        mSwitchState = getSwitchState(mCurrentId);
319055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        updateShiftLockState(keyboard);
320c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        mKeyboardView.setKeyPreviewPopupEnabled(
3210a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
3220a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                Settings.Values.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
323050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        final boolean localeChanged = (oldKeyboard == null)
324055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
325050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        mInputMethodService.mHandler.startDisplayLanguageOnSpacebar(localeChanged);
326353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
327353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
328055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private int getSwitchState(KeyboardId id) {
329055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        return id.equals(mMainKeyboardId) ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
330055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
331055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
332055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    private void updateShiftLockState(Keyboard keyboard) {
333055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
334055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
335055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
336055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // that takes care of the current keyboard having such ALT key or not.
337055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(keyboard.hasShiftLockKey());
338055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
339055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
340055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            // indicator, we need to call setShiftLocked(false).
341055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka            keyboard.setShiftLocked(false);
342055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka        }
343055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka    }
344055054eef3ccd32c6dfd69a3f76bfb7383ea93bbTadashi G. Takaoka
345353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    private LatinKeyboard getKeyboard(KeyboardId id) {
3468b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
347681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        LatinKeyboard keyboard = (ref == null) ? null : ref.get();
348681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        if (keyboard == null) {
3490a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka            final Locale savedLocale = Utils.setSystemLocale(
3500a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka                    mResources, mSubtypeSwitcher.getInputLocale());
3518da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            try {
3528da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build();
3538da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            } finally {
3548da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka                Utils.setSystemLocale(mResources, savedLocale);
3558da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
3568b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            mKeyboardCache.put(id, new SoftReference<LatinKeyboard>(keyboard));
3578da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka
3588da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            if (DEBUG_CACHE) {
3598b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": "
3608b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka                        + ((ref == null) ? "LOAD" : "GCed") + " id=" + id);
3618da9a13760896cd78235b60d0ea680ea13620532Tadashi G. Takaoka            }
362e896d31bb92146379c8b7c0050ee05eec0830317Tadashi G. Takaoka        } else if (DEBUG_CACHE) {
3638b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka            Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT  id=" + id);
364353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger        }
3658b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka
3661b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka        keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive);
3678b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        keyboard.setShifted(false);
368de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // If the cached keyboard had been switched to another keyboard while the language was
369de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        // displayed on its spacebar, it might have had arbitrary text fade factor. In such case,
3705cd87e1b1c4258e8d016518914eccfbb4437caceTadashi G. Takaoka        // we should reset the text fade factor. It is also applicable to shortcut key.
371de0c8874a4eb1250e8439d9e4e1badca88316670Tadashi G. Takaoka        keyboard.setSpacebarTextFadeFactor(0.0f, null);
3724503e2ea9853c1573f60903d8639d82e39e07c56Tadashi G. Takaoka        keyboard.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady(), null);
373681b676b0aecb30e644f25550018ce2b6cea3e15Tadashi G. Takaoka        return keyboard;
374353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger    }
375353da6d207bed3d32ec38b9ce52e4a136adbf060Brandon Ballinger
3762ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
3772ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final boolean isShift, Settings.Values settingsValues) {
3782ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int mode = Utils.getKeyboardMode(editorInfo);
37959c9930ca98f68aeb6b0b9dc19e29666b8fb152aTadashi G. Takaoka        final int xmlId;
38060ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        switch (mode) {
38160ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_PHONE:
38260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
38360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
38460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        case KeyboardId.MODE_NUMBER:
38560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            xmlId = R.xml.kbd_number;
38660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
38760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        default:
38860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            if (isSymbols) {
38960ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
3902c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            } else {
3912c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka                xmlId = R.xml.kbd_qwerty;
3922c60d6e28ecf7d6f7e97a504ebfa97e286d931edTadashi G. Takaoka            }
39360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            break;
394923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
39560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka
3962ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(editorInfo);
39773e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean noMicrophone = Utils.inPrivateImeOptions(
39873e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
39973e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                || Utils.inPrivateImeOptions(
40073e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                        null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
40173e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka        final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
40273e716f20284f929b2c0323c9e4759efe558de9dTadashi G. Takaoka                && !noMicrophone;
4032ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
4040a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka        final boolean noSettingsKey = Utils.inPrivateImeOptions(
4052ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
4062ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
4072ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
408c3afe2c244c14ed0f8a355fe608b3402e3656330Tadashi G. Takaoka        final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
4092ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        final Configuration conf = mResources.getConfiguration();
4100a054dd1075d679385434ecef38a3d88c7551d7aTadashi G. Takaoka
4119b6d1d52d91f8f18952ae3841f4bb0d7309bfc0eTadashi G. Takaoka        return new KeyboardId(
4122ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                mResources.getResourceEntryName(xmlId), xmlId, mSubtypeSwitcher.getInputLocale(),
4132ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa                conf.orientation, mWindowWidthCache.getWidth(conf), mode, editorInfo,
414c3afe2c244c14ed0f8a355fe608b3402e3656330Tadashi G. Takaoka                hasSettingsKey, f2KeyMode, noSettingsKey, voiceKeyEnabled, hasShortcutKey);
4154199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka    }
4164199e29a7d796191d3e13ef07e6e80e91834fe7aTadashi G. Takaoka
4173a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public int getKeyboardMode() {
418cb97c2f1407364b24dc1a54226481a55501d1533Tadashi G. Takaoka        return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
419923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
4201679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4213a2896c80475094f751ef447fc9c97028bfc2265Tadashi G. Takaoka    public boolean isAlphabetMode() {
422571bdb401f670b92bd7710a12a990cb65a99b7d3Tadashi G. Takaoka        return mCurrentId != null && mCurrentId.isAlphabetKeyboard();
423923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
424923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
4251679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isInputViewShown() {
42686e815a142c8aa13213151e381a8a24ef23073d3Tadashi G. Takaoka        return mCurrentInputView != null && mCurrentInputView.isShown();
4271679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4281679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4291679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isKeyboardAvailable() {
430c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null)
431c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            return mKeyboardView.getKeyboard() != null;
432b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
433b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
434b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
435050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka    public LatinKeyboard getLatinKeyboard() {
436c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        if (mKeyboardView != null) {
437c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            final Keyboard keyboard = mKeyboardView.getKeyboard();
438050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka            if (keyboard instanceof LatinKeyboard)
439050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka                return (LatinKeyboard)keyboard;
440050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        }
441b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return null;
4421679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4431679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
444f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isShiftedOrShiftLocked() {
445b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
446b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
447f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isShiftedOrShiftLocked();
448b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
4491679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4501679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
4511679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    public boolean isShiftLocked() {
452b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
453b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null)
454b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return latinKeyboard.isShiftLocked();
455b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        return false;
4561679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka    }
4571679432d1c8a8cfe6ff09be0a49792f4af91ab35Tadashi G. Takaoka
458f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isAutomaticTemporaryUpperCase() {
459f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
460f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
461f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isAutomaticTemporaryUpperCase();
462f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
463f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
464f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
465f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    public boolean isManualTemporaryUpperCase() {
466f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
467f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null)
468f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCase();
469f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        return false;
470f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
471f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
472d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    private boolean isManualTemporaryUpperCaseFromAuto() {
473d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
474d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        if (latinKeyboard != null)
475d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka            return latinKeyboard.isManualTemporaryUpperCaseFromAuto();
476d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka        return false;
477d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka    }
478d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka
479f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setManualTemporaryUpperCase(boolean shifted) {
480b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
4810d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka        if (latinKeyboard != null) {
4820d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On non-distinct multi touch panel device, we should also turn off the shift locked
4830d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state when shift key is pressed to go to normal mode.
4840d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // On the other hand, on distinct multi touch panel device, turning off the shift locked
4850d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            // state with shift key pressing is handled by onReleaseShift().
486101a00e3d4b1c29ef2ecdecd1b72b43efde7791aTadashi G. Takaoka            if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) {
4870d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka                latinKeyboard.setShiftLocked(false);
4880d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
4890d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            if (latinKeyboard.setShifted(shifted)) {
490c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.invalidateAllKeys();
4910d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka            }
492979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
493979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
494979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
495b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void setShiftLocked(boolean shiftLocked) {
496b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
497b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) {
498c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            mKeyboardView.invalidateAllKeys();
499b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
500979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
501979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
5020d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
5030d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Toggle keyboard shift state triggered by user touch event.
5040d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
505b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleShift() {
506b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
507f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
508f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleShift:"
509f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
510f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
511b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
512f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            setManualTemporaryUpperCase(!isShiftedOrShiftLocked());
513b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
514b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            toggleShiftInSymbol();
515b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
516b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
517b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
518b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void toggleCapsLock() {
519b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        mInputMethodService.mHandler.cancelUpdateShiftState();
520f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
521f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "toggleCapsLock:"
522f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
523f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
524b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
525b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            if (isShiftLocked()) {
526f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is long pressed while caps lock state, we will toggle back to normal
527f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // state. And mark as if shift key is released.
528f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setShiftLocked(false);
529f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                mShiftKeyState.onRelease();
530b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            } else {
531b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                setShiftLocked(true);
532b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
533b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
534889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
535889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
536f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void setAutomaticTemporaryUpperCase() {
537f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        LatinKeyboard latinKeyboard = getLatinKeyboard();
538f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (latinKeyboard != null) {
539f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            latinKeyboard.setAutomaticTemporaryUpperCase();
540c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka            mKeyboardView.invalidateAllKeys();
541f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        }
542f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    }
543f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka
5440d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka    /**
5450d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     * Update keyboard shift state triggered by connected EditText status change.
5460d0a46da0393c6dd73cccf5e22cb0dd70c99e15aTadashi G. Takaoka     */
547b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void updateShiftState() {
54845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        final ShiftKeyState shiftKeyState = mShiftKeyState;
549f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
550f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "updateShiftState:"
551f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()
552f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
55345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState);
55445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (isAlphabetMode()) {
55545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            if (!isShiftLocked() && !shiftKeyState.isIgnoring()) {
55645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                if (shiftKeyState.isReleasing() && mInputMethodService.getCurrentAutoCapsState()) {
55745911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    // Only when shift key is releasing, automatic temporary upper case will be set.
55845911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setAutomaticTemporaryUpperCase();
55945911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                } else {
56045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    setManualTemporaryUpperCase(shiftKeyState.isMomentary());
56145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                }
562f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
56345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        } else {
56445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // In symbol keyboard mode, we should clear shift key state because only alphabet
56545911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            // keyboard has shift key.
56645911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            shiftKeyState.onRelease();
567b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
568889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
569889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
570b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    public void changeKeyboardMode() {
571f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
572f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "changeKeyboardMode:"
573f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
574f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState);
575b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        toggleKeyboardMode();
576b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isShiftLocked() && isAlphabetMode())
577b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            setShiftLocked(true);
578b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        updateShiftState();
579889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
580889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
581e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onPressShift(boolean withSliding) {
582b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
583b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
584f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
585f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
586f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onPressShift:"
587f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
588e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
589f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (isAlphabetMode()) {
590f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (isShiftLocked()) {
591f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while caps lock state, we will treat this state as shifted
592f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // caps lock state and mark as if shift key pressed while normal state.
593f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
5946769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                setManualTemporaryUpperCase(true);
595f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isAutomaticTemporaryUpperCase()) {
596f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift key is pressed while automatic temporary upper case, we have to move to
597f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // manual temporary upper case.
5986769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                shiftKeyState.onPress();
599f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                setManualTemporaryUpperCase(true);
600f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked()) {
601f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In manual upper case state, we just record shift key has been pressing while
602f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // shifted state.
603f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPressOnShifted();
604f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            } else {
605f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // In base layout, chording or manual temporary upper case mode is started.
606f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                shiftKeyState.onPress();
6076769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka                toggleShift();
608f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            }
609b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        } else {
610f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            // In symbol mode, just toggle symbol and symbol more keyboard.
611f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            shiftKeyState.onPress();
6126769c67987f323008647f5d029c02f8cc95272eeTadashi G. Takaoka            toggleShift();
613717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
614b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
615b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    }
616b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka
617e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka    public void onReleaseShift(boolean withSliding) {
618b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (!isKeyboardAvailable())
619b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            return;
620f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        ShiftKeyState shiftKeyState = mShiftKeyState;
621f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
622f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            Log.d(TAG, "onReleaseShift:"
623f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
624e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    + " shiftKeyState=" + shiftKeyState + " sliding=" + withSliding);
625b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        if (isAlphabetMode()) {
626f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka            if (shiftKeyState.isMomentary()) {
627f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // After chording input while normal state.
628f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleShift();
629e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftLocked() && !shiftKeyState.isIgnoring() && !withSliding) {
630f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while caps lock state.
631f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                toggleCapsLock();
632e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // To be able to turn off caps lock by "double tap" on shift key, we should ignore
633e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // the second tap of the "double tap" from now for a while because we just have
634e541f03286189eebbc4a75615070e0e6f43ec37cTadashi G. Takaoka                // already turned off caps lock above.
635c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka                mKeyboardView.startIgnoringDoubleTap();
636e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isShiftedOrShiftLocked() && shiftKeyState.isPressingOnShifted()
637e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
638f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                // Shift has been pressed without chording while shifted state.
639b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                toggleShift();
640e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka            } else if (isManualTemporaryUpperCaseFromAuto() && shiftKeyState.isPressing()
641e59491460b0411bed430a5ca6eca0c56c5bf18d9Tadashi G. Takaoka                    && !withSliding) {
642d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // Shift has been pressed without chording while manual temporary upper case
643d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                // transited from automatic temporary upper case.
644d01ae897d38d4e788e4f089e2b1d6d74655847c6Tadashi G. Takaoka                toggleShift();
645b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
646717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        } else {
647717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // In symbol mode, snap back to the previous keyboard mode if the user chords the shift
648717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // key and another key, then releases the shift key.
649717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) {
650717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
651717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
652b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        }
653f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        shiftKeyState.onRelease();
654889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka    }
655889691eca1ad991a85fb721deb37ecba6a913762Tadashi G. Takaoka
6566c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onPressSymbol() {
657f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
6589e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onPressSymbol:"
659f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
660f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
661b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka        changeKeyboardMode();
6626c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onPress();
663717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
6646c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6656c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6666c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onReleaseSymbol() {
667f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        if (DEBUG_STATE)
6689e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            Log.d(TAG, "onReleaseSymbol:"
669f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
670f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
6719e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user chords the mode change key and
672717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // another key, then releases the mode change key.
673717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) {
674b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            changeKeyboardMode();
675717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
6766c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onRelease();
6776c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6786c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6796c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    public void onOtherKeyPressed() {
68045911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka        if (DEBUG_STATE)
68145911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka            Log.d(TAG, "onOtherKeyPressed:"
68245911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
68345911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " shiftKeyState=" + mShiftKeyState
68445911256fd4e32880e33c42259b19e8d7af70cb1Tadashi G. Takaoka                    + " symbolKeyState=" + mSymbolKeyState);
685f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka        mShiftKeyState.onOtherKeyPressed();
6866c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka        mSymbolKeyState.onOtherKeyPressed();
6876c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka    }
6886c92ee127df50509d364a2b6d428b223d2cbaa6eTadashi G. Takaoka
6895f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    public void onCancelInput() {
6905f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka        // Snap back to the previous keyboard mode if the user cancels sliding input.
691717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (getPointerCount() == 1) {
692717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
693717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                changeKeyboardMode();
694717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
695717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
696717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
697717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        }
6985f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka    }
6995f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka
700b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka    private void toggleShiftInSymbol() {
7017e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        if (isAlphabetMode())
7027e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka            return;
7038b00bc4f3281c229ee830f34a78c0ec287902c3fTadashi G. Takaoka        final LatinKeyboard keyboard;
70460ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mSymbolsKeyboardId)
70560ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
70660ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
7077e1f5a2d5a96c74691b3b09fa986efb7161e5a12Tadashi G. Takaoka        } else {
70860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            keyboard = getKeyboard(mSymbolsKeyboardId);
709923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
710050c0462dc2ada5a5afecec5b6745693c5066b85Tadashi G. Takaoka        setKeyboard(keyboard);
711923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
712923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
713717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public boolean isInMomentarySwitchState() {
714717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
715717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
7169e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
7179e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
718cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    public boolean isVibrateAndSoundFeedbackRequired() {
719c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null || !mKeyboardView.isInSlidingKeyInput();
720cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka    }
721cb2469ae17e0ca8a94767008fef3945cb2a3b406Tadashi G. Takaoka
7229e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    private int getPointerCount() {
723c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView == null ? 0 : mKeyboardView.getPointerCount();
7249e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka    }
7259e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka
726f27364600c742509b48857e6b8f17312033e0dc7Tadashi G. Takaoka    private void toggleKeyboardMode() {
72760ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka        if (mCurrentId.equals(mMainKeyboardId)) {
72860ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mSymbolsKeyboardId));
729b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        } else {
73060ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka            setKeyboard(getKeyboard(mMainKeyboardId));
731b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
732b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    }
733b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani
734c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    public boolean hasDistinctMultitouch() {
735c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch();
736c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka    }
737c8b9afe0378e3f33c3f83271bd1df9678a70c2a2Tadashi G. Takaoka
738717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isSpaceCharacter(int c) {
739717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER;
740717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
741717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
742717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    private static boolean isQuoteCharacter(int c) {
743717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // Apostrophe, quotation mark.
7440730bbfbf5e37bbcb5c287aeff71b304c833a36eJean Chalard        if (c == Keyboard.CODE_SINGLE_QUOTE || c == Keyboard.CODE_DOUBLE_QUOTE)
745717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
746717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2018: Left single quotation mark
747717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u2019: Right single quotation mark
748717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201a: Single low-9 quotation mark
749717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201b: Single high-reversed-9 quotation mark
750717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201c: Left double quotation mark
751717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201d: Right double quotation mark
752717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201e: Double low-9 quotation mark
753717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u201f: Double high-reversed-9 quotation mark
754717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c >= '\u2018' && c <= '\u201f')
755717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
756717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00ab: Left-pointing double angle quotation mark
757717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        // \u00bb: Right-pointing double angle quotation mark
758717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        if (c == '\u00ab' || c == '\u00bb')
759717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            return true;
760717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        return false;
761717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    }
762717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka
763b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani    /**
7649e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka     * Updates state machine to figure out when to automatically snap back to the previous mode.
765b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani     */
766717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka    public void onKey(int code) {
7679e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka        if (DEBUG_STATE)
768717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState
7699e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                    + " pointers=" + getPointerCount());
770717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        switch (mSwitchState) {
771717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
7729e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Only distinct multi touch devices can be in this state.
7735f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // On non-distinct multi touch devices, mode change key is handled by
7745f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka            // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and
775717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts
776717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from
777717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            // {@link #SWITCH_STATE_MOMENTARY}.
778717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
7799e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Detected only the mode change key has been pressed, and then released.
78060ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                if (mCurrentId.equals(mMainKeyboardId)) {
781717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                    mSwitchState = SWITCH_STATE_ALPHA;
78260ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                } else {
78360ccbe16eea5ce9a874835850f257b3c88295022Tadashi G. Takaoka                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
7849e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                }
7859e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else if (getPointerCount() == 1) {
7869e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the mode change key
7879e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // and slid to other key, then released the finger.
7885f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // If the user cancels the sliding input, snapping back to the previous keyboard
7895f922caff80d5067c5af2bbbae2731ef25c9572aTadashi G. Takaoka                // mode is handled by {@link #onCancelInput}.
7909e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                changeKeyboardMode();
7919e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            } else {
7929e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
7939e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka                // previous mode in {@link onReleaseSymbol} when the mode change key is released.
794717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_ALPHA;
795717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            }
796717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            break;
797717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
798717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (code == Keyboard.CODE_SHIFT) {
799717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Detected only the shift key has been pressed on symbol layout, and then released.
800717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
801717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else if (getPointerCount() == 1) {
802717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Snap back to the previous keyboard mode if the user pressed the shift key on
803717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // symbol mode and slid to other key, then released the finger.
804717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                toggleShift();
805717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
806717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            } else {
807717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // Chording input is being started. The keyboard mode will be snapped back to the
808717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                // previous mode in {@link onReleaseShift} when the shift key is released.
809717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_CHORDING_SYMBOL;
8109e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            }
8119e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            break;
812717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL_BEGIN:
813717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (!isSpaceCharacter(code) && code >= 0) {
814717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka                mSwitchState = SWITCH_STATE_SYMBOL;
815b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
8160b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // Snap back to alpha keyboard mode immediately if user types a quote character.
8170b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            if (isQuoteCharacter(code)) {
8180b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa                changeKeyboardMode();
8190b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            }
820b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
821717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_SYMBOL:
822717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka        case SWITCH_STATE_CHORDING_SYMBOL:
8239e91472285a1b903631f1e3c998f5aa1efd3e98eTadashi G. Takaoka            // Snap back to alpha keyboard mode if user types one or more non-space/enter
8240b4757604de15bfe66c10e9cdf01da6d437c6082Ken Wakasa            // characters followed by a space/enter or a quote character.
825717cef79ead5d63a01d09b47caab0a3d719c69dfTadashi G. Takaoka            if (isSpaceCharacter(code) || isQuoteCharacter(code)) {
826b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka                changeKeyboardMode();
827b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            }
828b643dab73ab9527cc63d896cad81c0cdc92fe5f6Tadashi G. Takaoka            break;
829b19668cfc17ad30afcc3c8c0407d47238ce1a90dAmith Yamasani        }
830923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
831979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
832c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public LatinKeyboardView getKeyboardView() {
833c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka        return mKeyboardView;
834979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
835979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
836c150acc7c85ff2f5eeb5bd2c6ff288df4e46a355Tadashi G. Takaoka    public View onCreateInputView() {
837f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return createInputView(mThemeIndex, true);
838979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
839979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
840f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private View createInputView(final int newThemeIndex, final boolean forceRecreate) {
841f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mCurrentInputView != null && mThemeIndex == newThemeIndex && !forceRecreate)
842f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            return mCurrentInputView;
843f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
844f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        if (mKeyboardView != null) {
845f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            mKeyboardView.closing();
846f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        }
847f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
8485a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka        final int oldThemeIndex = mThemeIndex;
849f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        Utils.GCUtils.getInstance().reset();
850f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        boolean tryGC = true;
851f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
852f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            try {
8535a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                setContextThemeWrapper(mInputMethodService, newThemeIndex);
8545a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                mCurrentInputView = LayoutInflater.from(mThemeContext).inflate(
85513a741999480343ccebd81ff6349b572bde17b07Tadashi G. Takaoka                        R.layout.input_view, null);
856f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                tryGC = false;
857f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (OutOfMemoryError e) {
858f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
8595a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
8605a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
861f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            } catch (InflateException e) {
862f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                Log.w(TAG, "load keyboard failed: " + e);
8635a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                tryGC = Utils.GCUtils.getInstance().tryGCOrWait(
8645a2d0630474d6df639e97ff48f5f48b220ca4ee9Tadashi G. Takaoka                        oldThemeIndex + "," + newThemeIndex, e);
865979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
866979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
867f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka
868070760dc5abdb9a850a94c509eeb9f486515af59Tadashi G. Takaoka        mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
8695a7a696aff6718d4e0250c394a9d01cbf2a16916Tadashi G. Takaoka        mKeyboardView.setKeyboardActionListener(mInputMethodService);
8705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
8715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // This always needs to be set since the accessibility state can
8725ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        // potentially change without the input view being re-created.
8735ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette        AccessibleKeyboardViewProxy.setView(mKeyboardView);
8745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette
875f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka        return mCurrentInputView;
8768d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    }
8778d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka
878f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka    private void postSetInputView(final View newInputView) {
879979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        mInputMethodService.mHandler.post(new Runnable() {
8808d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            @Override
881979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            public void run() {
882f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                if (newInputView != null) {
883f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka                    mInputMethodService.setInputView(newInputView);
884979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                }
885979f8690967ff5409fe18f5085858ccdb8e0ccf1satok                mInputMethodService.updateInputViewShown();
8868d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka            }
8878d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka        });
888979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
889979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
8908d7ecc70a6572c288064e41235e4ae8ad5b1b47eTadashi G. Takaoka    @Override
891979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
892979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
8932ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            final int themeIndex = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
8942ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            postSetInputView(createInputView(themeIndex, false));
8952ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else if (Settings.PREF_SHOW_SETTINGS_KEY.equals(key)) {
896f3cd2bb33d1688fd6de01787140a1ef2de4b7723Tadashi G. Takaoka            postSetInputView(createInputView(mThemeIndex, true));
897979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
898979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
899979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
9001b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka    public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
9015e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka        if (mIsAutoCorrectionActive != isAutoCorrection) {
9021b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka            mIsAutoCorrectionActive = isAutoCorrection;
9035e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            final LatinKeyboard keyboard = getLatinKeyboard();
9045e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            if (keyboard != null && keyboard.needsAutoCorrectionSpacebarLed()) {
9055e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                final Key invalidatedKey = keyboard.onAutoCorrectionStateChanged(isAutoCorrection);
9065e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                final LatinKeyboardView keyboardView = getKeyboardView();
9075e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                if (keyboardView != null)
9085e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka                    keyboardView.invalidateKey(invalidatedKey);
9095e4e05afd2825bf7acb594fef5e7bd21c751dd63Tadashi G. Takaoka            }
91041feaaadb758a8b31d3e436063b4b5faed104d4dsatok        }
91141feaaadb758a8b31d3e436063b4b5faed104d4dsatok    }
9121508c0e84f0cd93ab6f5d46fea5026e833f299bcKen Wakasa
9132ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa    private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
9142ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (noSettingsKey) {
9152ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // Never shows the Settings key
916cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka            return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
917cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka        }
9182ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa
9192ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        if (settingsKeyEnabled) {
9202ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SETTINGS;
9212ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        } else {
9222ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // It should be alright to fall back to the Settings key on 7-inch layouts
9232ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            // even when the Settings key is not explicitly enabled.
9242ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa            return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
9252ba975afb9529a6574148596db190b939fbc3b3fKen Wakasa        }
926cd96a691b14b384face577d907f6c1aa33b47233Tadashi G. Takaoka    }
927923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
928