KeyboardSwitcher.java revision ac88f3d8459e17236654aa38debea749ecb631d1
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.Configuration;
22import android.content.res.Resources;
23import android.preference.PreferenceManager;
24import android.util.Log;
25import android.view.ContextThemeWrapper;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.inputmethod.EditorInfo;
29
30import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
31import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
32import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
33import com.android.inputmethod.keyboard.internal.KeyboardState;
34import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
35import com.android.inputmethod.latin.InputView;
36import com.android.inputmethod.latin.LatinIME;
37import com.android.inputmethod.latin.R;
38import com.android.inputmethod.latin.RichInputMethodManager;
39import com.android.inputmethod.latin.SubtypeSwitcher;
40import com.android.inputmethod.latin.WordComposer;
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    private SettingsValues mCurrentSettingsValues;
64
65    private KeyboardTheme mKeyboardTheme;
66    private Context mThemeContext;
67
68    private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
69
70    public static KeyboardSwitcher getInstance() {
71        return sInstance;
72    }
73
74    private KeyboardSwitcher() {
75        // Intentional empty constructor for singleton.
76    }
77
78    public static void init(final LatinIME latinIme) {
79        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
80        sInstance.initInternal(latinIme, prefs);
81    }
82
83    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
84        mLatinIME = latinIme;
85        mPrefs = prefs;
86        mSubtypeSwitcher = SubtypeSwitcher.getInstance();
87        mState = new KeyboardState(this);
88        mIsHardwareAcceleratedDrawingEnabled =
89                InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
90    }
91
92    public void updateKeyboardTheme() {
93        final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
94                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
95        if (themeUpdated && mKeyboardView != null) {
96            mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled));
97        }
98    }
99
100    private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
101            final KeyboardTheme keyboardTheme) {
102        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
103            mKeyboardTheme = keyboardTheme;
104            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
105            KeyboardLayoutSet.onKeyboardThemeChanged();
106            return true;
107        }
108        return false;
109    }
110
111    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
112            final int currentAutoCapsState, final int currentRecapitalizeState) {
113        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
114                mThemeContext, editorInfo);
115        final Resources res = mThemeContext.getResources();
116        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
117        final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
118        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
119        builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
120        builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
121        builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
122        mKeyboardLayoutSet = builder.build();
123        mCurrentSettingsValues = settingsValues;
124        try {
125            mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
126            mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocale(), mThemeContext);
127        } catch (KeyboardLayoutSetException e) {
128            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
129            return;
130        }
131    }
132
133    public void saveKeyboardState() {
134        if (getKeyboard() != null || isShowingEmojiPalettes()) {
135            mState.onSaveKeyboardState();
136        }
137    }
138
139    public void onHideWindow() {
140        if (mKeyboardView != null) {
141            mKeyboardView.onHideWindow();
142        }
143    }
144
145    private void setKeyboard(final Keyboard keyboard) {
146        // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
147        setMainKeyboardFrame();
148        final MainKeyboardView keyboardView = mKeyboardView;
149        final Keyboard oldKeyboard = keyboardView.getKeyboard();
150        keyboardView.setKeyboard(keyboard);
151        mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
152        keyboardView.setKeyPreviewPopupEnabled(
153                mCurrentSettingsValues.mKeyPreviewPopupOn,
154                mCurrentSettingsValues.mKeyPreviewPopupDismissDelay);
155        keyboardView.setKeyPreviewAnimationParams(
156                mCurrentSettingsValues.mHasCustomKeyPreviewAnimationParams,
157                mCurrentSettingsValues.mKeyPreviewShowUpStartXScale,
158                mCurrentSettingsValues.mKeyPreviewShowUpStartYScale,
159                mCurrentSettingsValues.mKeyPreviewShowUpDuration,
160                mCurrentSettingsValues.mKeyPreviewDismissEndXScale,
161                mCurrentSettingsValues.mKeyPreviewDismissEndYScale,
162                mCurrentSettingsValues.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() {
240        mMainKeyboardFrame.setVisibility(hasHardwareKeyboard() ? View.GONE : View.VISIBLE);
241        mEmojiPalettesView.setVisibility(View.GONE);
242        mEmojiPalettesView.stopEmojiPalettes();
243    }
244
245    // TODO: Move this boolean to a member of {@link SettingsValues} and reset it
246    // at {@link LatinIME#onConfigurationChanged(Configuration)}.
247    public boolean hasHardwareKeyboard() {
248        // Copied from {@link InputMethodServce#onEvaluateInputViewShown()}.
249        final Configuration config = mLatinIME.getResources().getConfiguration();
250        final boolean noHardwareKeyboard = config.keyboard == Configuration.KEYBOARD_NOKEYS
251                || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES;
252        return !noHardwareKeyboard;
253    }
254
255    // Implements {@link KeyboardState.SwitchActions}.
256    @Override
257    public void setEmojiKeyboard() {
258        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
259        mMainKeyboardFrame.setVisibility(View.GONE);
260        mEmojiPalettesView.startEmojiPalettes(
261                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
262                mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
263        mEmojiPalettesView.setVisibility(View.VISIBLE);
264    }
265
266    public void onToggleEmojiKeyboard() {
267        if (mKeyboardLayoutSet == null) {
268            return;
269        }
270        if (isShowingEmojiPalettes()) {
271            setAlphabetKeyboard();
272        } else {
273            setEmojiKeyboard();
274        }
275    }
276
277    // Implements {@link KeyboardState.SwitchActions}.
278    @Override
279    public void setSymbolsShiftedKeyboard() {
280        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
281    }
282
283    // Future method for requesting an updating to the shift state.
284    public void requestUpdatingShiftState(final int currentAutoCapsState,
285            final int currentRecapitalizeState) {
286        mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
287    }
288
289    // Implements {@link KeyboardState.SwitchActions}.
290    @Override
291    public void startDoubleTapShiftKeyTimer() {
292        final MainKeyboardView keyboardView = getMainKeyboardView();
293        if (keyboardView != null) {
294            keyboardView.startDoubleTapShiftKeyTimer();
295        }
296    }
297
298    // Implements {@link KeyboardState.SwitchActions}.
299    @Override
300    public void cancelDoubleTapShiftKeyTimer() {
301        final MainKeyboardView keyboardView = getMainKeyboardView();
302        if (keyboardView != null) {
303            keyboardView.cancelDoubleTapShiftKeyTimer();
304        }
305    }
306
307    // Implements {@link KeyboardState.SwitchActions}.
308    @Override
309    public boolean isInDoubleTapShiftKeyTimeout() {
310        final MainKeyboardView keyboardView = getMainKeyboardView();
311        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
312    }
313
314    /**
315     * Updates state machine to figure out when to automatically switch back to the previous mode.
316     */
317    public void onCodeInput(final int code, final int currentAutoCapsState,
318            final int currentRecapitalizeState) {
319        mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
320    }
321
322    public boolean isShowingEmojiPalettes() {
323        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
324    }
325
326    public boolean isShowingMoreKeysPanel() {
327        if (isShowingEmojiPalettes()) {
328            return false;
329        }
330        return mKeyboardView.isShowingMoreKeysPanel();
331    }
332
333    public View getVisibleKeyboardView() {
334        if (isShowingEmojiPalettes()) {
335            return mEmojiPalettesView;
336        }
337        return mKeyboardView;
338    }
339
340    public MainKeyboardView getMainKeyboardView() {
341        return mKeyboardView;
342    }
343
344    public void deallocateMemory() {
345        if (mKeyboardView != null) {
346            mKeyboardView.cancelAllOngoingEvents();
347            mKeyboardView.deallocateMemory();
348        }
349        if (mEmojiPalettesView != null) {
350            mEmojiPalettesView.stopEmojiPalettes();
351        }
352    }
353
354    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
355        if (mKeyboardView != null) {
356            mKeyboardView.closing();
357        }
358
359        updateKeyboardThemeAndContextThemeWrapper(
360                mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs));
361        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
362                R.layout.input_view, null);
363        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
364        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
365                R.id.emoji_palettes_view);
366
367        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
368        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
369        mKeyboardView.setKeyboardActionListener(mLatinIME);
370        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
371                isHardwareAcceleratedDrawingEnabled);
372        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
373        return mCurrentInputView;
374    }
375
376    public void onNetworkStateChanged() {
377        if (mKeyboardView != null) {
378            mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
379        }
380    }
381
382    public int getKeyboardShiftMode() {
383        final Keyboard keyboard = getKeyboard();
384        if (keyboard == null) {
385            return WordComposer.CAPS_MODE_OFF;
386        }
387        switch (keyboard.mId.mElementId) {
388        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
389        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
390            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
391        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
392            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
393        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
394            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
395        default:
396            return WordComposer.CAPS_MODE_OFF;
397        }
398    }
399
400    public int getCurrentKeyboardScriptId() {
401        if (null == mKeyboardLayoutSet) {
402            return ScriptUtils.SCRIPT_UNKNOWN;
403        }
404        return mKeyboardLayoutSet.getScriptId();
405    }
406}
407