OpenWnnEN.java revision 59aefa242169b7a51c2381daee58ff22fd1834ce
1/*
2 * Copyright (C) 2008-2012  OMRON SOFTWARE Co., Ltd.
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 jp.co.omronsoft.openwnn;
18
19import jp.co.omronsoft.openwnn.EN.*;
20import android.content.SharedPreferences;
21import android.content.Context;
22import android.content.res.Configuration;
23import android.os.Message;
24import android.os.Handler;
25import android.preference.PreferenceManager;
26import android.text.SpannableStringBuilder;
27import android.text.Spanned;
28import android.text.method.MetaKeyKeyListener;
29import android.text.style.BackgroundColorSpan;
30import android.text.style.CharacterStyle;
31import android.text.style.ForegroundColorSpan;
32import android.text.style.UnderlineSpan;
33import android.util.Log;
34import android.view.KeyCharacterMap;
35import android.view.KeyEvent;
36import android.view.MotionEvent;
37import android.view.View;
38import android.view.inputmethod.EditorInfo;
39
40/**
41 * The OpenWnn English IME class.
42 *
43 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
44 */
45public class OpenWnnEN extends OpenWnn {
46    /** A space character */
47    private static final char[] SPACE = {' '};
48
49    /** Character style of underline */
50    private static final CharacterStyle SPAN_UNDERLINE   = new UnderlineSpan();
51    /** Highlight color style for the selected string  */
52    private static final CharacterStyle SPAN_EXACT_BGCOLOR_HL     = new BackgroundColorSpan(0xFF66CDAA);
53    /** Highlight color style for the composing text */
54    private static final CharacterStyle SPAN_REMAIN_BGCOLOR_HL    = new BackgroundColorSpan(0xFFF0FFFF);
55    /** Highlight text color */
56    private static final CharacterStyle SPAN_TEXTCOLOR  = new ForegroundColorSpan(0xFF000000);
57
58    /** A private area code(ALT+SHIFT+X) to be ignore (G1 specific). */
59    private static final int PRIVATE_AREA_CODE = 61184;
60    /** Never move cursor in to the composing text (adapting to IMF's specification change) */
61    private static final boolean FIX_CURSOR_TEXT_END = true;
62
63    /** Spannable string for the composing text */
64    protected SpannableStringBuilder mDisplayText;
65
66    /** Characters treated as a separator */
67    private String mWordSeparators;
68    /** Previous event's code */
69    private int mPreviousEventCode;
70
71    /** Array of words from the user dictionary */
72    private WnnWord[] mUserDictionaryWords = null;
73
74    /** The converter for English prediction/spell correction */
75    private OpenWnnEngineEN mConverterEN;
76    /** The symbol list generator */
77    private SymbolList mSymbolList;
78    /** Whether it is displaying symbol list */
79    private boolean mSymbolMode;
80    /** Whether prediction is enabled */
81    private boolean mOptPrediction;
82    /** Whether spell correction is enabled */
83    private boolean mOptSpellCorrection;
84    /** Whether learning is enabled */
85    private boolean mOptLearning;
86
87    /** SHIFT key state */
88    private int mHardShift;
89    /** SHIFT key state (pressing) */
90    private boolean mShiftPressing;
91    /** ALT key state */
92    private int mHardAlt;
93    /** ALT key state (pressing) */
94    private boolean mAltPressing;
95
96    /** Instance of this service */
97    private static OpenWnnEN mSelf = null;
98
99    /** Shift lock toggle definition */
100    private static final int[] mShiftKeyToggle = {0, MetaKeyKeyListener.META_SHIFT_ON, MetaKeyKeyListener.META_CAP_LOCKED};
101    /** ALT lock toggle definition */
102    private static final int[] mAltKeyToggle = {0, MetaKeyKeyListener.META_ALT_ON, MetaKeyKeyListener.META_ALT_LOCKED};
103    /** Auto caps mode */
104    private boolean mAutoCaps = false;
105
106    /** Whether dismissing the keyboard when the enter key is pressed */
107    private boolean mEnableAutoHideKeyboard = true;
108
109    /** Tutorial */
110    private TutorialEN mTutorial;
111
112    /** Whether tutorial mode or not */
113    private boolean mEnableTutorial;
114
115    /** Message for {@code mHandler} (execute prediction) */
116    private static final int MSG_PREDICTION = 0;
117
118    /** Message for {@code mHandler} (execute tutorial) */
119    private static final int MSG_START_TUTORIAL = 1;
120
121    /** Message for {@code mHandler} (close) */
122    private static final int MSG_CLOSE = 2;
123
124    /** Delay time(msec.) to start prediction after key input when the candidates view is not shown. */
125    private static final int PREDICTION_DELAY_MS_1ST = 200;
126
127    /** Delay time(msec.) to start prediction after key input when the candidates view is shown. */
128    private static final int PREDICTION_DELAY_MS_SHOWING_CANDIDATE = 200;
129
130    /** {@code Handler} for drawing candidates/displaying tutorial */
131    Handler mHandler = new Handler() {
132            @Override public void handleMessage(Message msg) {
133                switch (msg.what) {
134                case MSG_PREDICTION:
135                    updatePrediction();
136                    break;
137                case MSG_START_TUTORIAL:
138                    if (mTutorial == null) {
139                        if (isInputViewShown()) {
140                            DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
141                            View v = inputManager.getKeyboardView();
142                            mTutorial = new TutorialEN(OpenWnnEN.this, v, inputManager);
143
144                            mTutorial.start();
145                        } else {
146                            /* Try again soon if the view is not yet showing */
147                            sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
148                        }
149                    }
150                    break;
151                case MSG_CLOSE:
152                    if (mConverterEN != null) mConverterEN.close();
153                    if (mSymbolList != null) mSymbolList.close();
154                    break;
155                }
156            }
157        };
158
159    /**
160     * Constructor
161     */
162    public OpenWnnEN() {
163        super();
164        mSelf = this;
165
166        /* used by OpenWnn */
167        mComposingText = new ComposingText();
168        mCandidatesViewManager = new TextCandidatesViewManager(-1);
169        mInputViewManager = new DefaultSoftKeyboardEN();
170
171        if (OpenWnn.getCurrentIme() != null) {
172            if (mConverterEN == null) {
173                mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
174            }
175        }
176
177        mConverter = mConverterEN;
178        mSymbolList = null;
179
180        /* etc */
181        mDisplayText = new SpannableStringBuilder();
182        mAutoHideMode = false;
183        mSymbolMode = false;
184        mOptPrediction = true;
185        mOptSpellCorrection = true;
186        mOptLearning = true;
187    }
188
189    /**
190     * Constructor
191     *
192     * @param context       The context
193     */
194    public OpenWnnEN(Context context) {
195        this();
196        attachBaseContext(context);
197    }
198    /**
199     * Get the instance of this service.
200     * <br>
201     * Before using this method, the constructor of this service must be invoked.
202     *
203     * @return      The instance of this object
204     */
205    public static OpenWnnEN getInstance() {
206        return mSelf;
207    }
208
209    /**
210     * Insert a character into the composing text.
211     *
212     * @param chars     A array of character
213     */
214    private void insertCharToComposingText(char[] chars) {
215        StrSegment seg = new StrSegment(chars);
216
217        if (chars[0] == SPACE[0] || chars[0] == '\u0009') {
218            /* if the character is a space, commit the composing text */
219            commitText(1);
220            commitText(seg.string);
221            mComposingText.clear();
222        } else if (mWordSeparators.contains(seg.string)) {
223            /* if the character is a separator, remove an auto-inserted space and commit the composing text. */
224            if (mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
225                mInputConnection.deleteSurroundingText(1, 0);
226            }
227            commitText(1);
228            commitText(seg.string);
229            mComposingText.clear();
230        } else {
231            mComposingText.insertStrSegment(0, 1, seg);
232            updateComposingText(1);
233        }
234    }
235
236    /**
237     * Insert a character into the composing text.
238     *
239     * @param charCode      A character code
240     * @return              {@code true} if success; {@code false} if an error occurs.
241     */
242    private boolean insertCharToComposingText(int charCode) {
243        if (charCode == 0) {
244            return false;
245        }
246        insertCharToComposingText(Character.toChars(charCode));
247        return true;
248    }
249
250    /**
251     * Get the shift key state from the editor.
252     *
253     * @param editor    Editor
254     *
255     * @return          State ID of the shift key (0:off, 1:on)
256     */
257    protected int getShiftKeyState(EditorInfo editor) {
258        return (getCurrentInputConnection().getCursorCapsMode(editor.inputType) == 0) ? 0 : 1;
259    }
260
261    /**
262     * Set the mode of the symbol list.
263     *
264     * @param mode      {@code SymbolList.SYMBOL_ENGLISH} or {@code null}.
265     */
266    private void setSymbolMode(String mode) {
267        if (mode != null) {
268            mHandler.removeMessages(MSG_PREDICTION);
269            mSymbolMode = true;
270            mSymbolList.setDictionary(mode);
271            mConverter = mSymbolList;
272        } else {
273            if (!mSymbolMode) {
274                return;
275            }
276            mHandler.removeMessages(MSG_PREDICTION);
277            mSymbolMode = false;
278            mConverter = mConverterEN;
279        }
280    }
281
282    /***********************************************************************
283     * InputMethodServer
284     ***********************************************************************/
285    /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreate */
286    @Override public void onCreate() {
287        super.onCreate();
288        mWordSeparators = getResources().getString(R.string.en_word_separators);
289
290        if (mConverterEN == null) {
291            mConverterEN = new OpenWnnEngineEN("/data/data/jp.co.omronsoft.openwnn/writableEN.dic");
292        }
293
294        if (mSymbolList == null) {
295            mSymbolList = new SymbolList(this, SymbolList.LANG_EN);
296        }
297    }
298
299    /** @see jp.co.omronsoft.openwnn.OpenWnn#onCreateInputView */
300    @Override public View onCreateInputView() {
301        int hiddenState = getResources().getConfiguration().hardKeyboardHidden;
302        boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
303        ((DefaultSoftKeyboardEN) mInputViewManager).setHardKeyboardHidden(hidden);
304        mEnableTutorial = hidden;
305
306        return super.onCreateInputView();
307    }
308
309    /** @see jp.co.omronsoft.openwnn.OpenWnn#onStartInputView */
310    @Override public void onStartInputView(EditorInfo attribute, boolean restarting) {
311        super.onStartInputView(attribute, restarting);
312
313        /* initialize views */
314        mCandidatesViewManager.clearCandidates();
315        mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
316
317        mHardShift = 0;
318        mHardAlt   = 0;
319        updateMetaKeyStateDisplay();
320
321        /* load preferences */
322        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
323
324        /* auto caps mode */
325        mAutoCaps = pref.getBoolean("auto_caps", true);
326
327        /* set TextCandidatesViewManager's option */
328        ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(true);
329
330        /* display status icon */
331        showStatusIcon(R.drawable.immodeic_half_alphabet);
332
333        if (mComposingText != null) {
334            mComposingText.clear();
335        }
336        /* initialize the engine's state */
337        fitInputType(pref, attribute);
338
339        ((DefaultSoftKeyboard) mInputViewManager).resetCurrentKeyboard();
340
341        if (OpenWnn.isXLarge()) {
342            mTextCandidatesViewManager.setPreferences(pref);
343        }
344    }
345
346    /** @see jp.co.omronsoft.openwnn.OpenWnn#hideWindow */
347    @Override public void hideWindow() {
348        ((BaseInputView)((DefaultSoftKeyboard) mInputViewManager).getCurrentView()).closeDialog();
349        mComposingText.clear();
350        mInputViewManager.onUpdateState(this);
351        mHandler.removeMessages(MSG_START_TUTORIAL);
352        mInputViewManager.closing();
353        if (mTutorial != null) {
354            mTutorial.close();
355            mTutorial = null;
356        }
357
358        super.hideWindow();
359    }
360
361    /** @see jp.co.omronsoft.openwnn.OpenWnn#onUpdateSelection */
362    @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd,
363            int newSelStart, int newSelEnd, int candidatesStart,
364            int candidatesEnd) {
365
366        boolean isNotComposing = ((candidatesStart < 0) && (candidatesEnd < 0));
367        if (isNotComposing) {
368            mComposingText.clear();
369            updateComposingText(1);
370        } else {
371            if (mComposingText.size(1) != 0) {
372                updateComposingText(1);
373            }
374        }
375    }
376
377    /** @see jp.co.omronsoft.openwnn.OpenWnn#onConfigurationChanged */
378    @Override public void onConfigurationChanged(Configuration newConfig) {
379        try {
380            super.onConfigurationChanged(newConfig);
381            if (mInputConnection != null) {
382                updateComposingText(1);
383            }
384            /* Hardware keyboard */
385            int hiddenState = newConfig.hardKeyboardHidden;
386            boolean hidden = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
387            mEnableTutorial = hidden;
388        } catch (Exception ex) {
389        }
390    }
391
392    /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateFullscreenMode */
393    @Override public boolean onEvaluateFullscreenMode() {
394        return false;
395    }
396
397    /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvaluateInputViewShown */
398    @Override public boolean onEvaluateInputViewShown() {
399        return true;
400    }
401
402    /***********************************************************************
403     * OpenWnn
404     ***********************************************************************/
405    /** @see jp.co.omronsoft.openwnn.OpenWnn#onEvent */
406    @Override synchronized public boolean onEvent(OpenWnnEvent ev) {
407        /* handling events which are valid when InputConnection is not active. */
408        switch (ev.code) {
409
410        case OpenWnnEvent.KEYUP:
411            onKeyUpEvent(ev.keyEvent);
412            return true;
413
414        case OpenWnnEvent.INITIALIZE_LEARNING_DICTIONARY:
415            return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_LEARN );
416
417        case OpenWnnEvent.INITIALIZE_USER_DICTIONARY:
418            return mConverterEN.initializeDictionary( WnnEngine.DICTIONARY_TYPE_USER );
419
420        case OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY:
421            mUserDictionaryWords = mConverterEN.getUserDictionaryWords( );
422            return true;
423
424        case OpenWnnEvent.GET_WORD:
425            if( mUserDictionaryWords != null ) {
426                ev.word = mUserDictionaryWords[ 0 ];
427                for( int i = 0 ; i < mUserDictionaryWords.length-1 ; i++ ) {
428                    mUserDictionaryWords[ i ] = mUserDictionaryWords[ i + 1 ];
429                }
430                mUserDictionaryWords[ mUserDictionaryWords.length-1 ] = null;
431                if( mUserDictionaryWords[ 0 ] == null ) {
432                    mUserDictionaryWords = null;
433                }
434                return true;
435            }
436            break;
437
438        case OpenWnnEvent.ADD_WORD:
439            mConverterEN.addWord(ev.word);
440            return true;
441
442        case OpenWnnEvent.DELETE_WORD:
443            mConverterEN.deleteWord(ev.word);
444            return true;
445
446        case OpenWnnEvent.CHANGE_MODE:
447            return false;
448
449        case OpenWnnEvent.UPDATE_CANDIDATE:
450            updateComposingText(ComposingText.LAYER1);
451            return true;
452
453        case OpenWnnEvent.CHANGE_INPUT_VIEW:
454            setInputView(onCreateInputView());
455            return true;
456
457        case OpenWnnEvent.CANDIDATE_VIEW_TOUCH:
458            boolean ret;
459                ret = ((TextCandidatesViewManager)mCandidatesViewManager).onTouchSync();
460            return ret;
461
462        default:
463            break;
464        }
465
466        dismissPopupKeyboard();
467        KeyEvent keyEvent = ev.keyEvent;
468        int keyCode = 0;
469        if (keyEvent != null) {
470            keyCode = keyEvent.getKeyCode();
471        }
472        if (mDirectInputMode) {
473            if (ev.code == OpenWnnEvent.INPUT_SOFT_KEY && mInputConnection != null) {
474                mInputConnection.sendKeyEvent(keyEvent);
475                mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
476                                                           keyEvent.getKeyCode()));
477            }
478            return false;
479        }
480
481        if (ev.code == OpenWnnEvent.LIST_CANDIDATES_FULL) {
482            mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_FULL);
483            return true;
484        } else if (ev.code == OpenWnnEvent.LIST_CANDIDATES_NORMAL) {
485            mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
486            return true;
487        }
488
489        boolean ret = false;
490        switch (ev.code) {
491        case OpenWnnEvent.INPUT_CHAR:
492             ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
493            EditorInfo edit = getCurrentInputEditorInfo();
494            if( edit.inputType == EditorInfo.TYPE_CLASS_PHONE){
495                commitText(new String(ev.chars));
496            }else{
497                setSymbolMode(null);
498                insertCharToComposingText(ev.chars);
499                ret = true;
500                mPreviousEventCode = ev.code;
501            }
502            break;
503
504        case OpenWnnEvent.INPUT_KEY:
505            keyCode = ev.keyEvent.getKeyCode();
506            /* update shift/alt state */
507            switch (keyCode) {
508            case KeyEvent.KEYCODE_ALT_LEFT:
509            case KeyEvent.KEYCODE_ALT_RIGHT:
510                if (ev.keyEvent.getRepeatCount() == 0) {
511                    if (++mHardAlt > 2) { mHardAlt = 0; }
512                }
513                mAltPressing   = true;
514                updateMetaKeyStateDisplay();
515                return true;
516
517            case KeyEvent.KEYCODE_SHIFT_LEFT:
518            case KeyEvent.KEYCODE_SHIFT_RIGHT:
519                if (ev.keyEvent.getRepeatCount() == 0) {
520                    if (++mHardShift > 2) { mHardShift = 0; }
521                }
522                mShiftPressing = true;
523                updateMetaKeyStateDisplay();
524                return true;
525            }
526            setSymbolMode(null);
527            updateComposingText(1);
528            /* handle other key event */
529            ret = processKeyEvent(ev.keyEvent);
530            mPreviousEventCode = ev.code;
531            break;
532
533        case OpenWnnEvent.INPUT_SOFT_KEY:
534            setSymbolMode(null);
535            updateComposingText(1);
536            ret = processKeyEvent(ev.keyEvent);
537            if (!ret) {
538            	int code = keyEvent.getKeyCode();
539            	if (code == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER) {
540                    sendKeyChar('\n');
541            	} else {
542                    mInputConnection.sendKeyEvent(keyEvent);
543                    mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, code));
544            	}
545                ret = true;
546            }
547            mPreviousEventCode = ev.code;
548            break;
549
550        case OpenWnnEvent.SELECT_CANDIDATE:
551            if (mSymbolMode) {
552                commitText(ev.word, false);
553            } else {
554                if (mWordSeparators.contains(ev.word.candidate) &&
555                    mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
556                    mInputConnection.deleteSurroundingText(1, 0);
557                }
558                commitText(ev.word, true);
559            }
560            mComposingText.clear();
561            mPreviousEventCode = ev.code;
562            updateComposingText(1);
563            break;
564
565        case OpenWnnEvent.LIST_SYMBOLS:
566            commitText(1);
567            mComposingText.clear();
568            setSymbolMode(SymbolList.SYMBOL_ENGLISH);
569            updateComposingText(1);
570            break;
571
572        default:
573            break;
574        }
575
576        if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
577        	mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
578        }
579
580        return ret;
581    }
582
583    /***********************************************************************
584     * OpenWnnEN
585     ***********************************************************************/
586    /**
587     * Handling KeyEvent
588     * <br>
589     * This method is called from {@link #onEvent()}.
590     *
591     * @param ev   A key event
592     * @return      {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method
593     */
594    private boolean processKeyEvent(KeyEvent ev) {
595
596        int key = ev.getKeyCode();
597        EditorInfo edit = getCurrentInputEditorInfo();
598        /* keys which produce a glyph */
599        if (ev.isPrintingKey()) {
600            /* do nothing if the character is not able to display or the character is dead key */
601            if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) {
602                int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON);
603                if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) {
604                    if(mHardShift == 1){
605                        mShiftPressing = false;
606                    }
607                    if(mHardAlt == 1){
608                        mAltPressing   = false;
609                    }
610                    if(!ev.isAltPressed()){
611                        if (mHardAlt == 1) {
612                            mHardAlt = 0;
613                        }
614                    }
615                    if(!ev.isShiftPressed()){
616                        if (mHardShift == 1) {
617                            mHardShift = 0;
618                        }
619                    }
620                    if(!ev.isShiftPressed() && !ev.isAltPressed()){
621                        updateMetaKeyStateDisplay();
622                    }
623                    return true;
624                }
625            }
626
627            ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
628
629            /* get the key character */
630            if (mHardShift== 0  && mHardAlt == 0) {
631                /* no meta key is locked */
632                int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0;
633                if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) {
634                    /* handling auto caps for a alphabet character */
635                    insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
636                } else {
637                    insertCharToComposingText(ev.getUnicodeChar());
638                }
639            } else {
640                insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
641                                                            | mAltKeyToggle[mHardAlt]));
642                if(mHardShift == 1){
643                    mShiftPressing = false;
644                }
645                if(mHardAlt == 1){
646                    mAltPressing   = false;
647                }
648                /* back to 0 (off) if 1 (on/not locked) */
649                if(!ev.isAltPressed()){
650                    if (mHardAlt == 1) {
651                        mHardAlt = 0;
652                    }
653                }
654                if(!ev.isShiftPressed()){
655                    if (mHardShift == 1) {
656                        mHardShift = 0;
657                    }
658                }
659                if(!ev.isShiftPressed() && !ev.isAltPressed()){
660                    updateMetaKeyStateDisplay();
661                }
662            }
663
664            if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) {
665                commitText(1);
666                mComposingText.clear();
667                return true;
668            }
669            return true;
670
671        } else if (key == KeyEvent.KEYCODE_SPACE) {
672            if (ev.isAltPressed()) {
673                /* display the symbol list (G1 specific. same as KEYCODE_SYM) */
674                commitText(1);
675                mComposingText.clear();
676                setSymbolMode(SymbolList.SYMBOL_ENGLISH);
677                updateComposingText(1);
678                mHardAlt = 0;
679                updateMetaKeyStateDisplay();
680            } else {
681                insertCharToComposingText(SPACE);
682            }
683            return true;
684        } else if (key == KeyEvent.KEYCODE_SYM) {
685            /* display the symbol list */
686            commitText(1);
687            mComposingText.clear();
688            setSymbolMode(SymbolList.SYMBOL_ENGLISH);
689            updateComposingText(1);
690            mHardAlt = 0;
691            updateMetaKeyStateDisplay();
692        }
693
694
695        /* Functional key */
696        if (mComposingText.size(1) > 0) {
697            switch (key) {
698            case KeyEvent.KEYCODE_DEL:
699                mComposingText.delete(1, false);
700                updateComposingText(1);
701                return true;
702
703            case KeyEvent.KEYCODE_BACK:
704                if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
705                    mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
706                } else {
707                    mComposingText.clear();
708                    updateComposingText(1);
709                }
710                return true;
711
712            case KeyEvent.KEYCODE_DPAD_LEFT:
713                mComposingText.moveCursor(1, -1);
714                updateComposingText(1);
715                return true;
716
717            case KeyEvent.KEYCODE_DPAD_RIGHT:
718                mComposingText.moveCursor(1, 1);
719                updateComposingText(1);
720                return true;
721
722            case KeyEvent.KEYCODE_ENTER:
723            case KeyEvent.KEYCODE_NUMPAD_ENTER:
724            case KeyEvent.KEYCODE_DPAD_CENTER:
725                commitText(1);
726                mComposingText.clear();
727                if (mEnableAutoHideKeyboard) {
728                    mInputViewManager.closing();
729                    requestHideSelf(0);
730                }
731                return true;
732
733            default:
734                return !isThroughKeyCode(key);
735            }
736        } else {
737            /* if there is no composing string. */
738            if (mCandidatesViewManager.getCurrentView().isShown()) {
739            	if (key == KeyEvent.KEYCODE_BACK) {
740            		if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
741            			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
742            		} else {
743            			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
744            		}
745            		return true;
746            	}
747            } else {
748                switch (key) {
749                case KeyEvent.KEYCODE_DPAD_CENTER:
750                case KeyEvent.KEYCODE_ENTER:
751                case KeyEvent.KEYCODE_NUMPAD_ENTER:
752                    if (mEnableAutoHideKeyboard) {
753                        mInputViewManager.closing();
754                        requestHideSelf(0);
755                        return true;
756                    }
757                    break;
758                case KeyEvent.KEYCODE_BACK:
759                    /*
760                     * If 'BACK' key is pressed when the SW-keyboard is shown
761                     * and the candidates view is not shown, dismiss the SW-keyboard.
762                     */
763                    if (isInputViewShown()) {
764                        mInputViewManager.closing();
765                        requestHideSelf(0);
766                        return true;
767                    }
768                    break;
769                default:
770                    break;
771                }
772            }
773        }
774
775        return false;
776    }
777
778    /**
779     * Thread for updating the candidates view
780     */
781    private void updatePrediction() {
782        int candidates = 0;
783        if (mConverter != null) {
784            /* normal prediction */
785            candidates = mConverter.predict(mComposingText, 0, -1);
786        }
787        /* update the candidates view */
788        if (candidates > 0) {
789            mCandidatesViewManager.displayCandidates(mConverter);
790        } else {
791            mCandidatesViewManager.clearCandidates();
792        }
793    }
794
795    /**
796     * Update the composing text.
797     *
798     * @param layer  {@link mComposingText}'s layer to display
799     */
800    private void updateComposingText(int layer) {
801        /* update the candidates view */
802        if (!mOptPrediction) {
803            commitText(1);
804            mComposingText.clear();
805            if (mSymbolMode) {
806                mHandler.removeMessages(MSG_PREDICTION);
807                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
808            }
809        } else {
810            if (mComposingText.size(1) != 0) {
811                mHandler.removeMessages(MSG_PREDICTION);
812                if (mCandidatesViewManager.getCurrentView().isShown()) {
813                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
814                                                PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
815                } else {
816                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
817                                                PREDICTION_DELAY_MS_1ST);
818                }
819            } else {
820                mHandler.removeMessages(MSG_PREDICTION);
821                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
822            }
823
824            /* notice to the input view */
825            this.mInputViewManager.onUpdateState(this);
826
827            /* set the text for displaying as the composing text */
828            SpannableStringBuilder disp = mDisplayText;
829            disp.clear();
830            disp.insert(0, mComposingText.toString(layer));
831
832            /* add decoration to the text */
833            int cursor = mComposingText.getCursor(layer);
834            if (disp.length() != 0) {
835                if (cursor > 0 && cursor < disp.length()) {
836                    disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
837                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
838                }
839                if (cursor < disp.length()) {
840                    mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(),
841                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
842                    mDisplayText.setSpan(SPAN_TEXTCOLOR, 0, disp.length(),
843                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
844                }
845
846                disp.setSpan(SPAN_UNDERLINE, 0, disp.length(),
847                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
848            }
849
850            int displayCursor = cursor;
851            if (FIX_CURSOR_TEXT_END) {
852                displayCursor = (cursor == 0) ?  0 : 1;
853            }
854            /* update the composing text on the EditView */
855            mInputConnection.setComposingText(disp, displayCursor);
856        }
857    }
858
859    /**
860     * Commit the composing text.
861     *
862     * @param layer  {@link mComposingText}'s layer to commit.
863     */
864    private void commitText(int layer) {
865        String tmp = mComposingText.toString(layer);
866
867        if (mOptLearning && mConverter != null && tmp.length() > 0) {
868            WnnWord word = new WnnWord(tmp, tmp);
869            mConverter.learn(word);
870        }
871
872        mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length()));
873        mCandidatesViewManager.clearCandidates();
874    }
875
876    /**
877     * Commit a word
878     *
879     * @param word          A word to commit
880     * @param withSpace     Append a space after the word if {@code true}.
881     */
882    private void commitText(WnnWord word, boolean withSpace) {
883
884        if (mOptLearning && mConverter != null) {
885            mConverter.learn(word);
886        }
887
888        mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length()));
889
890        if (withSpace) {
891            commitText(" ");
892        }
893    }
894
895    /**
896     * Commit a string
897     * <br>
898     * The string is not registered into the learning dictionary.
899     *
900     * @param str  A string to commit
901     */
902    private void commitText(String str) {
903        mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
904        mCandidatesViewManager.clearCandidates();
905    }
906
907    /**
908     * Dismiss the pop-up keyboard
909     */
910    protected void dismissPopupKeyboard() {
911        DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager;
912        if (kbd != null) {
913            kbd.dismissPopupKeyboard();
914        }
915    }
916
917    /**
918     * Display current meta-key state.
919     */
920    private void updateMetaKeyStateDisplay() {
921        int mode = 0;
922        if(mHardShift == 0 && mHardAlt == 0){
923            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
924        }else if(mHardShift == 1 && mHardAlt == 0){
925            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF;
926        }else if(mHardShift == 2  && mHardAlt == 0){
927            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
928        }else if(mHardShift == 0 && mHardAlt == 1){
929            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON;
930        }else if(mHardShift == 0 && mHardAlt == 2){
931            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK;
932        }else if(mHardShift == 1 && mHardAlt == 1){
933            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON;
934        }else if(mHardShift == 1 && mHardAlt == 2){
935            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK;
936        }else if(mHardShift == 2 && mHardAlt == 1){
937            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON;
938        }else if(mHardShift == 2 && mHardAlt == 2){
939            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK;
940        }else{
941            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
942        }
943
944        ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
945    }
946
947    /**
948     * Handling KeyEvent(KEYUP)
949     * <br>
950     * This method is called from {@link #onEvent()}.
951     *
952     * @param ev   An up key event
953     */
954    private void onKeyUpEvent(KeyEvent ev) {
955        int key = ev.getKeyCode();
956        if(!mShiftPressing){
957            if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){
958                mHardShift = 0;
959                mShiftPressing = true;
960                updateMetaKeyStateDisplay();
961            }
962        }
963        if(!mAltPressing ){
964            if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
965                mHardAlt = 0;
966                mAltPressing   = true;
967                updateMetaKeyStateDisplay();
968            }
969        }
970    }
971    /**
972     * Fits an editor info.
973     *
974     * @param preferences  The preference data.
975     * @param info          The editor info.
976     */
977    private void fitInputType(SharedPreferences preference, EditorInfo info) {
978        if (info.inputType == EditorInfo.TYPE_NULL) {
979            mDirectInputMode = true;
980            return;
981        }
982
983        mEnableAutoHideKeyboard = false;
984
985        /* set prediction & spell correction mode */
986        mOptPrediction      = preference.getBoolean("opt_en_prediction", true);
987        mOptSpellCorrection = preference.getBoolean("opt_en_spell_correction", true);
988        mOptLearning        = preference.getBoolean("opt_en_enable_learning", true);
989
990        /* prediction on/off */
991        switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
992        case EditorInfo.TYPE_CLASS_NUMBER:
993        case EditorInfo.TYPE_CLASS_DATETIME:
994        case EditorInfo.TYPE_CLASS_PHONE:
995            mOptPrediction = false;
996            mOptLearning = false;
997            break;
998
999        case EditorInfo.TYPE_CLASS_TEXT:
1000            switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
1001            case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD:
1002            case EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
1003                mOptLearning = false;
1004                mOptPrediction = false;
1005                break;
1006
1007            case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
1008                mOptLearning = false;
1009                mOptPrediction = false;
1010                break;
1011            default:
1012                break;
1013            }
1014        }
1015
1016        /* doesn't learn any word if it is not prediction mode */
1017        if (!mOptPrediction) {
1018            mOptLearning = false;
1019        }
1020
1021        /* set engine's mode */
1022        if (mOptSpellCorrection) {
1023            mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
1024        } else {
1025            mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
1026        }
1027        checkTutorial(info.privateImeOptions);
1028    }
1029
1030    /**
1031     * Check and start the tutorial if it is the tutorial mode.
1032     *
1033     * @param privateImeOptions IME's options
1034     */
1035    private void checkTutorial(String privateImeOptions) {
1036        if (privateImeOptions == null) return;
1037        if (privateImeOptions.equals("com.google.android.setupwizard:ShowTutorial")) {
1038            if ((mTutorial == null) && mEnableTutorial) startTutorial();
1039        } else if (privateImeOptions.equals("com.google.android.setupwizard:HideTutorial")) {
1040            if (mTutorial != null) {
1041                if (mTutorial.close()) {
1042                    mTutorial = null;
1043                }
1044            }
1045        }
1046    }
1047
1048    /**
1049     * Start the tutorial
1050     */
1051    private void startTutorial() {
1052        DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
1053        View v = inputManager.getKeyboardView();
1054        v.setOnTouchListener(new View.OnTouchListener() {
1055				public boolean onTouch(View v, MotionEvent event) {
1056					return true;
1057				}});
1058        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
1059    }
1060
1061    /**
1062     * Close the tutorial
1063     */
1064    public void tutorialDone() {
1065        mTutorial = null;
1066    }
1067
1068    /** @see OpenWnn#close */
1069    @Override protected void close() {
1070        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
1071    }
1072}
1073