OpenWnnEN.java revision e83971e4dafd53e9fb00386355afdbae7e00c0cf
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                mInputConnection.sendKeyEvent(ev.keyEvent);
524                mInputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, ev.keyEvent.getKeyCode()));
525                ret = true;
526            }
527            mPreviousEventCode = ev.code;
528            break;
529
530        case OpenWnnEvent.SELECT_CANDIDATE:
531            if (mSymbolMode) {
532                commitText(ev.word, false);
533            } else {
534                if (mWordSeparators.contains(ev.word.candidate) &&
535                    mPreviousEventCode == OpenWnnEvent.SELECT_CANDIDATE) {
536                    mInputConnection.deleteSurroundingText(1, 0);
537                }
538                commitText(ev.word, true);
539            }
540            mComposingText.clear();
541            mPreviousEventCode = ev.code;
542            updateComposingText(1);
543            break;
544
545        case OpenWnnEvent.LIST_SYMBOLS:
546            commitText(1);
547            mComposingText.clear();
548            setSymbolMode(SymbolList.SYMBOL_ENGLISH);
549            updateComposingText(1);
550            break;
551
552        default:
553            break;
554        }
555
556        if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
557        	mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
558        }
559
560        return ret;
561    }
562
563    /***********************************************************************
564     * OpenWnnEN
565     ***********************************************************************/
566    /**
567     * Handling KeyEvent
568     * <br>
569     * This method is called from {@link #onEvent()}.
570     *
571     * @param ev   A key event
572     * @return      {@code true} if the event is processed in this method; {@code false} if the event is not processed in this method
573     */
574    private boolean processKeyEvent(KeyEvent ev) {
575
576        int key = ev.getKeyCode();
577        EditorInfo edit = getCurrentInputEditorInfo();
578        /* keys which produce a glyph */
579        if (ev.isPrintingKey()) {
580            /* do nothing if the character is not able to display or the character is dead key */
581            if ((mHardShift > 0 && mHardAlt > 0) || (ev.isAltPressed() && ev.isShiftPressed())) {
582                int charCode = ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON | MetaKeyKeyListener.META_ALT_ON);
583                if (charCode == 0 || (charCode & KeyCharacterMap.COMBINING_ACCENT) != 0 || charCode == PRIVATE_AREA_CODE) {
584                    if(mHardShift == 1){
585                        mShiftPressing = false;
586                    }
587                    if(mHardAlt == 1){
588                        mAltPressing   = false;
589                    }
590                    if(!ev.isAltPressed()){
591                        if (mHardAlt == 1) {
592                            mHardAlt = 0;
593                        }
594                    }
595                    if(!ev.isShiftPressed()){
596                        if (mHardShift == 1) {
597                            mHardShift = 0;
598                        }
599                    }
600                    if(!ev.isShiftPressed() && !ev.isAltPressed()){
601                        updateMetaKeyStateDisplay();
602                    }
603                    return true;
604                }
605            }
606
607            ((TextCandidatesViewManager)mCandidatesViewManager).setAutoHide(false);
608
609            /* get the key character */
610            if (mHardShift== 0  && mHardAlt == 0) {
611                /* no meta key is locked */
612                int shift = (mAutoCaps) ? getShiftKeyState(edit) : 0;
613                if (shift != mHardShift && (key >= KeyEvent.KEYCODE_A && key <= KeyEvent.KEYCODE_Z)) {
614                    /* handling auto caps for a alphabet character */
615                    insertCharToComposingText(ev.getUnicodeChar(MetaKeyKeyListener.META_SHIFT_ON));
616                } else {
617                    insertCharToComposingText(ev.getUnicodeChar());
618                }
619            } else {
620                insertCharToComposingText(ev.getUnicodeChar(mShiftKeyToggle[mHardShift]
621                                                            | mAltKeyToggle[mHardAlt]));
622                if(mHardShift == 1){
623                    mShiftPressing = false;
624                }
625                if(mHardAlt == 1){
626                    mAltPressing   = false;
627                }
628                /* back to 0 (off) if 1 (on/not locked) */
629                if(!ev.isAltPressed()){
630                    if (mHardAlt == 1) {
631                        mHardAlt = 0;
632                    }
633                }
634                if(!ev.isShiftPressed()){
635                    if (mHardShift == 1) {
636                        mHardShift = 0;
637                    }
638                }
639                if(!ev.isShiftPressed() && !ev.isAltPressed()){
640                    updateMetaKeyStateDisplay();
641                }
642            }
643
644            if (edit.inputType == EditorInfo.TYPE_CLASS_PHONE) {
645                commitText(1);
646                mComposingText.clear();
647                return true;
648            }
649            return true;
650
651        } else if (key == KeyEvent.KEYCODE_SPACE) {
652            if (ev.isAltPressed()) {
653                /* display the symbol list (G1 specific. same as KEYCODE_SYM) */
654                commitText(1);
655                mComposingText.clear();
656                setSymbolMode(SymbolList.SYMBOL_ENGLISH);
657                updateComposingText(1);
658                mHardAlt = 0;
659                updateMetaKeyStateDisplay();
660            } else {
661                insertCharToComposingText(SPACE);
662            }
663            return true;
664        } else if (key == KeyEvent.KEYCODE_SYM) {
665            /* display the symbol list */
666            commitText(1);
667            mComposingText.clear();
668            setSymbolMode(SymbolList.SYMBOL_ENGLISH);
669            updateComposingText(1);
670            mHardAlt = 0;
671            updateMetaKeyStateDisplay();
672        }
673
674
675        /* Functional key */
676        if (mComposingText.size(1) > 0) {
677            switch (key) {
678            case KeyEvent.KEYCODE_DEL:
679                mComposingText.delete(1, false);
680                updateComposingText(1);
681                return true;
682
683            case KeyEvent.KEYCODE_BACK:
684                if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
685                    mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
686                } else {
687                    mComposingText.clear();
688                    updateComposingText(1);
689                }
690                return true;
691
692            case KeyEvent.KEYCODE_DPAD_LEFT:
693                mComposingText.moveCursor(1, -1);
694                updateComposingText(1);
695                return true;
696
697            case KeyEvent.KEYCODE_DPAD_RIGHT:
698                mComposingText.moveCursor(1, 1);
699                updateComposingText(1);
700                return true;
701
702            case KeyEvent.KEYCODE_ENTER:
703            case KeyEvent.KEYCODE_DPAD_CENTER:
704                commitText(1);
705                mComposingText.clear();
706                if (mEnableAutoHideKeyboard) {
707                    mInputViewManager.closing();
708                    requestHideSelf(0);
709                }
710                return true;
711
712            default:
713                break;
714            }
715        } else {
716            /* if there is no composing string. */
717            if (mCandidatesViewManager.getCurrentView().isShown()) {
718            	if (key == KeyEvent.KEYCODE_BACK) {
719            		if (mCandidatesViewManager.getViewType() == CandidatesViewManager.VIEW_TYPE_FULL) {
720            			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_NORMAL);
721            		} else {
722            			mCandidatesViewManager.setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);
723            		}
724            		return true;
725            	}
726            } else {
727                switch (key) {
728                case KeyEvent.KEYCODE_DPAD_CENTER:
729                case KeyEvent.KEYCODE_ENTER:
730                    if (mEnableAutoHideKeyboard) {
731                        mInputViewManager.closing();
732                        requestHideSelf(0);
733                        return true;
734                    }
735                    break;
736                case KeyEvent.KEYCODE_BACK:
737                    /*
738                     * If 'BACK' key is pressed when the SW-keyboard is shown
739                     * and the candidates view is not shown, dismiss the SW-keyboard.
740                     */
741                    if (isInputViewShown()) {
742                        mInputViewManager.closing();
743                        requestHideSelf(0);
744                        return true;
745                    }
746                    break;
747                default:
748                    break;
749                }
750            }
751        }
752
753        return false;
754    }
755
756    /**
757     * Thread for updating the candidates view
758     */
759    private void updatePrediction() {
760        int candidates = 0;
761        if (mConverter != null) {
762            /* normal prediction */
763            candidates = mConverter.predict(mComposingText, 0, -1);
764        }
765        /* update the candidates view */
766        if (candidates > 0) {
767            mCandidatesViewManager.displayCandidates(mConverter);
768        } else {
769            mCandidatesViewManager.clearCandidates();
770        }
771    }
772
773    /**
774     * Update the composing text.
775     *
776     * @param layer  {@link mComposingText}'s layer to display
777     */
778    private void updateComposingText(int layer) {
779        /* update the candidates view */
780        if (!mOptPrediction) {
781            commitText(1);
782            mComposingText.clear();
783            if (mSymbolMode) {
784                mHandler.removeMessages(MSG_PREDICTION);
785                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
786            }
787        } else {
788            if (mComposingText.size(1) != 0) {
789                mHandler.removeMessages(MSG_PREDICTION);
790                if (mCandidatesViewManager.getCurrentView().isShown()) {
791                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
792                                                PREDICTION_DELAY_MS_SHOWING_CANDIDATE);
793                } else {
794                    mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION),
795                                                PREDICTION_DELAY_MS_1ST);
796                }
797            } else {
798                mHandler.removeMessages(MSG_PREDICTION);
799                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_PREDICTION), 0);
800            }
801
802            /* notice to the input view */
803            this.mInputViewManager.onUpdateState(this);
804
805            /* set the text for displaying as the composing text */
806            SpannableStringBuilder disp = mDisplayText;
807            disp.clear();
808            disp.insert(0, mComposingText.toString(layer));
809
810            /* add decoration to the text */
811            int cursor = mComposingText.getCursor(layer);
812            if (disp.length() != 0) {
813                if (cursor > 0 && cursor < disp.length()) {
814                    disp.setSpan(SPAN_EXACT_BGCOLOR_HL, 0, cursor,
815                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
816                }
817                if (cursor < disp.length()) {
818                    mDisplayText.setSpan(SPAN_REMAIN_BGCOLOR_HL, cursor, disp.length(),
819                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
820                    mDisplayText.setSpan(SPAN_TEXTCOLOR, 0, disp.length(),
821                            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
822                }
823
824                disp.setSpan(SPAN_UNDERLINE, 0, disp.length(),
825                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
826            }
827
828            int displayCursor = cursor;
829            if (FIX_CURSOR_TEXT_END) {
830                displayCursor = (cursor == 0) ?  0 : 1;
831            }
832            /* update the composing text on the EditView */
833            mInputConnection.setComposingText(disp, displayCursor);
834        }
835    }
836
837    /**
838     * Commit the composing text.
839     *
840     * @param layer  {@link mComposingText}'s layer to commit.
841     */
842    private void commitText(int layer) {
843        String tmp = mComposingText.toString(layer);
844
845        if (mOptLearning && mConverter != null && tmp.length() > 0) {
846            WnnWord word = new WnnWord(tmp, tmp);
847            mConverter.learn(word);
848        }
849
850        mInputConnection.commitText(tmp, (FIX_CURSOR_TEXT_END ? 1 : tmp.length()));
851        mCandidatesViewManager.clearCandidates();
852    }
853
854    /**
855     * Commit a word
856     *
857     * @param word          A word to commit
858     * @param withSpace     Append a space after the word if {@code true}.
859     */
860    private void commitText(WnnWord word, boolean withSpace) {
861
862        if (mOptLearning && mConverter != null) {
863            mConverter.learn(word);
864        }
865
866        mInputConnection.commitText(word.candidate, (FIX_CURSOR_TEXT_END ? 1 : word.candidate.length()));
867
868        if (withSpace) {
869            commitText(" ");
870        }
871    }
872
873    /**
874     * Commit a string
875     * <br>
876     * The string is not registered into the learning dictionary.
877     *
878     * @param str  A string to commit
879     */
880    private void commitText(String str) {
881        mInputConnection.commitText(str, (FIX_CURSOR_TEXT_END ? 1 : str.length()));
882        mCandidatesViewManager.clearCandidates();
883    }
884
885    /**
886     * Dismiss the pop-up keyboard
887     */
888    protected void dismissPopupKeyboard() {
889        DefaultSoftKeyboardEN kbd = (DefaultSoftKeyboardEN)mInputViewManager;
890        if (kbd != null) {
891            kbd.dismissPopupKeyboard();
892        }
893    }
894
895    /**
896     * Display current meta-key state.
897     */
898    private void updateMetaKeyStateDisplay() {
899        int mode = 0;
900        if(mHardShift == 0 && mHardAlt == 0){
901            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
902        }else if(mHardShift == 1 && mHardAlt == 0){
903            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_OFF;
904        }else if(mHardShift == 2  && mHardAlt == 0){
905            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
906        }else if(mHardShift == 0 && mHardAlt == 1){
907            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_ON;
908        }else if(mHardShift == 0 && mHardAlt == 2){
909            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_LOCK;
910        }else if(mHardShift == 1 && mHardAlt == 1){
911            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_ON;
912        }else if(mHardShift == 1 && mHardAlt == 2){
913            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_ON_ALT_LOCK;
914        }else if(mHardShift == 2 && mHardAlt == 1){
915            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_ON;
916        }else if(mHardShift == 2 && mHardAlt == 2){
917            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK;
918        }else{
919            mode = DefaultSoftKeyboard.HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
920        }
921
922        ((DefaultSoftKeyboard) mInputViewManager).updateIndicator(mode);
923    }
924
925    /**
926     * Handling KeyEvent(KEYUP)
927     * <br>
928     * This method is called from {@link #onEvent()}.
929     *
930     * @param ev   An up key event
931     */
932    private void onKeyUpEvent(KeyEvent ev) {
933        int key = ev.getKeyCode();
934        if(!mShiftPressing){
935            if(key == KeyEvent.KEYCODE_SHIFT_LEFT || key == KeyEvent.KEYCODE_SHIFT_RIGHT){
936                mHardShift = 0;
937                mShiftPressing = true;
938                updateMetaKeyStateDisplay();
939            }
940        }
941        if(!mAltPressing ){
942            if(key == KeyEvent.KEYCODE_ALT_LEFT || key == KeyEvent.KEYCODE_ALT_RIGHT){
943                mHardAlt = 0;
944                mAltPressing   = true;
945                updateMetaKeyStateDisplay();
946            }
947        }
948    }
949    /**
950     * Fits an editor info.
951     *
952     * @param preferences  The preference data.
953     * @param info          The editor info.
954     */
955    private void fitInputType(SharedPreferences preference, EditorInfo info) {
956        if (info.inputType == EditorInfo.TYPE_NULL) {
957            mDirectInputMode = true;
958            return;
959        }
960
961        mEnableAutoHideKeyboard = false;
962
963        /* set prediction & spell correction mode */
964        mOptPrediction      = preference.getBoolean("opt_en_prediction", true);
965        mOptSpellCorrection = preference.getBoolean("opt_en_spell_correction", true);
966        mOptLearning        = preference.getBoolean("opt_en_enable_learning", true);
967
968        /* prediction on/off */
969        switch (info.inputType & EditorInfo.TYPE_MASK_CLASS) {
970        case EditorInfo.TYPE_CLASS_NUMBER:
971        case EditorInfo.TYPE_CLASS_DATETIME:
972        case EditorInfo.TYPE_CLASS_PHONE:
973            mOptPrediction = false;
974            mOptLearning = false;
975            break;
976
977        case EditorInfo.TYPE_CLASS_TEXT:
978            switch (info.inputType & EditorInfo.TYPE_MASK_VARIATION) {
979            case EditorInfo.TYPE_TEXT_VARIATION_PASSWORD:
980                mEnableAutoHideKeyboard = true;
981                mOptLearning = false;
982                mOptPrediction = false;
983                break;
984
985            case EditorInfo.TYPE_TEXT_VARIATION_PHONETIC:
986                mOptLearning = false;
987                mOptPrediction = false;
988                break;
989            default:
990                break;
991            }
992        }
993
994        /* doesn't learn any word if it is not prediction mode */
995        if (!mOptPrediction) {
996            mOptLearning = false;
997        }
998
999        /* set engine's mode */
1000        if (mOptSpellCorrection) {
1001            mConverterEN.setDictionary(OpenWnnEngineEN.DICT_FOR_CORRECT_MISTYPE);
1002        } else {
1003            mConverterEN.setDictionary(OpenWnnEngineEN.DICT_DEFAULT);
1004        }
1005        checkTutorial(info.privateImeOptions);
1006    }
1007
1008    /**
1009     * Check and start the tutorial if it is the tutorial mode.
1010     *
1011     * @param privateImeOptions IME's options
1012     */
1013    private void checkTutorial(String privateImeOptions) {
1014        if (privateImeOptions == null) return;
1015        if (privateImeOptions.equals("com.android.setupwizard:ShowTutorial")) {
1016            if ((mTutorial == null) && mEnableTutorial) startTutorial();
1017        } else if (privateImeOptions.equals("com.android.setupwizard:HideTutorial")) {
1018            if (mTutorial != null) {
1019                if (mTutorial.close()) {
1020                    mTutorial = null;
1021                }
1022            }
1023        }
1024    }
1025
1026    /**
1027     * Start the tutorial
1028     */
1029    private void startTutorial() {
1030        DefaultSoftKeyboardEN inputManager = ((DefaultSoftKeyboardEN) mInputViewManager);
1031        View v = inputManager.getKeyboardView();
1032        v.setOnTouchListener(new View.OnTouchListener() {
1033				public boolean onTouch(View v, MotionEvent event) {
1034					return true;
1035				}});
1036        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
1037    }
1038
1039    /**
1040     * Close the tutorial
1041     */
1042    public void tutorialDone() {
1043        mTutorial = null;
1044    }
1045
1046    /** @see OpenWnn#close */
1047    @Override protected void close() {
1048        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLOSE), 0);
1049    }
1050}
1051