KeyboardSwitcher.java revision 3897a43940eca9c400e542c76d2c790f2c48eb8c
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(0, R.style.KeyboardTheme),
62        new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast),
63        new KeyboardTheme(6, R.style.KeyboardTheme_Stone),
64        new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold),
65        new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread),
66        new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
67    };
68
69    private SubtypeSwitcher mSubtypeSwitcher;
70    private SharedPreferences mPrefs;
71
72    private InputView mCurrentInputView;
73    private MainKeyboardView mKeyboardView;
74    private LatinIME mLatinIME;
75    private Resources mResources;
76
77    private KeyboardState mState;
78
79    private KeyboardLayoutSet mKeyboardLayoutSet;
80
81    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
82     * what user actually typed. */
83    private boolean mIsAutoCorrectionActive;
84
85    private KeyboardTheme mKeyboardTheme = KEYBOARD_THEMES[0];
86    private Context mThemeContext;
87
88    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
89
90    public static KeyboardSwitcher getInstance() {
91        return sInstance;
92    }
93
94    private KeyboardSwitcher() {
95        // Intentional empty constructor for singleton.
96    }
97
98    public static void init(final LatinIME latinIme) {
99        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
100        sInstance.initInternal(latinIme, prefs);
101    }
102
103    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
104        mLatinIME = latinIme;
105        mResources = latinIme.getResources();
106        mPrefs = prefs;
107        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
108        mState = new KeyboardState(this);
109        setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs));
110    }
111
112    private static KeyboardTheme getKeyboardTheme(final Context context,
113            final SharedPreferences prefs) {
114        final String defaultIndex = context.getString(R.string.config_default_keyboard_theme_index);
115        final String themeIndex = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultIndex);
116        try {
117            final int index = Integer.valueOf(themeIndex);
118            if (index >= 0 && index < KEYBOARD_THEMES.length) {
119                return KEYBOARD_THEMES[index];
120            }
121        } catch (NumberFormatException e) {
122            // Format error, keyboard theme is default to 0.
123        }
124        Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to 0");
125        return KEYBOARD_THEMES[0];
126    }
127
128    private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) {
129        if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
130            mKeyboardTheme = keyboardTheme;
131            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
132            KeyboardLayoutSet.clearKeyboardCache();
133        }
134    }
135
136    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) {
137        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
138                mThemeContext, editorInfo);
139        final Resources res = mThemeContext.getResources();
140        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
141        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
142        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
143        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
144        builder.setOptions(
145                settingsValues.isVoiceKeyEnabled(editorInfo),
146                settingsValues.isVoiceKeyOnMain(),
147                settingsValues.isLanguageSwitchKeyEnabled());
148        mKeyboardLayoutSet = builder.build();
149        try {
150            mState.onLoadKeyboard();
151        } catch (KeyboardLayoutSetException e) {
152            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
153            LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
154            return;
155        }
156    }
157
158    public void saveKeyboardState() {
159        if (getKeyboard() != null) {
160            mState.onSaveKeyboardState();
161        }
162    }
163
164    public void onFinishInputView() {
165        mIsAutoCorrectionActive = false;
166    }
167
168    public void onHideWindow() {
169        mIsAutoCorrectionActive = false;
170    }
171
172    private void setKeyboard(final Keyboard keyboard) {
173        final MainKeyboardView keyboardView = mKeyboardView;
174        final Keyboard oldKeyboard = keyboardView.getKeyboard();
175        keyboardView.setKeyboard(keyboard);
176        mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
177        keyboardView.setKeyPreviewPopupEnabled(
178                Settings.readKeyPreviewPopupEnabled(mPrefs, mResources),
179                Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources));
180        keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
181        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
182        final boolean subtypeChanged = (oldKeyboard == null)
183                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
184        final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
185                keyboard.mId.mLocale);
186        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
187                RichInputMethodManager.getInstance().hasMultipleEnabledIMEsOrSubtypes(true));
188    }
189
190    public Keyboard getKeyboard() {
191        if (mKeyboardView != null) {
192            return mKeyboardView.getKeyboard();
193        }
194        return null;
195    }
196
197    /**
198     * Update keyboard shift state triggered by connected EditText status change.
199     */
200    public void updateShiftState() {
201        mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
202                mLatinIME.getCurrentRecapitalizeState());
203    }
204
205    // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
206    // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
207    public void resetKeyboardStateToAlphabet() {
208        mState.onResetKeyboardStateToAlphabet();
209    }
210
211    public void onPressKey(final int code, final boolean isSinglePointer) {
212        mState.onPressKey(code, isSinglePointer, mLatinIME.getCurrentAutoCapsState());
213    }
214
215    public void onReleaseKey(final int code, final boolean withSliding) {
216        mState.onReleaseKey(code, withSliding);
217    }
218
219    public void onFinishSlidingInput() {
220        mState.onFinishSlidingInput();
221    }
222
223    // Implements {@link KeyboardState.SwitchActions}.
224    @Override
225    public void setAlphabetKeyboard() {
226        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
227    }
228
229    // Implements {@link KeyboardState.SwitchActions}.
230    @Override
231    public void setAlphabetManualShiftedKeyboard() {
232        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
233    }
234
235    // Implements {@link KeyboardState.SwitchActions}.
236    @Override
237    public void setAlphabetAutomaticShiftedKeyboard() {
238        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
239    }
240
241    // Implements {@link KeyboardState.SwitchActions}.
242    @Override
243    public void setAlphabetShiftLockedKeyboard() {
244        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
245    }
246
247    // Implements {@link KeyboardState.SwitchActions}.
248    @Override
249    public void setAlphabetShiftLockShiftedKeyboard() {
250        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
251    }
252
253    // Implements {@link KeyboardState.SwitchActions}.
254    @Override
255    public void setSymbolsKeyboard() {
256        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
257    }
258
259    // Implements {@link KeyboardState.SwitchActions}.
260    @Override
261    public void setSymbolsShiftedKeyboard() {
262        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
263    }
264
265    // Implements {@link KeyboardState.SwitchActions}.
266    @Override
267    public void requestUpdatingShiftState() {
268        mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(),
269                mLatinIME.getCurrentRecapitalizeState());
270    }
271
272    // Implements {@link KeyboardState.SwitchActions}.
273    @Override
274    public void startDoubleTapShiftKeyTimer() {
275        final MainKeyboardView keyboardView = getMainKeyboardView();
276        if (keyboardView != null) {
277            keyboardView.startDoubleTapShiftKeyTimer();
278        }
279    }
280
281    // Implements {@link KeyboardState.SwitchActions}.
282    @Override
283    public void cancelDoubleTapShiftKeyTimer() {
284        final MainKeyboardView keyboardView = getMainKeyboardView();
285        if (keyboardView != null) {
286            keyboardView.cancelDoubleTapShiftKeyTimer();
287        }
288    }
289
290    // Implements {@link KeyboardState.SwitchActions}.
291    @Override
292    public boolean isInDoubleTapShiftKeyTimeout() {
293        final MainKeyboardView keyboardView = getMainKeyboardView();
294        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
295    }
296
297    /**
298     * Updates state machine to figure out when to automatically switch back to the previous mode.
299     */
300    public void onCodeInput(final int code) {
301        mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
302    }
303
304    public MainKeyboardView getMainKeyboardView() {
305        return mKeyboardView;
306    }
307
308    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
309        if (mKeyboardView != null) {
310            mKeyboardView.closing();
311        }
312
313        setContextThemeWrapper(mLatinIME, mKeyboardTheme);
314        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
315                R.layout.input_view, null);
316
317        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
318        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
319        mKeyboardView.setKeyboardActionListener(mLatinIME);
320
321        // This always needs to be set since the accessibility state can
322        // potentially change without the input view being re-created.
323        AccessibleKeyboardViewProxy.getInstance().setView(mKeyboardView);
324
325        return mCurrentInputView;
326    }
327
328    public void onNetworkStateChanged() {
329        if (mKeyboardView != null) {
330            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
331        }
332    }
333
334    public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) {
335        if (mIsAutoCorrectionActive != isAutoCorrection) {
336            mIsAutoCorrectionActive = isAutoCorrection;
337            if (mKeyboardView != null) {
338                mKeyboardView.updateAutoCorrectionState(isAutoCorrection);
339            }
340        }
341    }
342
343    public int getKeyboardShiftMode() {
344        final Keyboard keyboard = getKeyboard();
345        if (keyboard == null) {
346            return WordComposer.CAPS_MODE_OFF;
347        }
348        switch (keyboard.mId.mElementId) {
349        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
350        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
351            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
352        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
353            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
354        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
355            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
356        default:
357            return WordComposer.CAPS_MODE_OFF;
358        }
359    }
360}
361