KeyboardSwitcher.java revision 88f282de0dbb92c7d423c30a84395893d6765092
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.Settings;
41import com.android.inputmethod.latin.settings.SettingsValues;
42import com.android.inputmethod.latin.utils.ResourceUtils;
43import com.android.inputmethod.latin.utils.ScriptUtils;
44
45public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
46    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
47
48    private SubtypeSwitcher mSubtypeSwitcher;
49    private SharedPreferences mPrefs;
50
51    private InputView mCurrentInputView;
52    private View mMainKeyboardFrame;
53    private MainKeyboardView mKeyboardView;
54    private EmojiPalettesView mEmojiPalettesView;
55    private LatinIME mLatinIME;
56    private boolean mIsHardwareAcceleratedDrawingEnabled;
57
58    private KeyboardState mState;
59
60    private KeyboardLayoutSet mKeyboardLayoutSet;
61    // TODO: The following {@link KeyboardTextsSet} should be in {@link KeyboardLayoutSet}.
62    private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
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.onKeyboardThemeChanged();
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        try {
123            mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
124            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext);
125        } catch (KeyboardLayoutSetException e) {
126            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
127            return;
128        }
129    }
130
131    public void saveKeyboardState() {
132        if (getKeyboard() != null || isShowingEmojiPalettes()) {
133            mState.onSaveKeyboardState();
134        }
135    }
136
137    public void onHideWindow() {
138        if (mKeyboardView != null) {
139            mKeyboardView.onHideWindow();
140        }
141    }
142
143    private void setKeyboard(final Keyboard keyboard) {
144        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
145        final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
146        setMainKeyboardFrame(currentSettingsValues);
147        // TODO: pass this object to setKeyboard instead of getting the current values.
148        final MainKeyboardView keyboardView = mKeyboardView;
149        final Keyboard oldKeyboard = keyboardView.getKeyboard();
150        keyboardView.setKeyboard(keyboard);
151        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
152        keyboardView.setKeyPreviewPopupEnabled(
153                currentSettingsValues.mKeyPreviewPopupOn,
154                currentSettingsValues.mKeyPreviewPopupDismissDelay);
155        keyboardView.setKeyPreviewAnimationParams(
156                currentSettingsValues.mHasCustomKeyPreviewAnimationParams,
157                currentSettingsValues.mKeyPreviewShowUpStartXScale,
158                currentSettingsValues.mKeyPreviewShowUpStartYScale,
159                currentSettingsValues.mKeyPreviewShowUpDuration,
160                currentSettingsValues.mKeyPreviewDismissEndXScale,
161                currentSettingsValues.mKeyPreviewDismissEndYScale,
162                currentSettingsValues.mKeyPreviewDismissDuration);
163        keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
164        final boolean subtypeChanged = (oldKeyboard == null)
165                || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
166        final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
167                keyboard.mId.mSubtype);
168        final boolean hasMultipleEnabledIMEsOrSubtypes = RichInputMethodManager.getInstance()
169                .hasMultipleEnabledIMEsOrSubtypes(true /* shouldIncludeAuxiliarySubtypes */);
170        keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, languageOnSpacebarFormatType,
171                hasMultipleEnabledIMEsOrSubtypes);
172    }
173
174    public Keyboard getKeyboard() {
175        if (mKeyboardView != null) {
176            return mKeyboardView.getKeyboard();
177        }
178        return null;
179    }
180
181    // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
182    // when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
183    public void resetKeyboardStateToAlphabet(final int currentAutoCapsState,
184            final int currentRecapitalizeState) {
185        mState.onResetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState);
186    }
187
188    public void onPressKey(final int code, final boolean isSinglePointer,
189            final int currentAutoCapsState, final int currentRecapitalizeState) {
190        mState.onPressKey(code, isSinglePointer, currentAutoCapsState, currentRecapitalizeState);
191    }
192
193    public void onReleaseKey(final int code, final boolean withSliding,
194            final int currentAutoCapsState, final int currentRecapitalizeState) {
195        mState.onReleaseKey(code, withSliding, currentAutoCapsState, currentRecapitalizeState);
196    }
197
198    public void onFinishSlidingInput(final int currentAutoCapsState,
199            final int currentRecapitalizeState) {
200        mState.onFinishSlidingInput(currentAutoCapsState, currentRecapitalizeState);
201    }
202
203    // Implements {@link KeyboardState.SwitchActions}.
204    @Override
205    public void setAlphabetKeyboard() {
206        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
207    }
208
209    // Implements {@link KeyboardState.SwitchActions}.
210    @Override
211    public void setAlphabetManualShiftedKeyboard() {
212        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
213    }
214
215    // Implements {@link KeyboardState.SwitchActions}.
216    @Override
217    public void setAlphabetAutomaticShiftedKeyboard() {
218        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
219    }
220
221    // Implements {@link KeyboardState.SwitchActions}.
222    @Override
223    public void setAlphabetShiftLockedKeyboard() {
224        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
225    }
226
227    // Implements {@link KeyboardState.SwitchActions}.
228    @Override
229    public void setAlphabetShiftLockShiftedKeyboard() {
230        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
231    }
232
233    // Implements {@link KeyboardState.SwitchActions}.
234    @Override
235    public void setSymbolsKeyboard() {
236        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
237    }
238
239    private void setMainKeyboardFrame(final SettingsValues settingsValues) {
240        mMainKeyboardFrame.setVisibility(
241                settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE);
242        mEmojiPalettesView.setVisibility(View.GONE);
243        mEmojiPalettesView.stopEmojiPalettes();
244    }
245
246    // Implements {@link KeyboardState.SwitchActions}.
247    @Override
248    public void setEmojiKeyboard() {
249        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
250        mMainKeyboardFrame.setVisibility(View.GONE);
251        mEmojiPalettesView.startEmojiPalettes(
252                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
253                mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
254        mEmojiPalettesView.setVisibility(View.VISIBLE);
255    }
256
257    public void onToggleEmojiKeyboard() {
258        if (mKeyboardLayoutSet == null || !isShowingEmojiPalettes()) {
259            mLatinIME.startShowingInputView();
260            setEmojiKeyboard();
261        } else {
262            mLatinIME.stopShowingInputView();
263            setAlphabetKeyboard();
264        }
265    }
266
267    // Implements {@link KeyboardState.SwitchActions}.
268    @Override
269    public void setSymbolsShiftedKeyboard() {
270        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
271    }
272
273    // Future method for requesting an updating to the shift state.
274    public void requestUpdatingShiftState(final int currentAutoCapsState,
275            final int currentRecapitalizeState) {
276        mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
277    }
278
279    // Implements {@link KeyboardState.SwitchActions}.
280    @Override
281    public void startDoubleTapShiftKeyTimer() {
282        final MainKeyboardView keyboardView = getMainKeyboardView();
283        if (keyboardView != null) {
284            keyboardView.startDoubleTapShiftKeyTimer();
285        }
286    }
287
288    // Implements {@link KeyboardState.SwitchActions}.
289    @Override
290    public void cancelDoubleTapShiftKeyTimer() {
291        final MainKeyboardView keyboardView = getMainKeyboardView();
292        if (keyboardView != null) {
293            keyboardView.cancelDoubleTapShiftKeyTimer();
294        }
295    }
296
297    // Implements {@link KeyboardState.SwitchActions}.
298    @Override
299    public boolean isInDoubleTapShiftKeyTimeout() {
300        final MainKeyboardView keyboardView = getMainKeyboardView();
301        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
302    }
303
304    /**
305     * Updates state machine to figure out when to automatically switch back to the previous mode.
306     */
307    public void onCodeInput(final int code, final int currentAutoCapsState,
308            final int currentRecapitalizeState) {
309        mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
310    }
311
312    public boolean isShowingEmojiPalettes() {
313        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
314    }
315
316    public boolean isShowingMoreKeysPanel() {
317        if (isShowingEmojiPalettes()) {
318            return false;
319        }
320        return mKeyboardView.isShowingMoreKeysPanel();
321    }
322
323    public View getVisibleKeyboardView() {
324        if (isShowingEmojiPalettes()) {
325            return mEmojiPalettesView;
326        }
327        return mKeyboardView;
328    }
329
330    public MainKeyboardView getMainKeyboardView() {
331        return mKeyboardView;
332    }
333
334    public void deallocateMemory() {
335        if (mKeyboardView != null) {
336            mKeyboardView.cancelAllOngoingEvents();
337            mKeyboardView.deallocateMemory();
338        }
339        if (mEmojiPalettesView != null) {
340            mEmojiPalettesView.stopEmojiPalettes();
341        }
342    }
343
344    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
345        if (mKeyboardView != null) {
346            mKeyboardView.closing();
347        }
348
349        updateKeyboardThemeAndContextThemeWrapper(
350                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
351        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
352                R.layout.input_view, null);
353        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
354        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
355                R.id.emoji_palettes_view);
356
357        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
358        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
359        mKeyboardView.setKeyboardActionListener(mLatinIME);
360        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
361                isHardwareAcceleratedDrawingEnabled);
362        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
363        return mCurrentInputView;
364    }
365
366    public void onNetworkStateChanged() {
367        if (mKeyboardView != null) {
368            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
369        }
370    }
371
372    public int getKeyboardShiftMode() {
373        final Keyboard keyboard = getKeyboard();
374        if (keyboard == null) {
375            return WordComposer.CAPS_MODE_OFF;
376        }
377        switch (keyboard.mId.mElementId) {
378        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
379        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
380            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
381        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
382            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
383        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
384            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
385        default:
386            return WordComposer.CAPS_MODE_OFF;
387        }
388    }
389
390    public int getCurrentKeyboardScriptId() {
391        if (null == mKeyboardLayoutSet) {
392            return ScriptUtils.SCRIPT_UNKNOWN;
393        }
394        return mKeyboardLayoutSet.getScriptId();
395    }
396}
397