KeyboardSwitcher.java revision 5f6c303f0ba70c0bcfd73d06b63f0175669dafc1
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.compat.InputMethodServiceCompatUtils;
30import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
31import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
32import com.android.inputmethod.keyboard.internal.KeyboardState;
33import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
34import com.android.inputmethod.latin.InputView;
35import com.android.inputmethod.latin.LatinIME;
36import com.android.inputmethod.latin.R;
37import com.android.inputmethod.latin.RichInputMethodManager;
38import com.android.inputmethod.latin.SubtypeSwitcher;
39import com.android.inputmethod.latin.WordComposer;
40import com.android.inputmethod.latin.settings.SettingsValues;
41import com.android.inputmethod.latin.utils.ResourceUtils;
42import com.android.inputmethod.latin.utils.ScriptUtils;
43
44public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
45    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
46
47    private SubtypeSwitcher mSubtypeSwitcher;
48    private SharedPreferences mPrefs;
49
50    private InputView mCurrentInputView;
51    private View mMainKeyboardFrame;
52    private MainKeyboardView mKeyboardView;
53    private EmojiPalettesView mEmojiPalettesView;
54    private LatinIME mLatinIME;
55    private boolean mIsHardwareAcceleratedDrawingEnabled;
56
57    private KeyboardState mState;
58
59    private KeyboardLayoutSet mKeyboardLayoutSet;
60    // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}.
61    private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
62    private SettingsValues mCurrentSettingsValues;
63
64    private KeyboardTheme mKeyboardTheme;
65    private Context mThemeContext;
66
67    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
68
69    public static KeyboardSwitcher getInstance() {
70        return sInstance;
71    }
72
73    private KeyboardSwitcher() {
74        // Intentional empty constructor for singleton.
75    }
76
77    public static void init(final LatinIME latinIme) {
78        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
79        sInstance.initInternal(latinIme, prefs);
80    }
81
82    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
83        mLatinIME = latinIme;
84        mPrefs = prefs;
85        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
86        mState = new KeyboardState(this);
87        mIsHardwareAcceleratedDrawingEnabled =
88                InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
89    }
90
91    public void updateKeyboardTheme() {
92        final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
93                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
94        if (themeUpdated && mKeyboardView != null) {
95            mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled));
96        }
97    }
98
99    private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
100            final KeyboardTheme keyboardTheme) {
101        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
102            mKeyboardTheme = keyboardTheme;
103            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
104            KeyboardLayoutSet.clearKeyboardCache();
105            return true;
106        }
107        return false;
108    }
109
110    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
111            final int currentAutoCapsState, final int currentRecapitalizeState) {
112        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
113                mThemeContext, editorInfo);
114        final Resources res = mThemeContext.getResources();
115        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
116        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
117        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
118        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
119        builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
120        builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
121        mKeyboardLayoutSet = builder.build();
122        mCurrentSettingsValues = settingsValues;
123        try {
124            mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
125            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext);
126        } catch (KeyboardLayoutSetException e) {
127            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
128            return;
129        }
130    }
131
132    public void saveKeyboardState() {
133        if (getKeyboard() != null || isShowingEmojiPalettes()) {
134            mState.onSaveKeyboardState();
135        }
136    }
137
138    public void onHideWindow() {
139        if (mKeyboardView != null) {
140            mKeyboardView.onHideWindow();
141        }
142    }
143
144    private void setKeyboard(final Keyboard keyboard) {
145        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
146        setMainKeyboardFrame();
147        final MainKeyboardView keyboardView = mKeyboardView;
148        final Keyboard oldKeyboard = keyboardView.getKeyboard();
149        keyboardView.setKeyboard(keyboard);
150        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
151        keyboardView.setKeyPreviewPopupEnabled(
152                mCurrentSettingsValues.mKeyPreviewPopupOn,
153                mCurrentSettingsValues.mKeyPreviewPopupDismissDelay);
154        keyboardView.setKeyPreviewAnimationParams(
155                mCurrentSettingsValues.mKeyPreviewShowUpStartScale,
156                mCurrentSettingsValues.mKeyPreviewShowUpDuration,
157                mCurrentSettingsValues.mKeyPreviewDismissEndScale,
158                mCurrentSettingsValues.mKeyPreviewDismissDuration);
159        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
160        final boolean subtypeChanged = (oldKeyboard == null)
161                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
162        final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
163                keyboard.mId.mSubtype);
164        final boolean hasMultipleEnabledIMEsOrSubtypes = RichInputMethodManager.getInstance()
165                .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
166        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
167                hasMultipleEnabledIMEsOrSubtypes);
168    }
169
170    public Keyboard getKeyboard() {
171        if (mKeyboardView != null) {
172            return mKeyboardView.getKeyboard();
173        }
174        return null;
175    }
176
177    // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
178    // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
179    public void resetKeyboardStateToAlphabet(final int currentAutoCapsState,
180            final int currentRecapitalizeState) {
181        mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState);
182    }
183
184    public void onPressKey(final int code, final boolean isSinglePointer,
185            final int currentAutoCapsState, final int currentRecapitalizeState) {
186        mState.onPressKey(code, isSinglePointer, currentAutoCapsState, currentRecapitalizeState);
187    }
188
189    public void onReleaseKey(final int code, final boolean withSliding,
190            final int currentAutoCapsState, final int currentRecapitalizeState) {
191        mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState);
192    }
193
194    public void onFinishSlidingInput(final int currentAutoCapsState,
195            final int currentRecapitalizeState) {
196        mState.onFinishSlidingInput(currentAutoCapsState, currentRecapitalizeState);
197    }
198
199    // Implements {@link KeyboardState.SwitchActions}.
200    @Override
201    public void setAlphabetKeyboard() {
202        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
203    }
204
205    // Implements {@link KeyboardState.SwitchActions}.
206    @Override
207    public void setAlphabetManualShiftedKeyboard() {
208        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
209    }
210
211    // Implements {@link KeyboardState.SwitchActions}.
212    @Override
213    public void setAlphabetAutomaticShiftedKeyboard() {
214        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
215    }
216
217    // Implements {@link KeyboardState.SwitchActions}.
218    @Override
219    public void setAlphabetShiftLockedKeyboard() {
220        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
221    }
222
223    // Implements {@link KeyboardState.SwitchActions}.
224    @Override
225    public void setAlphabetShiftLockShiftedKeyboard() {
226        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
227    }
228
229    // Implements {@link KeyboardState.SwitchActions}.
230    @Override
231    public void setSymbolsKeyboard() {
232        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
233    }
234
235    private void setMainKeyboardFrame() {
236        mMainKeyboardFrame.setVisibility(View.VISIBLE);
237        mEmojiPalettesView.setVisibility(View.GONE);
238        mEmojiPalettesView.stopEmojiPalettes();
239    }
240
241    // Implements {@link KeyboardState.SwitchActions}.
242    @Override
243    public void setEmojiKeyboard() {
244        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
245        mMainKeyboardFrame.setVisibility(View.GONE);
246        mEmojiPalettesView.startEmojiPalettes(
247                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
248                mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
249        mEmojiPalettesView.setVisibility(View.VISIBLE);
250    }
251
252    // Implements {@link KeyboardState.SwitchActions}.
253    @Override
254    public void setSymbolsShiftedKeyboard() {
255        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
256    }
257
258    // Future method for requesting an updating to the shift state.
259    public void requestUpdatingShiftState(final int currentAutoCapsState,
260            final int currentRecapitalizeState) {
261        mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
262    }
263
264    // Implements {@link KeyboardState.SwitchActions}.
265    @Override
266    public void startDoubleTapShiftKeyTimer() {
267        final MainKeyboardView keyboardView = getMainKeyboardView();
268        if (keyboardView != null) {
269            keyboardView.startDoubleTapShiftKeyTimer();
270        }
271    }
272
273    // Implements {@link KeyboardState.SwitchActions}.
274    @Override
275    public void cancelDoubleTapShiftKeyTimer() {
276        final MainKeyboardView keyboardView = getMainKeyboardView();
277        if (keyboardView != null) {
278            keyboardView.cancelDoubleTapShiftKeyTimer();
279        }
280    }
281
282    // Implements {@link KeyboardState.SwitchActions}.
283    @Override
284    public boolean isInDoubleTapShiftKeyTimeout() {
285        final MainKeyboardView keyboardView = getMainKeyboardView();
286        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
287    }
288
289    /**
290     * Updates state machine to figure out when to automatically switch back to the previous mode.
291     */
292    public void onCodeInput(final int code, final int currentAutoCapsState,
293            final int currentRecapitalizeState) {
294        mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
295    }
296
297    public boolean isShowingEmojiPalettes() {
298        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
299    }
300
301    public boolean isShowingMoreKeysPanel() {
302        if (isShowingEmojiPalettes()) {
303            return false;
304        }
305        return mKeyboardView.isShowingMoreKeysPanel();
306    }
307
308    public View getVisibleKeyboardView() {
309        if (isShowingEmojiPalettes()) {
310            return mEmojiPalettesView;
311        }
312        return mKeyboardView;
313    }
314
315    public MainKeyboardView getMainKeyboardView() {
316        return mKeyboardView;
317    }
318
319    public void deallocateMemory() {
320        if (mKeyboardView != null) {
321            mKeyboardView.cancelAllOngoingEvents();
322            mKeyboardView.deallocateMemory();
323        }
324        if (mEmojiPalettesView != null) {
325            mEmojiPalettesView.stopEmojiPalettes();
326        }
327    }
328
329    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
330        if (mKeyboardView != null) {
331            mKeyboardView.closing();
332        }
333
334        updateKeyboardThemeAndContextThemeWrapper(
335                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
336        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
337                R.layout.input_view, null);
338        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
339        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
340                R.id.emoji_keyboard_view);
341
342        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
343        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
344        mKeyboardView.setKeyboardActionListener(mLatinIME);
345        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
346                isHardwareAcceleratedDrawingEnabled);
347        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
348        return mCurrentInputView;
349    }
350
351    public void onNetworkStateChanged() {
352        if (mKeyboardView != null) {
353            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
354        }
355    }
356
357    public int getKeyboardShiftMode() {
358        final Keyboard keyboard = getKeyboard();
359        if (keyboard == null) {
360            return WordComposer.CAPS_MODE_OFF;
361        }
362        switch (keyboard.mId.mElementId) {
363        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
364        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
365            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
366        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
367            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
368        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
369            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
370        default:
371            return WordComposer.CAPS_MODE_OFF;
372        }
373    }
374
375    public int getCurrentKeyboardScriptId() {
376        if (null == mKeyboardLayoutSet) {
377            return ScriptUtils.SCRIPT_UNKNOWN;
378        }
379        return mKeyboardLayoutSet.getScriptId();
380    }
381}
382