KeyboardSwitcher.java revision 12d80ebead6a1d7f704a5a3af3b6fe3313ceab05
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.res.Resources;
21import android.util.Log;
22import android.view.ContextThemeWrapper;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.view.inputmethod.EditorInfo;
26
27import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
28import com.android.inputmethod.event.Event;
29import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
30import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
31import com.android.inputmethod.keyboard.internal.KeyboardState;
32import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
33import com.android.inputmethod.latin.InputView;
34import com.android.inputmethod.latin.LatinIME;
35import com.android.inputmethod.latin.R;
36import com.android.inputmethod.latin.RichInputMethodManager;
37import com.android.inputmethod.latin.WordComposer;
38import com.android.inputmethod.latin.define.ProductionFlags;
39import com.android.inputmethod.latin.settings.Settings;
40import com.android.inputmethod.latin.settings.SettingsValues;
41import com.android.inputmethod.latin.utils.CapsModeUtils;
42import com.android.inputmethod.latin.utils.LanguageOnSpacebarUtils;
43import com.android.inputmethod.latin.utils.RecapitalizeStatus;
44import com.android.inputmethod.latin.utils.ResourceUtils;
45import com.android.inputmethod.latin.utils.ScriptUtils;
46
47public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
48    private static final String TAG = KeyboardSwitcher.class.getSimpleName();
49
50    private InputView mCurrentInputView;
51    private View mMainKeyboardFrame;
52    private MainKeyboardView mKeyboardView;
53    private EmojiPalettesView mEmojiPalettesView;
54    private LatinIME mLatinIME;
55    private RichInputMethodManager mRichImm;
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        sInstance.initInternal(latinIme);
79    }
80
81    private void initInternal(final LatinIME latinIme) {
82        mLatinIME = latinIme;
83        mRichImm = RichInputMethodManager.getInstance();
84        mState = new KeyboardState(this);
85        mIsHardwareAcceleratedDrawingEnabled =
86                InputMethodServiceCompatUtils.enableHardwareAcceleration(mLatinIME);
87    }
88
89    public void updateKeyboardTheme() {
90        final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper(
91                mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */));
92        if (themeUpdated && mKeyboardView != null) {
93            mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled));
94        }
95    }
96
97    private boolean updateKeyboardThemeAndContextThemeWrapper(final Context context,
98            final KeyboardTheme keyboardTheme) {
99        if (mThemeContext == null || !keyboardTheme.equals(mKeyboardTheme)) {
100            mKeyboardTheme = keyboardTheme;
101            mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
102            KeyboardLayoutSet.onKeyboardThemeChanged();
103            return true;
104        }
105        return false;
106    }
107
108    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues,
109            final int currentAutoCapsState, final int currentRecapitalizeState) {
110        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
111                mThemeContext, editorInfo);
112        final Resources res = mThemeContext.getResources();
113        final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
114        final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
115        builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
116        builder.setSubtype(mRichImm.getCurrentSubtype());
117        builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
118        builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
119        builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
120                && settingsValues.mIsSplitKeyboardEnabled);
121        mKeyboardLayoutSet = builder.build();
122        try {
123            mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
124            // TODO: revisit this for multi-lingual input
125            mKeyboardTextsSet.setLocale(mRichImm.getCurrentSubtypeLocales()[0], mThemeContext);
126        } catch (KeyboardLayoutSetException e) {
127            Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
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(mRichImm.isShortcutImeReady());
164        final boolean subtypeChanged = (oldKeyboard == null)
165                || !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
166        final int languageOnSpacebarFormatType = LanguageOnSpacebarUtils
167                .getLanguageOnSpacebarFormatType(keyboard.mId.mSubtype);
168        final boolean hasMultipleEnabledIMEsOrSubtypes = mRichImm
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        if (DEBUG_ACTION) {
207            Log.d(TAG, "setAlphabetKeyboard");
208        }
209        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
210    }
211
212    // Implements {@link KeyboardState.SwitchActions}.
213    @Override
214    public void setAlphabetManualShiftedKeyboard() {
215        if (DEBUG_ACTION) {
216            Log.d(TAG, "setAlphabetManualShiftedKeyboard");
217        }
218        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
219    }
220
221    // Implements {@link KeyboardState.SwitchActions}.
222    @Override
223    public void setAlphabetAutomaticShiftedKeyboard() {
224        if (DEBUG_ACTION) {
225            Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard");
226        }
227        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
228    }
229
230    // Implements {@link KeyboardState.SwitchActions}.
231    @Override
232    public void setAlphabetShiftLockedKeyboard() {
233        if (DEBUG_ACTION) {
234            Log.d(TAG, "setAlphabetShiftLockedKeyboard");
235        }
236        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
237    }
238
239    // Implements {@link KeyboardState.SwitchActions}.
240    @Override
241    public void setAlphabetShiftLockShiftedKeyboard() {
242        if (DEBUG_ACTION) {
243            Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard");
244        }
245        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
246    }
247
248    // Implements {@link KeyboardState.SwitchActions}.
249    @Override
250    public void setSymbolsKeyboard() {
251        if (DEBUG_ACTION) {
252            Log.d(TAG, "setSymbolsKeyboard");
253        }
254        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
255    }
256
257    private void setMainKeyboardFrame(final SettingsValues settingsValues) {
258        final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE;
259        mKeyboardView.setVisibility(visibility);
260        // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
261        // @see #getVisibleKeyboardView() and
262        // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
263        mMainKeyboardFrame.setVisibility(visibility);
264        mEmojiPalettesView.setVisibility(View.GONE);
265        mEmojiPalettesView.stopEmojiPalettes();
266    }
267
268    // Implements {@link KeyboardState.SwitchActions}.
269    @Override
270    public void setEmojiKeyboard() {
271        if (DEBUG_ACTION) {
272            Log.d(TAG, "setEmojiKeyboard");
273        }
274        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
275        mMainKeyboardFrame.setVisibility(View.GONE);
276        // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
277        // @see #getVisibleKeyboardView() and
278        // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
279        mKeyboardView.setVisibility(View.GONE);
280        mEmojiPalettesView.startEmojiPalettes(
281                mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
282                mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
283        mEmojiPalettesView.setVisibility(View.VISIBLE);
284    }
285
286    public void onToggleEmojiKeyboard() {
287        final boolean needsToLoadKeyboard = (mKeyboardLayoutSet == null);
288        if (needsToLoadKeyboard || !isShowingEmojiPalettes()) {
289            mLatinIME.startShowingInputView(needsToLoadKeyboard);
290            setEmojiKeyboard();
291        } else {
292            mLatinIME.stopShowingInputView();
293            setAlphabetKeyboard();
294        }
295    }
296
297    // Implements {@link KeyboardState.SwitchActions}.
298    @Override
299    public void setSymbolsShiftedKeyboard() {
300        if (DEBUG_ACTION) {
301            Log.d(TAG, "setSymbolsShiftedKeyboard");
302        }
303        setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
304    }
305
306    // Future method for requesting an updating to the shift state.
307    @Override
308    public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode) {
309        if (DEBUG_ACTION) {
310            Log.d(TAG, "requestUpdatingShiftState: "
311                    + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags)
312                    + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode));
313        }
314        mState.onUpdateShiftState(autoCapsFlags, recapitalizeMode);
315    }
316
317    // Implements {@link KeyboardState.SwitchActions}.
318    @Override
319    public void startDoubleTapShiftKeyTimer() {
320        if (DEBUG_TIMER_ACTION) {
321            Log.d(TAG, "startDoubleTapShiftKeyTimer");
322        }
323        final MainKeyboardView keyboardView = getMainKeyboardView();
324        if (keyboardView != null) {
325            keyboardView.startDoubleTapShiftKeyTimer();
326        }
327    }
328
329    // Implements {@link KeyboardState.SwitchActions}.
330    @Override
331    public void cancelDoubleTapShiftKeyTimer() {
332        if (DEBUG_TIMER_ACTION) {
333            Log.d(TAG, "setAlphabetKeyboard");
334        }
335        final MainKeyboardView keyboardView = getMainKeyboardView();
336        if (keyboardView != null) {
337            keyboardView.cancelDoubleTapShiftKeyTimer();
338        }
339    }
340
341    // Implements {@link KeyboardState.SwitchActions}.
342    @Override
343    public boolean isInDoubleTapShiftKeyTimeout() {
344        if (DEBUG_TIMER_ACTION) {
345            Log.d(TAG, "isInDoubleTapShiftKeyTimeout");
346        }
347        final MainKeyboardView keyboardView = getMainKeyboardView();
348        return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
349    }
350
351    /**
352     * Updates state machine to figure out when to automatically switch back to the previous mode.
353     */
354    public void onEvent(final Event event, final int currentAutoCapsState,
355            final int currentRecapitalizeState) {
356        mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState);
357    }
358
359    public boolean isShowingEmojiPalettes() {
360        return mEmojiPalettesView != null && mEmojiPalettesView.isShown();
361    }
362
363    public boolean isShowingMoreKeysPanel() {
364        if (isShowingEmojiPalettes()) {
365            return false;
366        }
367        return mKeyboardView.isShowingMoreKeysPanel();
368    }
369
370    public View getVisibleKeyboardView() {
371        if (isShowingEmojiPalettes()) {
372            return mEmojiPalettesView;
373        }
374        return mKeyboardView;
375    }
376
377    public MainKeyboardView getMainKeyboardView() {
378        return mKeyboardView;
379    }
380
381    public void deallocateMemory() {
382        if (mKeyboardView != null) {
383            mKeyboardView.cancelAllOngoingEvents();
384            mKeyboardView.deallocateMemory();
385        }
386        if (mEmojiPalettesView != null) {
387            mEmojiPalettesView.stopEmojiPalettes();
388        }
389    }
390
391    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
392        if (mKeyboardView != null) {
393            mKeyboardView.closing();
394        }
395
396        updateKeyboardThemeAndContextThemeWrapper(
397                mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */));
398        mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
399                R.layout.input_view, null);
400        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
401        mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
402                R.id.emoji_palettes_view);
403
404        mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
405        mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
406        mKeyboardView.setKeyboardActionListener(mLatinIME);
407        mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
408                isHardwareAcceleratedDrawingEnabled);
409        mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
410        return mCurrentInputView;
411    }
412
413    public int getKeyboardShiftMode() {
414        final Keyboard keyboard = getKeyboard();
415        if (keyboard == null) {
416            return WordComposer.CAPS_MODE_OFF;
417        }
418        switch (keyboard.mId.mElementId) {
419        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
420        case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
421            return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
422        case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
423            return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
424        case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
425            return WordComposer.CAPS_MODE_AUTO_SHIFTED;
426        default:
427            return WordComposer.CAPS_MODE_OFF;
428        }
429    }
430
431    public int getCurrentKeyboardScriptId() {
432        if (null == mKeyboardLayoutSet) {
433            return ScriptUtils.SCRIPT_UNKNOWN;
434        }
435        return mKeyboardLayoutSet.getScriptId();
436    }
437}
438