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