KeyboardSwitcher.java revision 1f4612fc5434e5b223d6f6c1ef77fb5fae0d32ee
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.inputmethod.keyboard;
18
19import android.content.Context;
20import android.content.SharedPreferences;
21import android.content.res.Resources;
22import android.preference.PreferenceManager;
23import android.util.Log;
24import android.view.ContextThemeWrapper;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.inputmethod.EditorInfo;
28
29import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
30import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
31import com.android.inputmethod.keyboard.internal.KeyboardState;
32import com.android.inputmethod.latin.InputView;
33import com.android.inputmethod.latin.LatinIME;
34import com.android.inputmethod.latin.LatinImeLogger;
35import com.android.inputmethod.latin.R;
36import com.android.inputmethod.latin.RichInputMethodManager;
37import com.android.inputmethod.latin.SubtypeSwitcher;
38import com.android.inputmethod.latin.WordComposer;
39import com.android.inputmethod.latin.settings.Settings;
40import com.android.inputmethod.latin.settings.SettingsValues;
41import com.android.inputmethod.latin.utils.ResourceUtils;
42
43public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
44    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
45
46    public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
47
48    static final class KeyboardTheme {
49        public final int mThemeId;
50        public final int mStyleId;
51
52        // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
53        // in values/style.xml.
54        public KeyboardTheme(final int themeId, final int styleId) {
55            mThemeId = themeId;
56            mStyleId = styleId;
57        }
58    }
59
60    private static final KeyboardTheme[] KEYBOARD_THEMES = {
61        new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
62        new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread),
63    };
64
65    private SubtypeSwitcher mSubtypeSwitcher;
66    private SharedPreferences mPrefs;
67
68    private InputView mCurrentInputView;
69    private MainKeyboardView mKeyboardView;
70    private LatinIME mLatinIME;
71    private Resources mResources;
72
73    private KeyboardState mState;
74
75    private KeyboardLayoutSet mKeyboardLayoutSet;
76
77    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
78     * what user actually typed. */
79    private boolean mIsAutoCorrectionActive;
80
81    private KeyboardTheme mKeyboardTheme = KEYBOARD_THEMES[0];
82    private Context mThemeContext;
83
84    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
85
86    public static KeyboardSwitcher getInstance() {
87        return sInstance;
88    }
89
90    private KeyboardSwitcher() {
91        // Intentional empty constructor for singleton.
92    }
93
94    public static void init(final LatinIME latinIme) {
95        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
96        sInstance.initInternal(latinIme, prefs);
97    }
98
99    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
100        mLatinIME = latinIme;
101        mResources = latinIme.getResources();
102        mPrefs = prefs;
103        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
104        mState = new KeyboardState(this);
105        setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs));
106    }
107
108    private static KeyboardTheme getKeyboardTheme(final Context context,
109            final SharedPreferences prefs) {
110        final String defaultIndex = context.getString(R.string.config_default_keyboard_theme_index);
111        final String themeIndex = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultIndex);
112        try {
113            final int index = Integer.valueOf(themeIndex);
114            if (index >= 0 && index < KEYBOARD_THEMES.length) {
115                return KEYBOARD_THEMES[index];
116            }
117        } catch (NumberFormatException e) {
118            // Format error, keyboard theme is default to 0.
119        }
120        Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to "
121                + defaultIndex);
122        return KEYBOARD_THEMES[Integer.valueOf(defaultIndex)];
123    }
124
125    private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) {
126        if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
127            mKeyboardTheme = keyboardTheme;
128            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
129            KeyboardLayoutSet.clearKeyboardCache();
130        }
131    }
132
133    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) {
134        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
135                mThemeContext, editorInfo);
136        final Resources res = mThemeContext.getResources();
137        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
138        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
139        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
140        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
141        builder.setOptions(
142                settingsValues.isVoiceKeyEnabled(editorInfo),
143                settingsValues.isVoiceKeyOnMain(),
144                settingsValues.isLanguageSwitchKeyEnabled());
145        mKeyboardLayoutSet = builder.build();
146        try {
147            mState.onLoadKeyboard();
148        } catch (KeyboardLayoutSetException e) {
149            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
150            LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
151            return;
152        }
153    }
154
155    public void saveKeyboardState() {
156        if (getKeyboard() != null) {
157            mState.onSaveKeyboardState();
158        }
159    }
160
161    public void onFinishInputView() {
162        mIsAutoCorrectionActive = false;
163    }
164
165    public void onHideWindow() {
166        mIsAutoCorrectionActive = false;
167    }
168
169    private void setKeyboard(final Keyboard keyboard) {
170        final MainKeyboardView keyboardView = mKeyboardView;
171        final Keyboard oldKeyboard = keyboardView.getKeyboard();
172        keyboardView.setKeyboard(keyboard);
173        mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
174        keyboardView.setKeyPreviewPopupEnabled(
175                Settings.readKeyPreviewPopupEnabled(mPrefs, mResources),
176                Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources));
177        keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
178        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
179        final boolean subtypeChanged = (oldKeyboard == null)
180                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
181        final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
182                keyboard.mId.mLocale);
183        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
184                RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
185    }
186
187    public Keyboard getKeyboard() {
188        if (mKeyboardView != null) {
189            return mKeyboardView.getKeyboard();
190        }
191        return null;
192    }
193
194    /**
195     * Update keyboard shift state triggered by connected EditText status change.
196     */
197    public void updateShiftState() {
198        mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
199                mLatinIME.getCurrentRecapitalizeState());
200    }
201
202    // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
203    // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
204    public void resetKeyboardStateToAlphabet() {
205        mState.onResetKeyboardStateToAlphabet();
206    }
207
208    public void onPressKey(final int code, final boolean isSinglePointer) {
209        mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState());
210    }
211
212    public void onReleaseKey(final int code, final boolean withSliding) {
213        mState.onReleaseKey(code, withSliding);
214    }
215
216    public void onFinishSlidingInput() {
217        mState.onFinishSlidingInput();
218    }
219
220    // Implements {@link KeyboardState.SwitchActions}.
221    @Override
222    public void setAlphabetKeyboard() {
223        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
224    }
225
226    // Implements {@link KeyboardState.SwitchActions}.
227    @Override
228    public void setAlphabetManualShiftedKeyboard() {
229        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
230    }
231
232    // Implements {@link KeyboardState.SwitchActions}.
233    @Override
234    public void setAlphabetAutomaticShiftedKeyboard() {
235        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
236    }
237
238    // Implements {@link KeyboardState.SwitchActions}.
239    @Override
240    public void setAlphabetShiftLockedKeyboard() {
241        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
242    }
243
244    // Implements {@link KeyboardState.SwitchActions}.
245    @Override
246    public void setAlphabetShiftLockShiftedKeyboard() {
247        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
248    }
249
250    // Implements {@link KeyboardState.SwitchActions}.
251    @Override
252    public void setSymbolsKeyboard() {
253        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
254    }
255
256    // Implements {@link KeyboardState.SwitchActions}.
257    @Override
258    public void requestUpdatingShiftState() {
259        mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
260                mLatinIME.getCurrentRecapitalizeState());
261    }
262
263    // Implements {@link KeyboardState.SwitchActions}.
264    @Override
265    public void startDoubleTapShiftKeyTimer() {
266        final MainKeyboardView keyboardView = getMainKeyboardView();
267        if (keyboardView != null) {
268            keyboardView.startDoubleTapShiftKeyTimer();
269        }
270    }
271
272    // Implements {@link KeyboardState.SwitchActions}.
273    @Override
274    public void cancelDoubleTapShiftKeyTimer() {
275        final MainKeyboardView keyboardView = getMainKeyboardView();
276        if (keyboardView != null) {
277            keyboardView.cancelDoubleTapShiftKeyTimer();
278        }
279    }
280
281    // Implements {@link KeyboardState.SwitchActions}.
282    @Override
283    public boolean isInDoubleTapShiftKeyTimeout() {
284        final MainKeyboardView keyboardView = getMainKeyboardView();
285        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
286    }
287
288    /**
289     * Updates state machine to figure out when to automatically switch back to the previous mode.
290     */
291    public void onCodeInput(final int code) {
292        mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
293    }
294
295    public MainKeyboardView getMainKeyboardView() {
296        return mKeyboardView;
297    }
298
299    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
300        if (mKeyboardView != null) {
301            mKeyboardView.closing();
302        }
303
304        setContextThemeWrapper(mLatinIME, mKeyboardTheme);
305        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
306                R.layout.input_view, null);
307
308        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
309        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
310        mKeyboardView.setKeyboardActionListener(mLatinIME);
311
312        // This always needs to be set since the accessibility state can
313        // potentially change without the input view being re-created.
314        AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView);
315
316        return mCurrentInputView;
317    }
318
319    public void onNetworkStateChanged() {
320        if (mKeyboardView != null) {
321            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
322        }
323    }
324
325    public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) {
326        if (mIsAutoCorrectionActive != isAutoCorrection) {
327            mIsAutoCorrectionActive = isAutoCorrection;
328            if (mKeyboardView != null) {
329                mKeyboardView.updateAutoCorrectionState(isAutoCorrection);
330            }
331        }
332    }
333
334    public int getKeyboardShiftMode() {
335        final Keyboard keyboard = getKeyboard();
336        if (keyboard == null) {
337            return WordComposer.CAPS_MODE_OFF;
338        }
339        switch (keyboard.mId.mElementId) {
340        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
341        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
342            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
343        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
344            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
345        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
346            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
347        default:
348            return WordComposer.CAPS_MODE_OFF;
349        }
350    }
351}
352